diff --git a/docs/README.md b/docs/README.md
index 05da321..5023f08 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -249,13 +249,17 @@ There's only two arguments will be passed to `onTableChange`: `type` and `newSta
* `filter`
* `pagination`
+* `sort`
Following is a shape of `newState`
```js
{
page, // newest page
- sizePerPage, //newest sizePerPage
- filters // an object which have current filter status per column
+ sizePerPage, // newest sizePerPage
+ sortField, // newest sort field
+ sortOrder, // newest sort order
+ filters, // an object which have current filter status per column
+ data // when you enable remote sort, you may need to base on data to sort if data is filtered/searched
}
```
\ No newline at end of file
diff --git a/packages/react-bootstrap-table2-example/examples/remote/remote-sort.js b/packages/react-bootstrap-table2-example/examples/remote/remote-sort.js
new file mode 100644
index 0000000..0ca4377
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/remote/remote-sort.js
@@ -0,0 +1,106 @@
+/* eslint no-restricted-syntax: 0 */
+import React from 'react';
+import PropTypes from 'prop-types';
+import BootstrapTable from 'react-bootstrap-table2';
+import Code from 'components/common/code-block';
+import { productsGenerator } from 'utils/common';
+
+const products = productsGenerator(5);
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name',
+ sort: true
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ sort: true
+}];
+
+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 RemoteSort = props => (
+
+
+ { sourceCode }
+
+);
+
+RemoteSort.propTypes = {
+ data: PropTypes.array.isRequired,
+ onTableChange: PropTypes.func.isRequired
+};
+
+class Container extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ data: products
+ };
+ }
+
+ handleTableChange = (type, { sortField, sortOrder, data }) => {
+ setTimeout(() => {
+ let result;
+ if (sortOrder === 'asc') {
+ result = data.sort((a, b) => {
+ if (a[sortField] > b[sortField]) {
+ return 1;
+ } else if (b[sortField] > a[sortField]) {
+ return -1;
+ }
+ return 0;
+ });
+ } else {
+ result = data.sort((a, b) => {
+ if (a[sortField] > b[sortField]) {
+ return -1;
+ } else if (b[sortField] > a[sortField]) {
+ return 1;
+ }
+ return 0;
+ });
+ }
+ this.setState(() => ({
+ data: result
+ }));
+ }, 2000);
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default Container;
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index 36abbb0..909f5b8 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -88,6 +88,7 @@ import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
import TableOverlay from 'examples/loading-overlay/table-overlay';
// remote
+import RemoteSort from 'examples/remote/remote-sort';
import RemoteFilter from 'examples/remote/remote-filter';
import RemotePaginationTable from 'examples/remote/remote-pagination';
import RemoteAll from 'examples/remote/remote-all';
@@ -190,6 +191,7 @@ storiesOf('EmptyTableOverlay', module)
.add('Table Overlay', () => );
storiesOf('Remote', module)
+ .add('Remote Sort', () => )
.add('Remote Filter', () => )
.add('Remote Pagination', () => )
.add('Remote All', () => );
diff --git a/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js b/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js
index ddadafc..b59c9e3 100644
--- a/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js
+++ b/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js
@@ -8,6 +8,9 @@ export default ExtendBase =>
page: store.page,
sizePerPage: store.sizePerPage,
filters: store.filters,
+ sortField: store.sortField,
+ sortOrder: store.sortOrder,
+ data: store.data,
...state
};
}
@@ -22,6 +25,11 @@ export default ExtendBase =>
return remote === true || (_.isObject(remote) && remote.filter);
}
+ isRemoteSort() {
+ const { remote } = this.props;
+ return remote === true || (_.isObject(remote) && remote.sort);
+ }
+
handleRemotePageChange() {
this.props.onTableChange('pagination', this.getNewestState());
}
@@ -34,4 +42,8 @@ export default ExtendBase =>
}
this.props.onTableChange('filter', this.getNewestState(newState));
}
+
+ handleSortChange() {
+ this.props.onTableChange('sort', this.getNewestState());
+ }
};
diff --git a/packages/react-bootstrap-table2/src/sort/wrapper.js b/packages/react-bootstrap-table2/src/sort/wrapper.js
index 40486fb..45414e3 100644
--- a/packages/react-bootstrap-table2/src/sort/wrapper.js
+++ b/packages/react-bootstrap-table2/src/sort/wrapper.js
@@ -1,9 +1,10 @@
/* eslint react/prop-types: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
+import remoteResolver from '../props-resolver/remote-resolver';
export default Base =>
- class SortWrapper extends Component {
+ class SortWrapper extends remoteResolver(Component) {
static propTypes = {
store: PropTypes.object.isRequired
}
@@ -22,7 +23,13 @@ export default Base =>
const order = defaultSorted[0].order;
const column = columns.filter(col => col.dataField === dataField);
if (column.length > 0) {
- store.sortBy(column[0], order);
+ store.setSort(column[0], order);
+
+ if (this.isRemoteSort() || this.isRemotePagination()) {
+ this.handleSortChange();
+ } else {
+ store.sortBy(column[0]);
+ }
}
}
}
@@ -32,15 +39,21 @@ export default Base =>
const sortedColumn = nextProps.columns.find(
column => column.dataField === nextProps.store.sortField);
if (sortedColumn) {
- nextProps.store.sortBy(sortedColumn, nextProps.store.sortOrder);
+ nextProps.store.sortBy(sortedColumn);
}
}
}
handleSort(column) {
const { store } = this.props;
- store.sortBy(column);
- this.forceUpdate();
+ store.setSort(column);
+
+ if (this.isRemoteSort() || this.isRemotePagination()) {
+ this.handleSortChange();
+ } else {
+ store.sortBy(column);
+ this.forceUpdate();
+ }
}
render() {
diff --git a/packages/react-bootstrap-table2/src/store/index.js b/packages/react-bootstrap-table2/src/store/index.js
index b735f86..9c6e108 100644
--- a/packages/react-bootstrap-table2/src/store/index.js
+++ b/packages/react-bootstrap-table2/src/store/index.js
@@ -21,9 +21,12 @@ export default class Store {
if (row) _.set(row, dataField, newValue);
}
- sortBy({ dataField, sortFunc }, order) {
+ setSort({ dataField }, order) {
this.sortOrder = nextOrder(this)(dataField, order);
this.sortField = dataField;
+ }
+
+ sortBy({ sortFunc }) {
this.data = sort(this)(sortFunc);
}
diff --git a/packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js b/packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js
index fa7d8b3..a6e260d 100644
--- a/packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js
+++ b/packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js
@@ -102,6 +102,52 @@ describe('remoteResolver', () => {
});
});
+ describe('isRemoteSort', () => {
+ describe('when remote is false', () => {
+ beforeEach(() => {
+ shallowContainer();
+ });
+
+ it('should return false', () => {
+ expect(wrapper.instance().isRemoteSort()).toBeFalsy();
+ });
+ });
+
+ describe('when remote is true', () => {
+ beforeEach(() => {
+ shallowContainer({ remote: true });
+ });
+
+ it('should return true', () => {
+ expect(wrapper.instance().isRemoteSort()).toBeTruthy();
+ });
+ });
+
+ describe('when remote.sort is true', () => {
+ beforeEach(() => {
+ shallowContainer({ remote: { sort: true } });
+ });
+
+ it('should return true', () => {
+ expect(wrapper.instance().isRemoteSort()).toBeTruthy();
+ });
+ });
+ });
+
+ describe('handleSortChange', () => {
+ const onTableChangeCB = sinon.stub();
+ beforeEach(() => {
+ onTableChangeCB.reset();
+ shallowContainer({ onTableChange: onTableChangeCB });
+ wrapper.instance().handleSortChange();
+ });
+
+ it('should calling props.onTableChange correctly', () => {
+ expect(onTableChangeCB.calledOnce).toBeTruthy();
+ expect(onTableChangeCB.calledWith('sort', wrapper.instance().getNewestState())).toBeTruthy();
+ });
+ });
+
describe('handleRemotePageChange', () => {
const onTableChangeCB = sinon.stub();
beforeEach(() => {
diff --git a/packages/react-bootstrap-table2/test/sort/wrapper.test.js b/packages/react-bootstrap-table2/test/sort/wrapper.test.js
index 208058d..d8a3ce7 100644
--- a/packages/react-bootstrap-table2/test/sort/wrapper.test.js
+++ b/packages/react-bootstrap-table2/test/sort/wrapper.test.js
@@ -57,28 +57,78 @@ describe('SortWrapper', () => {
});
describe('call handleSort function', () => {
+ let sortBySpy;
const sortColumn = columns[0];
+
beforeEach(() => {
store = new Store(keyField);
store.data = data;
- wrapper = shallow(
-
- );
- wrapper.instance().handleSort(sortColumn);
+ sortBySpy = sinon.spy(store, 'sortBy');
});
- it('should operating on store correctly', () => {
- expect(store.sortOrder).toEqual(Const.SORT_DESC);
- expect(store.sortField).toEqual(sortColumn.dataField);
+ describe('when remote.sort is false', () => {
+ beforeEach(() => {
+ wrapper = shallow(
+
+ );
- wrapper.instance().handleSort(sortColumn); // sort same column again
- expect(store.sortOrder).toEqual(Const.SORT_ASC);
- expect(store.sortField).toEqual(sortColumn.dataField);
+ wrapper.instance().handleSort(sortColumn);
+ });
+
+ it('should operating on store correctly', () => {
+ expect(store.sortOrder).toEqual(Const.SORT_DESC);
+ expect(store.sortField).toEqual(sortColumn.dataField);
+
+ wrapper.instance().handleSort(sortColumn); // sort same column again
+ expect(store.sortOrder).toEqual(Const.SORT_ASC);
+ expect(store.sortField).toEqual(sortColumn.dataField);
+ });
+
+ it('should calling store.sortBy correctly', () => {
+ expect(sortBySpy.calledOnce).toBeTruthy();
+ expect(sortBySpy.calledWith(sortColumn)).toBeTruthy();
+ });
+ });
+
+ describe('when remote.sort is true', () => {
+ let onTableChangeCB;
+
+ beforeEach(() => {
+ onTableChangeCB = sinon.stub();
+ wrapper = shallow(
+
+ );
+ wrapper.instance().handleSort(sortColumn);
+ });
+
+ it('should operating on store correctly', () => {
+ expect(store.sortOrder).toEqual(Const.SORT_DESC);
+ expect(store.sortField).toEqual(sortColumn.dataField);
+
+ wrapper.instance().handleSort(sortColumn); // sort same column again
+ expect(store.sortOrder).toEqual(Const.SORT_ASC);
+ expect(store.sortField).toEqual(sortColumn.dataField);
+ });
+
+ it('should not calling store.sortBy', () => {
+ expect(sortBySpy.calledOnce).toBeFalsy();
+ });
+
+ it('should calling props.onTableChange', () => {
+ expect(onTableChangeCB.calledOnce).toBeTruthy();
+ });
});
});
diff --git a/packages/react-bootstrap-table2/test/store/index.test.js b/packages/react-bootstrap-table2/test/store/index.test.js
index f6f9901..dc8b28e 100644
--- a/packages/react-bootstrap-table2/test/store/index.test.js
+++ b/packages/react-bootstrap-table2/test/store/index.test.js
@@ -24,7 +24,7 @@ describe('Store Base', () => {
});
});
- describe('sortBy', () => {
+ describe('setSort', () => {
let dataField;
beforeEach(() => {
@@ -32,30 +32,43 @@ describe('Store Base', () => {
});
it('should change sortField by dataField param', () => {
- store.sortBy({ dataField });
+ store.setSort({ dataField });
expect(store.sortField).toEqual(dataField);
});
it('should change sortOrder correctly when sortBy same dataField', () => {
- store.sortBy({ dataField });
+ store.setSort({ dataField });
expect(store.sortOrder).toEqual(Const.SORT_DESC);
- store.sortBy({ dataField });
+ store.setSort({ dataField });
expect(store.sortOrder).toEqual(Const.SORT_ASC);
});
it('should change sortOrder correctly when sortBy different dataField', () => {
- store.sortBy({ dataField });
+ store.setSort({ dataField });
expect(store.sortOrder).toEqual(Const.SORT_DESC);
dataField = 'id';
- store.sortBy({ dataField });
+ store.setSort({ dataField });
expect(store.sortOrder).toEqual(Const.SORT_DESC);
dataField = 'name';
- store.sortBy({ dataField });
+ store.setSort({ dataField });
expect(store.sortOrder).toEqual(Const.SORT_DESC);
});
+ it('should force assign sortOrder correctly if second argument is passed', () => {
+ store.setSort({ dataField }, Const.SORT_DESC);
+ expect(store.sortOrder).toEqual(Const.SORT_DESC);
+ });
+ });
+
+ describe('sortBy', () => {
+ let dataField;
+
+ beforeEach(() => {
+ dataField = 'name';
+ });
+
it('should have correct result after sortBy', () => {
store.sortBy({ dataField });
const result = store.data.map(e => e[dataField]).sort((a, b) => b - a);
@@ -63,11 +76,6 @@ describe('Store Base', () => {
expect(e[dataField]).toEqual(result[i]);
});
});
-
- it('should force assign sortOrder correctly if second argument is passed', () => {
- store.sortBy({ dataField }, Const.SORT_DESC);
- expect(store.sortOrder).toEqual(Const.SORT_DESC);
- });
});
describe('edit', () => {