diff --git a/docs/README.md b/docs/README.md
index 0766df6..05da321 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -40,14 +40,21 @@ This is a chance that you can connect to your remote server or database to manip
For flexibility reason, you can control what functionality should be handled on remote via a object return:
```js
-remote={ { pagination: true } }
+remote={ { filter: true } }
```
-In above case, only pagination will be handled on remote.
+In above case, only column filter will be handled on remote.
> Note: when remote is enable, you are suppose to give [`onTableChange`](#onTableChange) prop on `BootstrapTable`
> It's the only way to communicate to your remote server and update table states.
+A special case for remote pagination:
+```js
+remote={ { pagination: true, filter: false, sort: false } }
+```
+
+In pagination case, even you only specified the paignation need to handle as remote, `react-bootstrap-table2` will handle all the table changes(`filter`, `sort` etc) as remote mode, because `react-bootstrap-table` only know the data of current page, but filtering, searching or sort need to work on overall datas.
+
### loading - [Bool]
Telling if table is loading or not, for example: waiting data loading, filtering etc. It's **only** valid when [`remote`](#remote) is enabled.
When `loading` is `true`, `react-bootstrap-table` will attend to render a overlay on table via [`overlay`](#overlay) prop, if [`overlay`](#overlay) prop is not given, `react-bootstrap-table` will ignore the overlay rendering.
@@ -230,17 +237,25 @@ const columns = [ {
This callback function will be called when [`remote`](#remote) enabled only.
```js
-const onTableChange = (newState) => {
+const onTableChange = (type, newState) => {
// handle any data change here
}
```
-There's only one argument will be passed to `onTableChange`, `newState`:
+There's only two arguments will be passed to `onTableChange`: `type` and `newState`:
+
+`type` is tell you what kind of functionality to trigger this table's change: available values at the below:
+
+* `filter`
+* `pagination`
+
+Following is a shape of `newState`
```js
{
page, // newest page
- sizePerPage //newest sizePerPage
+ sizePerPage, //newest sizePerPage
+ filters // an object which have current filter status per column
}
```
\ No newline at end of file
diff --git a/packages/react-bootstrap-table2-example/examples/remote/remote-all.js b/packages/react-bootstrap-table2-example/examples/remote/remote-all.js
new file mode 100644
index 0000000..c23d690
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/remote/remote-all.js
@@ -0,0 +1,202 @@
+/* eslint guard-for-in: 0 */
+/* eslint no-restricted-syntax: 0 */
+import React from 'react';
+import PropTypes from 'prop-types';
+import BootstrapTable from 'react-bootstrap-table2';
+import paginator from 'react-bootstrap-table2-paginator';
+import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
+import Code from 'components/common/code-block';
+import { productsGenerator } from 'utils/common';
+
+const products = productsGenerator(87);
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name',
+ filter: textFilter()
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ filter: textFilter()
+}];
+
+const sourceCode = `\
+import BootstrapTable from 'react-bootstrap-table2';
+import paginator from 'react-bootstrap-table2-paginator';
+import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
+// ...
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name',
+ filter: textFilter()
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ filter: textFilter()
+}];
+
+const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
+
+
+ { sourceCode }
+
+);
+
+RemoteAll.propTypes = {
+ data: PropTypes.array.isRequired,
+ page: PropTypes.number.isRequired,
+ totalSize: PropTypes.number.isRequired,
+ sizePerPage: PropTypes.number.isRequired,
+ onTableChange: PropTypes.func.isRequired
+};
+
+class Container extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ page: 1,
+ data: products.slice(0, 10),
+ totalSize: products.length,
+ sizePerPage: 10
+ };
+ this.handleTableChange = this.handleTableChange.bind(this);
+ }
+
+ handleTableChange = (type, { page, sizePerPage, filters }) => {
+ const currentIndex = (page - 1) * sizePerPage;
+ setTimeout(() => {
+ const result = products.filter((row) => {
+ let valid = true;
+ for (const dataField in filters) {
+ const { filterVal, filterType, comparator } = filters[dataField];
+
+ if (filterType === 'TEXT') {
+ if (comparator === Comparator.LIKE) {
+ valid = row[dataField].toString().indexOf(filterVal) > -1;
+ } else {
+ valid = row[dataField] === filterVal;
+ }
+ }
+ if (!valid) break;
+ }
+ return valid;
+ });
+ this.setState(() => ({
+ page,
+ data: result.slice(currentIndex, currentIndex + sizePerPage),
+ totalSize: result.length,
+ sizePerPage
+ }));
+ }, 2000);
+ }
+
+ render() {
+ const { data, sizePerPage, page } = this.state;
+ return (
+
+ );
+ }
+}
+`;
+
+const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
+
+
When remote.pagination is enabled, the filtering,
+ sorting and searching will also change to remote mode automatically
+
+ { sourceCode }
+
+);
+
+RemoteAll.propTypes = {
+ data: PropTypes.array.isRequired,
+ page: PropTypes.number.isRequired,
+ totalSize: PropTypes.number.isRequired,
+ sizePerPage: PropTypes.number.isRequired,
+ onTableChange: PropTypes.func.isRequired
+};
+
+class Container extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ page: 1,
+ data: products.slice(0, 10),
+ totalSize: products.length,
+ sizePerPage: 10
+ };
+ this.handleTableChange = this.handleTableChange.bind(this);
+ }
+
+ handleTableChange = (type, { page, sizePerPage, filters }) => {
+ const currentIndex = (page - 1) * sizePerPage;
+ setTimeout(() => {
+ const result = products.filter((row) => {
+ let valid = true;
+ for (const dataField in filters) {
+ const { filterVal, filterType, comparator } = filters[dataField];
+
+ if (filterType === 'TEXT') {
+ if (comparator === Comparator.LIKE) {
+ valid = row[dataField].toString().indexOf(filterVal) > -1;
+ } else {
+ valid = row[dataField] === filterVal;
+ }
+ }
+ if (!valid) break;
+ }
+ return valid;
+ });
+ this.setState(() => ({
+ page,
+ data: result.slice(currentIndex, currentIndex + sizePerPage),
+ totalSize: result.length,
+ sizePerPage
+ }));
+ }, 2000);
+ }
+
+ render() {
+ const { data, sizePerPage, page } = this.state;
+ return (
+
+ );
+ }
+}
+
+export default Container;
diff --git a/packages/react-bootstrap-table2-example/examples/remote/remote-filter.js b/packages/react-bootstrap-table2-example/examples/remote/remote-filter.js
new file mode 100644
index 0000000..d2b37b6
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/remote/remote-filter.js
@@ -0,0 +1,105 @@
+/* eslint guard-for-in: 0 */
+/* eslint no-restricted-syntax: 0 */
+import React from 'react';
+import PropTypes from 'prop-types';
+import BootstrapTable from 'react-bootstrap-table2';
+import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
+import Code from 'components/common/code-block';
+import { productsGenerator } from 'utils/common';
+
+const products = productsGenerator(17);
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name',
+ filter: textFilter()
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ filter: textFilter()
+}];
+
+const sourceCode = `\
+import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID',
+}, {
+ dataField: 'name',
+ text: 'Product Name',
+ filter: textFilter()
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ filter: textFilter()
+}];
+
+
+`;
+
+const RemoteFilter = props => (
+
+
+ { sourceCode }
+
+);
+
+RemoteFilter.propTypes = {
+ data: PropTypes.array.isRequired,
+ onTableChange: PropTypes.func.isRequired
+};
+
+class Container extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ data: products
+ };
+ }
+
+ handleTableChange = (type, { filters }) => {
+ setTimeout(() => {
+ const result = products.filter((row) => {
+ let valid = true;
+ for (const dataField in filters) {
+ const { filterVal, filterType, comparator } = filters[dataField];
+
+ if (filterType === 'TEXT') {
+ if (comparator === Comparator.LIKE) {
+ valid = row[dataField].toString().indexOf(filterVal) > -1;
+ } else {
+ valid = row[dataField] === filterVal;
+ }
+ }
+ if (!valid) break;
+ }
+ return valid;
+ });
+ this.setState(() => ({
+ data: result
+ }));
+ }, 2000);
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default Container;
diff --git a/packages/react-bootstrap-table2-example/examples/pagination/remote-pagination.js b/packages/react-bootstrap-table2-example/examples/remote/remote-pagination.js
similarity index 98%
rename from packages/react-bootstrap-table2-example/examples/pagination/remote-pagination.js
rename to packages/react-bootstrap-table2-example/examples/remote/remote-pagination.js
index ccae5c7..050dc4d 100644
--- a/packages/react-bootstrap-table2-example/examples/pagination/remote-pagination.js
+++ b/packages/react-bootstrap-table2-example/examples/remote/remote-pagination.js
@@ -105,7 +105,7 @@ class Container extends React.Component {
};
}
- handleTableChange = ({ page, sizePerPage }) => {
+ handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index 012cfc8..36abbb0 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -82,12 +82,16 @@ import HideSelectionColumnTable from 'examples/row-selection/hide-selection-colu
import PaginationTable from 'examples/pagination';
import PaginationHooksTable from 'examples/pagination/pagination-hooks';
import CustomPaginationTable from 'examples/pagination/custom-pagination';
-import RemotePaginationTable from 'examples/pagination/remote-pagination';
// loading overlay
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
import TableOverlay from 'examples/loading-overlay/table-overlay';
+// remote
+import RemoteFilter from 'examples/remote/remote-filter';
+import RemotePaginationTable from 'examples/remote/remote-pagination';
+import RemoteAll from 'examples/remote/remote-all';
+
// css style
import 'bootstrap/dist/css/bootstrap.min.css';
import 'stories/stylesheet/tomorrow.min.css';
@@ -179,9 +183,13 @@ storiesOf('Row Selection', module)
storiesOf('Pagination', module)
.add('Basic Pagination Table', () => )
.add('Pagination Hooks', () => )
- .add('Custom Pagination', () => )
- .add('Remote Pagination', () => );
+ .add('Custom Pagination', () => );
storiesOf('EmptyTableOverlay', module)
.add('Empty Table Overlay', () => )
.add('Table Overlay', () => );
+
+storiesOf('Remote', module)
+ .add('Remote Filter', () => )
+ .add('Remote Pagination', () => )
+ .add('Remote All', () => );
diff --git a/packages/react-bootstrap-table2-filter/src/wrapper.js b/packages/react-bootstrap-table2-filter/src/wrapper.js
index 0cd8f44..48e4d73 100644
--- a/packages/react-bootstrap-table2-filter/src/wrapper.js
+++ b/packages/react-bootstrap-table2-filter/src/wrapper.js
@@ -1,12 +1,16 @@
+/* eslint react/prop-types: 0 */
import { Component } from 'react';
import PropTypes from 'prop-types';
import { filters } from './filter';
+import { LIKE } from './comparison';
export default class FilterWrapper extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
columns: PropTypes.array.isRequired,
baseElement: PropTypes.func.isRequired,
+ onRemoteFilterChange: PropTypes.func.isRequired,
+ // refactoring later
_: PropTypes.object.isRequired
}
@@ -16,28 +20,51 @@ export default class FilterWrapper extends Component {
this.onFilter = this.onFilter.bind(this);
}
- componentWillReceiveProps() {
- this.setState(() => ({ isDataChanged: false }));
+ componentWillReceiveProps(nextProps) {
+ // consider to use lodash.isEqual
+ if (JSON.stringify(this.state.currFilters) !== JSON.stringify(nextProps.store.filters)) {
+ this.setState(() => ({ isDataChanged: true, currFilters: nextProps.store.filters }));
+ } else {
+ this.setState(() => ({ isDataChanged: false }));
+ }
}
onFilter(column, filterVal, filterType) {
- const { store, columns, _ } = this.props;
- const { currFilters } = this.state;
+ const { store, columns, _, onRemoteFilterChange } = this.props;
+ const currFilters = Object.assign({}, this.state.currFilters);
const { dataField, filter } = column;
if (!_.isDefined(filterVal) || filterVal === '') {
delete currFilters[dataField];
} else {
- const { comparator } = filter.props;
+ const { comparator = LIKE } = filter.props;
currFilters[dataField] = { filterVal, filterType, comparator };
}
+ store.filters = currFilters;
+
+ if (this.isRemote() || this.isPaginationRemote()) {
+ onRemoteFilterChange(this.isPaginationRemote());
+ // when remote filtering is enable, dont set currFilters state
+ // in the componentWillReceiveProps, it's the key point that we can know the filter is changed
+ return;
+ }
store.filteredData = filters(store, columns, _)(currFilters);
- store.filtering = Object.keys(currFilters).length > 0;
-
this.setState(() => ({ currFilters, isDataChanged: true }));
}
+ // refactoring later
+ isRemote() {
+ const { remote } = this.props;
+ return remote === true || (typeof remote === 'object' && remote.filter);
+ }
+
+ // refactoring later
+ isPaginationRemote() {
+ const { remote } = this.props;
+ return remote === true || (typeof remote === 'object' && remote.pagination);
+ }
+
render() {
return this.props.baseElement({
...this.props,
diff --git a/packages/react-bootstrap-table2-filter/test/wrapper.test.js b/packages/react-bootstrap-table2-filter/test/wrapper.test.js
index c877c62..9541c8d 100644
--- a/packages/react-bootstrap-table2-filter/test/wrapper.test.js
+++ b/packages/react-bootstrap-table2-filter/test/wrapper.test.js
@@ -1,4 +1,5 @@
import React from 'react';
+import sinon from 'sinon';
import { shallow } from 'enzyme';
import _ from 'react-bootstrap-table2/src/utils';
@@ -20,7 +21,11 @@ for (let i = 0; i < 20; i += 1) {
describe('Wrapper', () => {
let wrapper;
let instance;
+ const onRemoteFilterChangeCB = sinon.stub();
+ afterEach(() => {
+ onRemoteFilterChangeCB.reset();
+ });
const createTableProps = () => {
const tableProps = {
@@ -40,7 +45,8 @@ describe('Wrapper', () => {
data,
filter: filter(),
_,
- store: new Store('id')
+ store: new Store('id'),
+ onRemoteFilterChange: onRemoteFilterChangeCB
};
tableProps.store.data = data;
return tableProps;
@@ -84,13 +90,28 @@ describe('Wrapper', () => {
describe('componentWillReceiveProps', () => {
let nextProps;
- beforeEach(() => {
- nextProps = createTableProps();
- instance.componentWillReceiveProps(nextProps);
+ describe('when props.store.filters is same as current state.currFilters', () => {
+ beforeEach(() => {
+ nextProps = createTableProps();
+ instance.componentWillReceiveProps(nextProps);
+ });
+
+ it('should setting isDataChanged as false (Temporary solution)', () => {
+ expect(instance.state.isDataChanged).toBeFalsy();
+ });
});
- it('should setting isDataChanged as false always(Temporary solution)', () => {
- expect(instance.state.isDataChanged).toBeFalsy();
+ describe('when props.store.filters is different from current state.currFilters', () => {
+ beforeEach(() => {
+ nextProps = createTableProps();
+ nextProps.store.filters = { price: { filterVal: 20, filterType: FILTER_TYPE.TEXT } };
+ instance.componentWillReceiveProps(nextProps);
+ });
+
+ it('should setting states correctly', () => {
+ expect(instance.state.isDataChanged).toBeTruthy();
+ expect(instance.state.currFilters).toBe(nextProps.store.filters);
+ });
});
});
@@ -126,7 +147,7 @@ describe('Wrapper', () => {
it('should setting store object correctly', () => {
instance.onFilter(props.columns[1], filterVal, FILTER_TYPE.TEXT);
- expect(props.store.filtering).toBeTruthy();
+ expect(props.store.filters).toEqual(instance.state.currFilters);
});
it('should setting state correctly', () => {
@@ -136,30 +157,54 @@ describe('Wrapper', () => {
});
});
+ describe('when remote filter is enabled', () => {
+ const filterVal = '3';
+
+ beforeEach(() => {
+ props = createTableProps();
+ props.remote = { filter: true };
+ createFilterWrapper(props);
+ instance.onFilter(props.columns[1], filterVal, FILTER_TYPE.TEXT);
+ });
+
+ it('should not setting store object correctly', () => {
+ expect(props.store.filters).not.toEqual(instance.state.currFilters);
+ });
+
+ it('should not setting state', () => {
+ expect(instance.state.isDataChanged).toBeFalsy();
+ expect(Object.keys(instance.state.currFilters)).toHaveLength(0);
+ });
+
+ it('should calling props.onRemoteFilterChange correctly', () => {
+ expect(onRemoteFilterChangeCB.calledOnce).toBeTruthy();
+ });
+ });
+
describe('combination', () => {
it('should setting store object correctly', () => {
instance.onFilter(props.columns[1], '3', FILTER_TYPE.TEXT);
- expect(props.store.filtering).toBeTruthy();
+ expect(props.store.filters).toEqual(instance.state.currFilters);
expect(instance.state.isDataChanged).toBeTruthy();
expect(Object.keys(instance.state.currFilters)).toHaveLength(1);
instance.onFilter(props.columns[1], '2', FILTER_TYPE.TEXT);
- expect(props.store.filtering).toBeTruthy();
+ expect(props.store.filters).toEqual(instance.state.currFilters);
expect(instance.state.isDataChanged).toBeTruthy();
expect(Object.keys(instance.state.currFilters)).toHaveLength(1);
instance.onFilter(props.columns[2], '2', FILTER_TYPE.TEXT);
- expect(props.store.filtering).toBeTruthy();
+ expect(props.store.filters).toEqual(instance.state.currFilters);
expect(instance.state.isDataChanged).toBeTruthy();
expect(Object.keys(instance.state.currFilters)).toHaveLength(2);
instance.onFilter(props.columns[2], '', FILTER_TYPE.TEXT);
- expect(props.store.filtering).toBeTruthy();
+ expect(props.store.filters).toEqual(instance.state.currFilters);
expect(instance.state.isDataChanged).toBeTruthy();
expect(Object.keys(instance.state.currFilters)).toHaveLength(1);
instance.onFilter(props.columns[1], '', FILTER_TYPE.TEXT);
- expect(props.store.filtering).toBeFalsy();
+ expect(props.store.filters).toEqual(instance.state.currFilters);
expect(instance.state.isDataChanged).toBeTruthy();
expect(Object.keys(instance.state.currFilters)).toHaveLength(0);
});
diff --git a/packages/react-bootstrap-table2-paginator/src/wrapper.js b/packages/react-bootstrap-table2-paginator/src/wrapper.js
index f54a8d9..e01cd51 100644
--- a/packages/react-bootstrap-table2-paginator/src/wrapper.js
+++ b/packages/react-bootstrap-table2-paginator/src/wrapper.js
@@ -11,7 +11,8 @@ import { getByCurrPage } from './page';
class PaginationWrapper extends Component {
static propTypes = {
store: PropTypes.object.isRequired,
- baseElement: PropTypes.func.isRequired
+ baseElement: PropTypes.func.isRequired,
+ onRemotePageChange: PropTypes.func.isRequired
}
constructor(props) {
@@ -43,14 +44,15 @@ class PaginationWrapper extends Component {
}
this.state = { currPage, currSizePerPage };
+ this.saveToStore(currPage, currSizePerPage);
}
componentWillReceiveProps(nextProps) {
let needNewState = false;
let { currPage, currSizePerPage } = this.state;
- const { page, sizePerPage, pageStartIndex } = nextProps.pagination.options;
+ const { page, sizePerPage, pageStartIndex, onPageChange } = nextProps.pagination.options;
- if (typeof page !== 'undefined') { // user defined page
+ if (typeof page !== 'undefined' && currPage !== page) { // user defined page
currPage = page;
needNewState = true;
} else if (nextProps.isDataChanged) { // user didn't defined page but data change
@@ -63,7 +65,19 @@ class PaginationWrapper extends Component {
needNewState = true;
}
- if (needNewState) this.setState(() => ({ currPage, currSizePerPage }));
+ this.saveToStore(currPage, currSizePerPage);
+
+ if (needNewState) {
+ if (onPageChange) {
+ onPageChange(currPage, currSizePerPage);
+ }
+ this.setState(() => ({ currPage, currSizePerPage }));
+ }
+ }
+
+ saveToStore(page, sizePerPage) {
+ this.props.store.page = page;
+ this.props.store.sizePerPage = sizePerPage;
}
isRemote() {
@@ -74,11 +88,13 @@ class PaginationWrapper extends Component {
handleChangePage(currPage) {
const { currSizePerPage } = this.state;
const { pagination: { options }, onRemotePageChange } = this.props;
+ this.saveToStore(currPage, currSizePerPage);
+
if (options.onPageChange) {
options.onPageChange(currPage, currSizePerPage);
}
if (this.isRemote()) {
- onRemotePageChange(currPage, currSizePerPage);
+ onRemotePageChange();
return;
}
this.setState(() => {
@@ -90,11 +106,13 @@ class PaginationWrapper extends Component {
handleChangeSizePerPage(currSizePerPage, currPage) {
const { pagination: { options }, onRemotePageChange } = this.props;
+ this.saveToStore(currPage, currSizePerPage);
+
if (options.onSizePerPageChange) {
options.onSizePerPageChange(currSizePerPage, currPage);
}
if (this.isRemote()) {
- onRemotePageChange(currPage, currSizePerPage);
+ onRemotePageChange();
return;
}
this.setState(() => {
diff --git a/packages/react-bootstrap-table2-paginator/test/wrapper.test.js b/packages/react-bootstrap-table2-paginator/test/wrapper.test.js
index 7254e2a..947971d 100644
--- a/packages/react-bootstrap-table2-paginator/test/wrapper.test.js
+++ b/packages/react-bootstrap-table2-paginator/test/wrapper.test.js
@@ -21,6 +21,11 @@ for (let i = 0; i < 100; i += 1) {
describe('Wrapper', () => {
let wrapper;
let instance;
+ const onRemotePageChangeCB = sinon.stub();
+
+ afterEach(() => {
+ onRemotePageChangeCB.reset();
+ });
const createTableProps = (props = {}) => {
const tableProps = {
@@ -34,7 +39,8 @@ describe('Wrapper', () => {
}],
data,
pagination: paginator(props.options),
- store: new Store('id')
+ store: new Store('id'),
+ onRemotePageChange: onRemotePageChangeCB
};
tableProps.store.data = data;
return tableProps;
@@ -69,6 +75,11 @@ describe('Wrapper', () => {
expect(instance.state.currSizePerPage).toEqual(Const.SIZE_PER_PAGE_LIST[0]);
});
+ it('should saving page and sizePerPage to store correctly', () => {
+ expect(props.store.page).toBe(instance.state.currPage);
+ expect(props.store.sizePerPage).toBe(instance.state.currSizePerPage);
+ });
+
it('should rendering BootstraTable correctly', () => {
const table = wrapper.find(BootstrapTable);
expect(table.length).toBe(1);
@@ -105,10 +116,19 @@ describe('Wrapper', () => {
nextProps = createTableProps();
});
- it('should setting currPage state correctly by options.page', () => {
- nextProps.pagination.options.page = 2;
- instance.componentWillReceiveProps(nextProps);
- expect(instance.state.currPage).toEqual(nextProps.pagination.options.page);
+ describe('when options.page is existing', () => {
+ beforeEach(() => {
+ nextProps.pagination.options.page = 2;
+ instance.componentWillReceiveProps(nextProps);
+ });
+
+ it('should setting currPage state correctly', () => {
+ expect(instance.state.currPage).toEqual(nextProps.pagination.options.page);
+ });
+
+ it('should saving store.page correctly', () => {
+ expect(props.store.page).toEqual(instance.state.currPage);
+ });
});
it('should not setting currPage state if options.page not existing', () => {
@@ -117,10 +137,19 @@ describe('Wrapper', () => {
expect(instance.state.currPage).toBe(currPage);
});
- it('should setting currSizePerPage state correctly by options.sizePerPage', () => {
- nextProps.pagination.options.sizePerPage = 20;
- instance.componentWillReceiveProps(nextProps);
- expect(instance.state.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
+ describe('when options.sizePerPage is existing', () => {
+ beforeEach(() => {
+ nextProps.pagination.options.sizePerPage = 20;
+ instance.componentWillReceiveProps(nextProps);
+ });
+
+ it('should setting currSizePerPage state correctly', () => {
+ expect(instance.state.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
+ });
+
+ it('should saving store.sizePerPage correctly', () => {
+ expect(props.store.sizePerPage).toEqual(instance.state.currSizePerPage);
+ });
});
it('should not setting currSizePerPage state if options.sizePerPage not existing', () => {
@@ -129,17 +158,35 @@ describe('Wrapper', () => {
expect(instance.state.currSizePerPage).toBe(currSizePerPage);
});
- it('should setting currPage state when nextProps.isDataChanged is true', () => {
- nextProps.isDataChanged = true;
- instance.componentWillReceiveProps(nextProps);
- expect(instance.state.currPage).toBe(Const.PAGE_START_INDEX);
+ describe('when nextProps.isDataChanged is true', () => {
+ beforeEach(() => {
+ nextProps.isDataChanged = true;
+ instance.componentWillReceiveProps(nextProps);
+ });
+
+ it('should setting currPage state correctly', () => {
+ expect(instance.state.currPage).toBe(Const.PAGE_START_INDEX);
+ });
+
+ it('should saving store.page correctly', () => {
+ expect(props.store.page).toEqual(instance.state.currPage);
+ });
});
- it('should setting currPage state when nextProps.isDataChanged is true and options.pageStartIndex is existing', () => {
- nextProps.isDataChanged = true;
- nextProps.pagination.options.pageStartIndex = 0;
- instance.componentWillReceiveProps(nextProps);
- expect(instance.state.currPage).toBe(nextProps.pagination.options.pageStartIndex);
+ describe('when nextProps.isDataChanged is true and options.pageStartIndex is existing', () => {
+ beforeEach(() => {
+ nextProps.isDataChanged = true;
+ nextProps.pagination.options.pageStartIndex = 0;
+ instance.componentWillReceiveProps(nextProps);
+ });
+
+ it('should setting currPage state correctly', () => {
+ expect(instance.state.currPage).toBe(nextProps.pagination.options.pageStartIndex);
+ });
+
+ it('should saving store.page correctly', () => {
+ expect(props.store.page).toEqual(instance.state.currPage);
+ });
});
});
});
@@ -448,11 +495,16 @@ describe('Wrapper', () => {
expect(onPageChange.calledWith(newPage, instance.state.currSizePerPage)).toBeTruthy();
});
+ it('should saving page and sizePerPage to store correctly', () => {
+ expect(props.store.page).toBe(newPage);
+ expect(props.store.sizePerPage).toBe(instance.state.currSizePerPage);
+ });
+
describe('when pagination remote is enable', () => {
beforeEach(() => {
props.remote = true;
- props.onRemotePageChange = sinon.stub();
createPaginationWrapper(props, false);
+ onRemotePageChangeCB.reset();
instance.handleChangePage(newPage);
});
@@ -460,10 +512,8 @@ describe('Wrapper', () => {
expect(instance.state.currPage).not.toEqual(newPage);
});
- it('should calling options.onRemotePageChange correctly', () => {
- expect(props.onRemotePageChange.calledOnce).toBeTruthy();
- expect(props.onRemotePageChange.calledWith(
- newPage, instance.state.currSizePerPage)).toBeTruthy();
+ it('should calling props.onRemotePageChange correctly', () => {
+ expect(onRemotePageChangeCB.calledOnce).toBeTruthy();
});
});
});
@@ -492,11 +542,16 @@ describe('Wrapper', () => {
expect(onSizePerPageChange.calledWith(newSizePerPage, newPage)).toBeTruthy();
});
+ it('should saving page and sizePerPage to store correctly', () => {
+ expect(props.store.page).toBe(newPage);
+ expect(props.store.sizePerPage).toBe(newSizePerPage);
+ });
+
describe('when pagination remote is enable', () => {
beforeEach(() => {
props.remote = true;
- props.onRemotePageChange = sinon.stub();
createPaginationWrapper(props, false);
+ onRemotePageChangeCB.reset();
instance.handleChangeSizePerPage(newSizePerPage, newPage);
});
@@ -505,9 +560,8 @@ describe('Wrapper', () => {
expect(instance.state.currSizePerPage).not.toEqual(newSizePerPage);
});
- it('should calling options.onRemotePageChange correctly', () => {
- expect(props.onRemotePageChange.calledOnce).toBeTruthy();
- expect(props.onRemotePageChange.calledWith(newPage, newSizePerPage)).toBeTruthy();
+ it('should calling props.onRemotePageChange correctly', () => {
+ expect(onRemotePageChangeCB.calledOnce).toBeTruthy();
});
});
});
diff --git a/packages/react-bootstrap-table2/src/container.js b/packages/react-bootstrap-table2/src/container.js
index a0866a7..8fb7959 100644
--- a/packages/react-bootstrap-table2/src/container.js
+++ b/packages/react-bootstrap-table2/src/container.js
@@ -20,16 +20,35 @@ const withDataStore = Base =>
this.store = new Store(props.keyField);
this.store.data = props.data;
this.handleUpdateCell = this.handleUpdateCell.bind(this);
- this.onRemotePageChange = this.onRemotePageChange.bind(this);
+ this.handleRemotePageChange = this.handleRemotePageChange.bind(this);
+ this.handleRemoteFilterChange = this.handleRemoteFilterChange.bind(this);
}
componentWillReceiveProps(nextProps) {
this.store.data = nextProps.data;
}
- onRemotePageChange(page, sizePerPage) {
- const newState = { page, sizePerPage };
- this.props.onTableChange(newState);
+ getNewestState(state = {}) {
+ return {
+ page: this.store.page,
+ sizePerPage: this.store.sizePerPage,
+ filters: this.store.filters,
+ ...state
+ };
+ }
+
+ handleRemotePageChange() {
+ this.props.onTableChange('pagination', this.getNewestState());
+ }
+
+ // refactoring later for isRemotePagination
+ handleRemoteFilterChange(isRemotePagination) {
+ const newState = {};
+ if (isRemotePagination) {
+ const options = this.props.pagination.options || {};
+ newState.page = _.isDefined(options.pageStartIndex) ? options.pageStartIndex : 1;
+ }
+ this.props.onTableChange('filter', this.getNewestState(newState));
}
handleUpdateCell(rowId, dataField, newValue) {
@@ -72,13 +91,17 @@ const withDataStore = Base =>
} else if (this.props.selectRow) {
return wrapWithSelection(baseProps);
} else if (this.props.filter) {
- return wrapWithFilter(baseProps);
+ return wrapWithFilter({
+ ...baseProps,
+ onRemoteFilterChange: this.handleRemoteFilterChange,
+ onRemotePageChange: this.handleRemotePageChange
+ });
} else if (this.props.columns.filter(col => col.sort).length > 0) {
return wrapWithSort(baseProps);
} else if (this.props.pagination) {
return wrapWithPagination({
...baseProps,
- onRemotePageChange: this.onRemotePageChange
+ onRemotePageChange: this.handleRemotePageChange
});
}
diff --git a/packages/react-bootstrap-table2/src/store/index.js b/packages/react-bootstrap-table2/src/store/index.js
index 97d1aba..b735f86 100644
--- a/packages/react-bootstrap-table2/src/store/index.js
+++ b/packages/react-bootstrap-table2/src/store/index.js
@@ -11,7 +11,9 @@ export default class Store {
this._sortOrder = undefined;
this._sortField = undefined;
this._selected = [];
- this._filtering = false;
+ this._filters = {};
+ this._page = undefined;
+ this._sizePerPage = undefined;
}
edit(rowId, dataField, newValue) {
@@ -30,13 +32,13 @@ export default class Store {
}
get data() {
- if (this._filtering) {
+ if (Object.keys(this._filters).length > 0) {
return this._filteredData;
}
return this._data;
}
set data(data) {
- if (this._filtering) {
+ if (Object.keys(this._filters).length > 0) {
this._filteredData = data;
} else {
this._data = (data ? JSON.parse(JSON.stringify(data)) : []);
@@ -52,12 +54,18 @@ export default class Store {
get sortOrder() { return this._sortOrder; }
set sortOrder(sortOrder) { this._sortOrder = sortOrder; }
+ get page() { return this._page; }
+ set page(page) { this._page = page; }
+
+ get sizePerPage() { return this._sizePerPage; }
+ set sizePerPage(sizePerPage) { this._sizePerPage = sizePerPage; }
+
get sortField() { return this._sortField; }
set sortField(sortField) { this._sortField = sortField; }
get selected() { return this._selected; }
set selected(selected) { this._selected = selected; }
- get filtering() { return this._filtering; }
- set filtering(filtering) { this._filtering = filtering; }
+ get filters() { return this._filters; }
+ set filters(filters) { this._filters = filters; }
}
diff --git a/packages/react-bootstrap-table2/test/container.test.js b/packages/react-bootstrap-table2/test/container.test.js
index 8d1c614..fd4068a 100644
--- a/packages/react-bootstrap-table2/test/container.test.js
+++ b/packages/react-bootstrap-table2/test/container.test.js
@@ -202,9 +202,7 @@ describe('withDataStore', () => {
});
});
- describe('onRemotePageChange', () => {
- const page = 2;
- const sizePerPage = 25;
+ describe('handleRemotePageChange', () => {
const onTableChangeCallBack = sinon.stub();
beforeEach(() => {
@@ -216,12 +214,86 @@ describe('withDataStore', () => {
onTableChange={ onTableChangeCallBack }
/>
);
- wrapper.instance().onRemotePageChange(page, sizePerPage);
+ wrapper.instance().handleRemotePageChange();
});
it('should calling onTableChange correctly', () => {
expect(onTableChangeCallBack.calledOnce).toBeTruthy();
- expect(onTableChangeCallBack.calledWith({ page, sizePerPage })).toBeTruthy();
+ expect(onTableChangeCallBack.calledWith('pagination', wrapper.instance().getNewestState())).toBeTruthy();
+ });
+ });
+
+ describe('handleRemoteFilterChange', () => {
+ const onTableChangeCallBack = sinon.stub();
+
+ beforeEach(() => {
+ onTableChangeCallBack.reset();
+ wrapper = shallow(
+
+ );
+ });
+
+ describe('when isRemotePagination argument is false', () => {
+ beforeEach(() => {
+ wrapper.instance().handleRemoteFilterChange(false);
+ });
+
+ it('should calling onTableChange correctly', () => {
+ expect(onTableChangeCallBack.calledOnce).toBeTruthy();
+ expect(onTableChangeCallBack.calledWith('filter', wrapper.instance().getNewestState())).toBeTruthy();
+ });
+ });
+
+ describe('when isRemotePagination argument is false', () => {
+ describe('and pagination.options.pageStartIndex is defined', () => {
+ const options = { pageStartIndex: 0 };
+ beforeEach(() => {
+ wrapper = shallow(
+ {} } }
+ onTableChange={ onTableChangeCallBack }
+ />
+ );
+ wrapper.instance().handleRemoteFilterChange(true);
+ });
+
+ it('should calling onTableChange correctly', () => {
+ expect(onTableChangeCallBack.calledOnce).toBeTruthy();
+ const newState = wrapper.instance().getNewestState();
+ newState.page = options.pageStartIndex;
+ expect(onTableChangeCallBack.calledWith('filter', newState)).toBeTruthy();
+ });
+ });
+
+ describe('and pagination.options.pageStartIndex is not defined', () => {
+ beforeEach(() => {
+ wrapper = shallow(
+ {} } }
+ onTableChange={ onTableChangeCallBack }
+ />
+ );
+ wrapper.instance().handleRemoteFilterChange(true);
+ });
+
+ it('should calling onTableChange correctly', () => {
+ expect(onTableChangeCallBack.calledOnce).toBeTruthy();
+ const newState = wrapper.instance().getNewestState();
+ newState.page = 1;
+ expect(onTableChangeCallBack.calledWith('filter', newState)).toBeTruthy();
+ });
+ });
});
});
});