Merge pull request #500 from react-bootstrap-table/develop

20180821 release
This commit is contained in:
Allen 2018-08-21 17:03:00 +08:00 committed by GitHub
commit 6e526f455b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 144 additions and 177 deletions

View File

@ -52,6 +52,7 @@
"css-loader": "0.28.1",
"enzyme": "3.3.0",
"enzyme-adapter-react-16": "1.1.1",
"enzyme-to-json": "3.3.4",
"eslint": "4.5.0",
"eslint-config-airbnb": "15.1.0",
"eslint-loader": "1.9.0",

View File

@ -22,8 +22,19 @@ const columns = [{
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// ...
const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
const NoDataIndication = () => (
<div className="spinner">
<div className="rect1" />
<div className="rect2" />
<div className="rect3" />
<div className="rect4" />
<div className="rect5" />
</div>
);
const Table = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div>
<BootstrapTable
remote
@ -32,12 +43,13 @@ const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize })
columns={ columns }
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
onTableChange={ onTableChange }
noDataIndication={ () => <NoDataIndication /> }
/>
<Code>{ sourceCode }</Code>
</div>
);
class Container extends React.Component {
class EmptyTableOverlay extends React.Component {
constructor(props) {
super(props);
this.state = {
@ -47,7 +59,7 @@ class Container extends React.Component {
};
}
handleTableChange = ({ page, sizePerPage }) => {
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({
@ -55,13 +67,14 @@ class Container extends React.Component {
data: products.slice(currentIndex, currentIndex + sizePerPage),
sizePerPage
}));
}, 2000);
}, 3000);
this.setState(() => ({ data: [] }));
}
render() {
const { data, sizePerPage, page } = this.state;
return (
<RemotePagination
<Table
data={ data }
page={ page }
sizePerPage={ sizePerPage }

View File

@ -54,31 +54,22 @@ export default (
componentWillReceiveProps(nextProps) {
let needNewState = false;
let { currPage, currSizePerPage } = this;
const { page, sizePerPage, onPageChange } = nextProps.pagination.options;
let { currPage } = this;
const { currSizePerPage } = this;
const { onPageChange } = nextProps.pagination.options;
const pageStartIndex = typeof nextProps.pagination.options.pageStartIndex !== 'undefined' ?
nextProps.pagination.options.pageStartIndex : Const.PAGE_START_INDEX;
if (typeof page !== 'undefined' && currPage !== page) { // user defined page
currPage = page;
needNewState = true;
} else {
// user should align the page when the page is not fit to the data size when remote enable
if (!isRemotePagination()) {
const newPage = alignPage(nextProps.data, currPage, currSizePerPage, pageStartIndex);
if (currPage !== newPage) {
currPage = newPage;
needNewState = true;
}
// user should align the page when the page is not fit to the data size when remote enable
if (!isRemotePagination()) {
const newPage = alignPage(nextProps.data, currPage, currSizePerPage, pageStartIndex);
if (currPage !== newPage) {
currPage = newPage;
needNewState = true;
}
}
if (typeof sizePerPage !== 'undefined' && currSizePerPage !== sizePerPage) {
currSizePerPage = sizePerPage;
needNewState = true;
}
if (needNewState) {
if (onPageChange) {
onPageChange(currPage, currSizePerPage);

View File

@ -23,10 +23,9 @@ export const alignPage = (
sizePerPage,
pageStartIndex
) => {
const end = endIndex(page, sizePerPage, pageStartIndex);
const dataSize = data.length;
if (end - 1 > dataSize) {
if (page < pageStartIndex || page > (Math.floor(dataSize / sizePerPage) + pageStartIndex)) {
return pageStartIndex;
}
return page;

View File

@ -126,69 +126,6 @@ describe('PaginationContext', () => {
let instance;
let nextProps;
describe('when nextProps.pagination.options.page is existing', () => {
const onPageChange = jest.fn();
afterEach(() => {
onPageChange.mockReset();
});
describe('and if it is different with currPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
page: 2,
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('should call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(1);
expect(onPageChange).toHaveBeenCalledWith(
instance.currPage,
instance.currSizePerPage
);
});
it('should set correct currPage', () => {
expect(instance.currPage).toEqual(nextProps.pagination.options.page);
});
});
describe('and if it is same as currPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
page: 1,
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('shouldn\'t call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(0);
});
it('should have correct currPage', () => {
expect(instance.currPage).toEqual(nextProps.pagination.options.page);
});
});
});
describe('when nextProps.pagination.options.page is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
@ -206,69 +143,6 @@ describe('PaginationContext', () => {
});
});
describe('when nextProps.pagination.options.sizePerPage is existing', () => {
const onPageChange = jest.fn();
afterEach(() => {
onPageChange.mockReset();
});
describe('and if it is different with currSizePerPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
sizePerPage: Const.SIZE_PER_PAGE_LIST[2],
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('should call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(1);
expect(onPageChange).toHaveBeenCalledWith(
instance.currPage,
instance.currSizePerPage
);
});
it('should set correct currSizePerPage', () => {
expect(instance.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
});
});
describe('and if it is same as currSizePerPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
sizePerPage: Const.SIZE_PER_PAGE_LIST[0],
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('shouldn\'t call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(0);
});
it('should have correct currSizePerPage', () => {
expect(instance.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
});
});
});
describe('when nextProps.pagination.options.sizePerPage is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
@ -281,10 +155,53 @@ describe('PaginationContext', () => {
instance.componentWillReceiveProps(nextProps);
});
it('should not set currPage', () => {
it('should not set currSizePerPage', () => {
expect(instance.currSizePerPage).toEqual(Const.SIZE_PER_PAGE_LIST[2]);
});
});
describe('when page is not align', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
page: 2
}));
instance = wrapper.instance();
wrapper.render();
nextProps = {
data: [],
pagination: { ...defaultPagination }
};
instance.componentWillReceiveProps(nextProps);
});
it('should reset currPage to first page', () => {
expect(instance.currPage).toEqual(1);
});
describe('if options.onPageChange is defined', () => {
const onPageChange = jest.fn();
beforeEach(() => {
onPageChange.mockClear();
wrapper = shallow(shallowContext({
...defaultPagination,
page: 2
}));
instance = wrapper.instance();
wrapper.render();
nextProps = {
data: [],
pagination: { ...defaultPagination, options: { onPageChange } }
};
instance.componentWillReceiveProps(nextProps);
});
it('should call options.onPageChange correctly', () => {
expect(onPageChange).toHaveBeenCalledTimes(1);
expect(onPageChange).toHaveBeenCalledWith(instance.currPage, instance.currSizePerPage);
});
});
});
});
describe('handleChangePage', () => {

View File

@ -45,8 +45,8 @@ describe('Page Functions', () => {
describe('alignPage', () => {
const pageStartIndex = 1;
const sizePerPage = 10;
const page = 2;
describe('if the length of store.data is less than the end page index', () => {
const page = 3;
describe('if the page does not fit the pages interval calculated from the length of store.data', () => {
beforeEach(() => {
data = [];
for (let i = 0; i < 15; i += 1) {

View File

@ -20,11 +20,26 @@ export default (options = {
searchText: PropTypes.string
}
constructor(props) {
super(props);
this.performRemoteSearch = props.searchText !== '';
}
componentWillReceiveProps(nextProps) {
if (isRemoteSearch()) {
if (nextProps.searchText !== this.props.searchText) {
this.performRemoteSearch = true;
} else {
this.performRemoteSearch = false;
}
}
}
search() {
const { data, columns } = this.props;
let { searchText } = this.props;
if (isRemoteSearch()) {
if (isRemoteSearch() && this.performRemoteSearch) {
handleRemoteSearchChange(searchText);
return data;
}

View File

@ -75,6 +75,7 @@ export default class SelectionCell extends Component {
checked={ selected }
disabled={ disabled }
className={ bootstrap4 ? 'selection-input-4' : '' }
onChange={ () => {} }
/>
)
}

View File

@ -12,6 +12,7 @@ export const CheckBox = ({ className, checked, indeterminate }) => (
ref={ (input) => {
if (input) input.indeterminate = indeterminate; // eslint-disable-line no-param-reassign
} }
onChange={ () => {} }
/>
);

View File

@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<SelectionCell /> render should render component correctly 1`] = `
<td
onClick={[Function]}
>
<input
checked={true}
className=""
onChange={[Function]}
type="checkbox"
/>
</td>
`;

View File

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<CheckBox /> render should render component correctly 1`] = `
<input
checked={true}
onChange={[Function]}
type="checkbox"
/>
`;

View File

@ -1,6 +1,7 @@
import 'jsdom-global/register';
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import sinon from 'sinon';
import { shallowWithContext } from '../test-helpers/new-context';
@ -61,7 +62,8 @@ describe('<SelectionCell />', () => {
mode={ mode }
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
wrapper.find('td').simulate('click');
});
@ -72,9 +74,7 @@ describe('<SelectionCell />', () => {
it('should calling onRowSelect callback correctly', () => {
expect(mockOnRowSelect.calledOnce).toBe(true);
expect(
mockOnRowSelect.calledWith(rowKey, !selected, rowIndex)
).toBe(true);
expect(mockOnRowSelect.calledWith(rowKey, !selected, rowIndex)).toBe(true);
});
});
@ -88,7 +88,8 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
disabled
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
wrapper.find('td').simulate('click');
});
@ -111,7 +112,8 @@ describe('<SelectionCell />', () => {
mode="radio"
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
@ -132,11 +134,12 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
selected
onRowSelect={ mockOnRowSelect }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
it('should be called with correct paramters', () => {
it('should be called with correct parameters', () => {
// first click
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(1);
@ -151,12 +154,8 @@ describe('<SelectionCell />', () => {
beforeEach(() => {
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
/>, { bootstrap4: false }
<SelectionCell rowKey={ 1 } mode={ mode } rowIndex={ rowIndex } selected={ selected } />,
{ bootstrap4: false }
);
});
@ -165,6 +164,7 @@ describe('<SelectionCell />', () => {
expect(wrapper.find('input')).toHaveLength(1);
expect(wrapper.find('input').get(0).props.type).toBe(mode);
expect(wrapper.find('input').get(0).props.checked).toBe(selected);
expect(toJson(wrapper)).toMatchSnapshot();
});
describe('when disabled prop give as true', () => {
@ -176,7 +176,8 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
selected={ selected }
disabled
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
@ -198,7 +199,8 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
selected={ selected }
selectionRenderer={ selectionRenderer }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
@ -219,12 +221,8 @@ describe('<SelectionCell />', () => {
describe('when bootstrap4 context is true', () => {
beforeEach(() => {
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
/>, { bootstrap4: true }
<SelectionCell rowKey={ 1 } mode={ mode } rowIndex={ rowIndex } selected={ selected } />,
{ bootstrap4: true }
);
});

View File

@ -1,5 +1,6 @@
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import sinon from 'sinon';
import { shallowWithContext } from '../test-helpers/new-context';
@ -201,6 +202,7 @@ describe('<CheckBox />', () => {
expect(wrapper.find('input').length).toBe(1);
expect(wrapper.find('input').prop('checked')).toBe(checked);
expect(wrapper.find('input').prop('type')).toBe('checkbox');
expect(toJson(wrapper)).toMatchSnapshot();
});
});
});

View File

@ -2711,6 +2711,12 @@ enzyme-adapter-utils@^1.3.0:
object.assign "^4.1.0"
prop-types "^15.6.0"
enzyme-to-json@3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.3.4.tgz#67c6040e931182f183418af2eb9f4323258aa77f"
dependencies:
lodash "^4.17.4"
enzyme@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.3.0.tgz#0971abd167f2d4bf3f5bd508229e1c4b6dc50479"