Skip to content

Commit 01337f5

Browse files
committed
implement remote sorting
1 parent bdfc4eb commit 01337f5

File tree

9 files changed

+279
-35
lines changed

9 files changed

+279
-35
lines changed

docs/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,17 @@ There's only two arguments will be passed to `onTableChange`: `type` and `newSta
249249

250250
* `filter`
251251
* `pagination`
252+
* `sort`
252253

253254
Following is a shape of `newState`
254255

255256
```js
256257
{
257258
page, // newest page
258-
sizePerPage, //newest sizePerPage
259-
filters // an object which have current filter status per column
259+
sizePerPage, // newest sizePerPage
260+
sortField, // newest sort field
261+
sortOrder, // newest sort order
262+
filters, // an object which have current filter status per column
263+
data // when you enable remote sort, you may need to base on data to sort if data is filtered/searched
260264
}
261265
```
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* eslint no-restricted-syntax: 0 */
2+
import React from 'react';
3+
import PropTypes from 'prop-types';
4+
import BootstrapTable from 'react-bootstrap-table2';
5+
import Code from 'components/common/code-block';
6+
import { productsGenerator } from 'utils/common';
7+
8+
const products = productsGenerator(5);
9+
10+
const columns = [{
11+
dataField: 'id',
12+
text: 'Product ID'
13+
}, {
14+
dataField: 'name',
15+
text: 'Product Name',
16+
sort: true
17+
}, {
18+
dataField: 'price',
19+
text: 'Product Price',
20+
sort: true
21+
}];
22+
23+
const sourceCode = `\
24+
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
25+
26+
const columns = [{
27+
dataField: 'id',
28+
text: 'Product ID',
29+
}, {
30+
dataField: 'name',
31+
text: 'Product Name',
32+
filter: textFilter()
33+
}, {
34+
dataField: 'price',
35+
text: 'Product Price',
36+
filter: textFilter()
37+
}];
38+
39+
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
40+
`;
41+
42+
const RemoteSort = props => (
43+
<div>
44+
<BootstrapTable
45+
remote={ { sort: true } }
46+
keyField="id"
47+
data={ props.data }
48+
columns={ columns }
49+
onTableChange={ props.onTableChange }
50+
/>
51+
<Code>{ sourceCode }</Code>
52+
</div>
53+
);
54+
55+
RemoteSort.propTypes = {
56+
data: PropTypes.array.isRequired,
57+
onTableChange: PropTypes.func.isRequired
58+
};
59+
60+
class Container extends React.Component {
61+
constructor(props) {
62+
super(props);
63+
this.state = {
64+
data: products
65+
};
66+
}
67+
68+
handleTableChange = (type, { sortField, sortOrder, data }) => {
69+
setTimeout(() => {
70+
let result;
71+
if (sortOrder === 'asc') {
72+
result = data.sort((a, b) => {
73+
if (a[sortField] > b[sortField]) {
74+
return 1;
75+
} else if (b[sortField] > a[sortField]) {
76+
return -1;
77+
}
78+
return 0;
79+
});
80+
} else {
81+
result = data.sort((a, b) => {
82+
if (a[sortField] > b[sortField]) {
83+
return -1;
84+
} else if (b[sortField] > a[sortField]) {
85+
return 1;
86+
}
87+
return 0;
88+
});
89+
}
90+
this.setState(() => ({
91+
data: result
92+
}));
93+
}, 2000);
94+
}
95+
96+
render() {
97+
return (
98+
<RemoteSort
99+
data={ this.state.data }
100+
onTableChange={ this.handleTableChange }
101+
/>
102+
);
103+
}
104+
}
105+
106+
export default Container;

packages/react-bootstrap-table2-example/stories/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
8888
import TableOverlay from 'examples/loading-overlay/table-overlay';
8989

9090
// remote
91+
import RemoteSort from 'examples/remote/remote-sort';
9192
import RemoteFilter from 'examples/remote/remote-filter';
9293
import RemotePaginationTable from 'examples/remote/remote-pagination';
9394
import RemoteAll from 'examples/remote/remote-all';
@@ -190,6 +191,7 @@ storiesOf('EmptyTableOverlay', module)
190191
.add('Table Overlay', () => <TableOverlay />);
191192

192193
storiesOf('Remote', module)
194+
.add('Remote Sort', () => <RemoteSort />)
193195
.add('Remote Filter', () => <RemoteFilter />)
194196
.add('Remote Pagination', () => <RemotePaginationTable />)
195197
.add('Remote All', () => <RemoteAll />);

packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ export default ExtendBase =>
88
page: store.page,
99
sizePerPage: store.sizePerPage,
1010
filters: store.filters,
11+
sortField: store.sortField,
12+
sortOrder: store.sortOrder,
13+
data: store.data,
1114
...state
1215
};
1316
}
@@ -22,6 +25,11 @@ export default ExtendBase =>
2225
return remote === true || (_.isObject(remote) && remote.filter);
2326
}
2427

28+
isRemoteSort() {
29+
const { remote } = this.props;
30+
return remote === true || (_.isObject(remote) && remote.sort);
31+
}
32+
2533
handleRemotePageChange() {
2634
this.props.onTableChange('pagination', this.getNewestState());
2735
}
@@ -34,4 +42,8 @@ export default ExtendBase =>
3442
}
3543
this.props.onTableChange('filter', this.getNewestState(newState));
3644
}
45+
46+
handleSortChange() {
47+
this.props.onTableChange('sort', this.getNewestState());
48+
}
3749
};

packages/react-bootstrap-table2/src/sort/wrapper.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
/* eslint react/prop-types: 0 */
22
import React, { Component } from 'react';
33
import PropTypes from 'prop-types';
4+
import remoteResolver from '../props-resolver/remote-resolver';
45

56
export default Base =>
6-
class SortWrapper extends Component {
7+
class SortWrapper extends remoteResolver(Component) {
78
static propTypes = {
89
store: PropTypes.object.isRequired
910
}
@@ -22,7 +23,13 @@ export default Base =>
2223
const order = defaultSorted[0].order;
2324
const column = columns.filter(col => col.dataField === dataField);
2425
if (column.length > 0) {
25-
store.sortBy(column[0], order);
26+
store.setSort(column[0], order);
27+
28+
if (this.isRemoteSort() || this.isRemotePagination()) {
29+
this.handleSortChange();
30+
} else {
31+
store.sortBy(column[0]);
32+
}
2633
}
2734
}
2835
}
@@ -32,15 +39,21 @@ export default Base =>
3239
const sortedColumn = nextProps.columns.find(
3340
column => column.dataField === nextProps.store.sortField);
3441
if (sortedColumn) {
35-
nextProps.store.sortBy(sortedColumn, nextProps.store.sortOrder);
42+
nextProps.store.sortBy(sortedColumn);
3643
}
3744
}
3845
}
3946

4047
handleSort(column) {
4148
const { store } = this.props;
42-
store.sortBy(column);
43-
this.forceUpdate();
49+
store.setSort(column);
50+
51+
if (this.isRemoteSort() || this.isRemotePagination()) {
52+
this.handleSortChange();
53+
} else {
54+
store.sortBy(column);
55+
this.forceUpdate();
56+
}
4457
}
4558

4659
render() {

packages/react-bootstrap-table2/src/store/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ export default class Store {
2121
if (row) _.set(row, dataField, newValue);
2222
}
2323

24-
sortBy({ dataField, sortFunc }, order) {
24+
setSort({ dataField }, order) {
2525
this.sortOrder = nextOrder(this)(dataField, order);
2626
this.sortField = dataField;
27+
}
28+
29+
sortBy({ sortFunc }) {
2730
this.data = sort(this)(sortFunc);
2831
}
2932

packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,52 @@ describe('remoteResolver', () => {
102102
});
103103
});
104104

105+
describe('isRemoteSort', () => {
106+
describe('when remote is false', () => {
107+
beforeEach(() => {
108+
shallowContainer();
109+
});
110+
111+
it('should return false', () => {
112+
expect(wrapper.instance().isRemoteSort()).toBeFalsy();
113+
});
114+
});
115+
116+
describe('when remote is true', () => {
117+
beforeEach(() => {
118+
shallowContainer({ remote: true });
119+
});
120+
121+
it('should return true', () => {
122+
expect(wrapper.instance().isRemoteSort()).toBeTruthy();
123+
});
124+
});
125+
126+
describe('when remote.sort is true', () => {
127+
beforeEach(() => {
128+
shallowContainer({ remote: { sort: true } });
129+
});
130+
131+
it('should return true', () => {
132+
expect(wrapper.instance().isRemoteSort()).toBeTruthy();
133+
});
134+
});
135+
});
136+
137+
describe('handleSortChange', () => {
138+
const onTableChangeCB = sinon.stub();
139+
beforeEach(() => {
140+
onTableChangeCB.reset();
141+
shallowContainer({ onTableChange: onTableChangeCB });
142+
wrapper.instance().handleSortChange();
143+
});
144+
145+
it('should calling props.onTableChange correctly', () => {
146+
expect(onTableChangeCB.calledOnce).toBeTruthy();
147+
expect(onTableChangeCB.calledWith('sort', wrapper.instance().getNewestState())).toBeTruthy();
148+
});
149+
});
150+
105151
describe('handleRemotePageChange', () => {
106152
const onTableChangeCB = sinon.stub();
107153
beforeEach(() => {

packages/react-bootstrap-table2/test/sort/wrapper.test.js

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,28 +57,78 @@ describe('SortWrapper', () => {
5757
});
5858

5959
describe('call handleSort function', () => {
60+
let sortBySpy;
6061
const sortColumn = columns[0];
62+
6163
beforeEach(() => {
6264
store = new Store(keyField);
6365
store.data = data;
64-
wrapper = shallow(
65-
<SortWrapper
66-
keyField={ keyField }
67-
data={ data }
68-
columns={ columns }
69-
store={ store }
70-
/>
71-
);
72-
wrapper.instance().handleSort(sortColumn);
66+
sortBySpy = sinon.spy(store, 'sortBy');
7367
});
7468

75-
it('should operating on store correctly', () => {
76-
expect(store.sortOrder).toEqual(Const.SORT_DESC);
77-
expect(store.sortField).toEqual(sortColumn.dataField);
69+
describe('when remote.sort is false', () => {
70+
beforeEach(() => {
71+
wrapper = shallow(
72+
<SortWrapper
73+
keyField={ keyField }
74+
data={ data }
75+
columns={ columns }
76+
store={ store }
77+
/>
78+
);
79+
80+
wrapper.instance().handleSort(sortColumn);
81+
});
82+
83+
it('should operating on store correctly', () => {
84+
expect(store.sortOrder).toEqual(Const.SORT_DESC);
85+
expect(store.sortField).toEqual(sortColumn.dataField);
86+
87+
wrapper.instance().handleSort(sortColumn); // sort same column again
88+
expect(store.sortOrder).toEqual(Const.SORT_ASC);
89+
expect(store.sortField).toEqual(sortColumn.dataField);
90+
});
7891

79-
wrapper.instance().handleSort(sortColumn); // sort same column again
80-
expect(store.sortOrder).toEqual(Const.SORT_ASC);
81-
expect(store.sortField).toEqual(sortColumn.dataField);
92+
it('should calling store.sortBy correctly', () => {
93+
expect(sortBySpy.calledOnce).toBeTruthy();
94+
expect(sortBySpy.calledWith(sortColumn)).toBeTruthy();
95+
});
96+
});
97+
98+
describe('when remote.sort is true', () => {
99+
let onTableChangeCB;
100+
101+
beforeEach(() => {
102+
onTableChangeCB = sinon.stub();
103+
wrapper = shallow(
104+
<SortWrapper
105+
remote
106+
keyField={ keyField }
107+
data={ data }
108+
columns={ columns }
109+
store={ store }
110+
onTableChange={ onTableChangeCB }
111+
/>
112+
);
113+
wrapper.instance().handleSort(sortColumn);
114+
});
115+
116+
it('should operating on store correctly', () => {
117+
expect(store.sortOrder).toEqual(Const.SORT_DESC);
118+
expect(store.sortField).toEqual(sortColumn.dataField);
119+
120+
wrapper.instance().handleSort(sortColumn); // sort same column again
121+
expect(store.sortOrder).toEqual(Const.SORT_ASC);
122+
expect(store.sortField).toEqual(sortColumn.dataField);
123+
});
124+
125+
it('should not calling store.sortBy', () => {
126+
expect(sortBySpy.calledOnce).toBeFalsy();
127+
});
128+
129+
it('should calling props.onTableChange', () => {
130+
expect(onTableChangeCB.calledOnce).toBeTruthy();
131+
});
82132
});
83133
});
84134

0 commit comments

Comments
 (0)