Compare commits

...

136 Commits

Author SHA1 Message Date
AllenFang
e2e6c51d40 Publish
- react-bootstrap-table2-example@1.0.24
 - react-bootstrap-table2-filter@1.1.9
 - react-bootstrap-table2-paginator@2.0.6
 - react-bootstrap-table2-toolkit@1.4.1
 - react-bootstrap-table-next@3.1.1
2019-04-15 19:45:38 +08:00
Allen
fb724331d3 Merge pull request #900 from react-bootstrap-table/develop
20180408 release
2019-04-15 19:38:41 +08:00
AllenFang
160dede412 fix #898 2019-04-07 17:33:00 +08:00
AllenFang
363a43251f fix #891 2019-04-07 16:49:03 +08:00
AllenFang
8ad0e65679 Merge branch 'SmaranSingh-master' into develop 2019-04-07 14:32:41 +08:00
AllenFang
6d5cca0047 optimize selection option equals 2019-04-07 14:31:43 +08:00
AllenFang
204e75c9c2 Merge branch 'master' of https://github.com/SmaranSingh/react-bootstrap-table2 into SmaranSingh-master 2019-04-07 14:30:21 +08:00
AllenFang
fc27c56cbb Merge branch 'Jayboy75-develop' into develop 2019-04-07 14:05:40 +08:00
AllenFang
8436edba7e no inline-block on label element 2019-04-07 14:03:42 +08:00
SmaranSingh
3606fa3b7b Update select.js 2019-04-04 16:11:01 +05:30
SmaranSingh
73e8701bde Update select.js 2019-04-04 15:37:34 +05:30
Jay Staudt
6d2493d537 revert page-button title 2019-04-02 14:53:52 -04:00
Jay Staudt
1a1f6969cb adjustments via eslint 2019-04-02 14:42:47 -04:00
Jay Staudt
d47a3757b8 wrap search bar with label and hidden text 2019-04-02 14:10:14 -04:00
Jay Staudt
ba7512969e add non-redundant titles to page buttons 2019-04-02 14:09:53 -04:00
Jay Staudt
955ee17939 wrap filter components with labels and hidden text 2019-04-02 14:09:20 -04:00
AllenFang
497bf44192 Publish
- react-bootstrap-table2-editor@1.2.3
 - react-bootstrap-table2-example@1.0.23
 - react-bootstrap-table2-filter@1.1.8
 - react-bootstrap-table-next@3.1.0
2019-04-02 21:33:49 +08:00
Allen
fcefcf8c84 Merge pull request #886 from react-bootstrap-table/develop
20190331 release
2019-04-02 21:25:03 +08:00
Allen
04e3af0bbb Merge pull request #861 from react-bootstrap-table/feature/row-expand-animate
Feature/row expand animate
2019-03-31 16:02:13 +08:00
AllenFang
9f47fa009c lock version 2019-03-31 15:33:43 +08:00
Alek Lefebvre
0edf9c8891 fix #808 (#874) 2019-03-31 15:17:57 +08:00
AllenFang
df5024892c Publish
- react-bootstrap-table2-example@1.0.22
 - react-bootstrap-table2-paginator@2.0.5
 - react-bootstrap-table-next@3.0.3
2019-03-26 20:41:04 +08:00
Allen
4448c3f28c Merge pull request #875 from react-bootstrap-table/develop
20190326 release
2019-03-26 20:39:13 +08:00
AllenFang
196ae33295 fix #859 2019-03-25 23:07:22 +08:00
AllenFang
7f1b7a6c97 fix #838 2019-03-24 20:52:26 +08:00
AllenFang
a6ccafcc75 fix #866 2019-03-24 16:55:56 +08:00
AllenFang
06d87299a3 Publish
- react-bootstrap-table2-example@1.0.21
 - react-bootstrap-table2-filter@1.1.7
 - react-bootstrap-table2-toolkit@1.4.0
 - react-bootstrap-table-next@3.0.2
2019-03-18 00:31:24 +08:00
Allen
5891ec1b93 Merge pull request #862 from react-bootstrap-table/develop
20190317 release
2019-03-18 00:28:47 +08:00
AllenFang
c5d9e04c2c fix #817 2019-03-17 16:30:39 +08:00
AllenFang
dba3da28c1 Merge branch 'YassienW-animate-row-expansion' into feature/row-expand-animate 2019-03-17 15:24:07 +08:00
AllenFang
a0e09cd804 fix conflicts 2019-03-17 15:20:16 +08:00
AllenFang
d0e70f7246 fix #852 2019-03-17 14:43:39 +08:00
henning-kvinnesland
b93c683f17 Fix typo. (#855)
`to cusom the sort caret` => `to customize the sort-caret`
2019-03-17 13:30:35 +08:00
henning-kvinnesland
f80e1ea66c Fix typo. (#856)
`chagne` => `change`
2019-03-17 13:30:21 +08:00
AllenFang
7642bfa1a3 fix #849 2019-03-17 13:29:17 +08:00
Yassien
8f304a849f Animation now works with custom expand management 2019-03-16 19:03:22 +02:00
AllenFang
956f1cef4d Publish
- react-bootstrap-table2-example@1.0.20
 - react-bootstrap-table2-filter@1.1.6
 - react-bootstrap-table2-paginator@2.0.4
 - react-bootstrap-table2-toolkit@1.3.2
 - react-bootstrap-table-next@3.0.1
2019-03-10 13:38:21 +08:00
Allen
c45deee590 Merge pull request #846 from react-bootstrap-table/develop
20190310 release
2019-03-10 13:37:02 +08:00
AllenFang
2aab4301dd fix #835 2019-03-09 23:01:21 +08:00
AllenFang
43aa280761 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2019-03-09 23:00:59 +08:00
AllenFang
3af30a0265 fix #840 2019-03-09 22:35:49 +08:00
AllenFang
4b8b8b261e update story 2019-03-09 22:31:24 +08:00
Norbert Nemeth
e44782f222 fix #863 - expanded row column span does not update (#837) 2019-03-09 18:46:04 +08:00
AllenFang
921e8c7ecc fix #830 2019-03-09 18:18:20 +08:00
AllenFang
a3b3ce0dc4 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2019-03-09 15:52:37 +08:00
AllenFang
e9f08d278d fix #826 2019-03-09 15:52:10 +08:00
Sartaj Singh Baveja
b4973c826c Minor update to docs (#841) 2019-03-06 22:28:41 +08:00
Yassien
33c026c7e2 Added original padding without breaking the animation 2019-02-25 13:01:42 +02:00
AllenFang
6cac7f6dc8 Publish
- react-bootstrap-table2-example@1.0.19
 - react-bootstrap-table2-filter@1.1.5
 - react-bootstrap-table2-toolkit@1.3.1
 - react-bootstrap-table-next@3.0.0
2019-02-24 17:01:52 +08:00
Allen
da5b93c3cf Merge pull request #818 from react-bootstrap-table/develop
20190224 release
2019-02-24 16:59:43 +08:00
AllenFang
3ffccce1fe fix #817 2019-02-24 16:20:44 +08:00
AllenFang
09f21e8130 allow to custom className on clear search and export csv button 2019-02-24 16:11:38 +08:00
AllenFang
bf0c5c43a2 fix #735 2019-02-24 15:50:13 +08:00
AllenFang
c01f45a719 fix #789 2019-02-24 14:56:27 +08:00
AllenFang
d26c13b9be fix #815 2019-02-24 14:11:05 +08:00
Yassien
d84fd5c801 CSS Animation for row expansion 2019-02-23 22:40:05 +02:00
AllenFang
8e940112f5 fix #811 2019-02-23 16:13:40 +08:00
AllenFang
6070d150a9 Publish
- react-bootstrap-table2-example@1.0.18
 - react-bootstrap-table2-paginator@2.0.3
 - react-bootstrap-table2-toolkit@1.3.0
 - react-bootstrap-table-next@2.2.0
2019-02-20 23:08:26 +08:00
Allen
dab6f1b206 Merge pull request #810 from react-bootstrap-table/develop
20190220 release
2019-02-20 23:05:03 +08:00
Allen
2a497194e7 Merge pull request #801 from react-bootstrap-table/enhance/795
Implement PaginationTotalStandalone
2019-02-17 17:41:57 +08:00
AllenFang
f5f17897fd add story for PaginationTotalStandalone 2019-02-17 17:29:56 +08:00
AllenFang
2932b8a1b8 fix #795 2019-02-17 17:29:56 +08:00
AllenFang
93103e5ca0 exclude examples from coverage 2019-02-17 17:17:56 +08:00
AllenFang
a7c2a49182 fix #793 2019-02-17 15:25:32 +08:00
Allen
cd27ff98ff Merge pull request #800 from react-bootstrap-table/feat/723
Implement Column Toggle(723)
2019-02-17 15:24:17 +08:00
AllenFang
4d815894e6 add stories for #723 2019-02-17 14:55:44 +08:00
AllenFang
d9ff201373 fix #723 2019-02-17 14:55:17 +08:00
AllenFang
8e142de332 Publish
- react-bootstrap-table2-example@1.0.17
 - react-bootstrap-table2-filter@1.1.4
 - react-bootstrap-table2-toolkit@1.2.2
 - react-bootstrap-table-next@2.1.2
2019-02-16 17:11:10 +08:00
Allen
322605f14e Merge pull request #798 from react-bootstrap-table/develop
20190216 release
2019-02-16 17:09:40 +08:00
Allen
3156e01dd6 fix #788 (#797) 2019-02-16 16:26:26 +08:00
Allen
052284a163 fix #791 (#796) 2019-02-16 16:18:33 +08:00
AllenFang
3cd8efffb9 Publish
- react-bootstrap-table2-example@1.0.16
 - react-bootstrap-table2-filter@1.1.3
 - react-bootstrap-table2-paginator@2.0.2
 - react-bootstrap-table2-toolkit@1.2.1
 - react-bootstrap-table-next@2.1.1
2019-02-09 21:07:26 +08:00
Allen
447d69cae5 Merge pull request #790 from react-bootstrap-table/develop
20190209 release
2019-02-09 21:04:28 +08:00
AllenFang
cacc28e1bc fix test cases 2019-02-09 20:28:28 +08:00
AllenFang
d7f84a9da5 fix remote filter/search broken when pagination enabled 2019-02-09 18:01:53 +08:00
AllenFang
63c2630f46 fix same issue #778, but for search 2019-02-07 15:44:15 +08:00
AllenFang
903dd2e5c8 rename listenerForPagination -> dataChangeListener 2019-02-07 15:34:03 +08:00
AllenFang
964faa53e3 fix wrong storyb code 2019-02-07 13:56:42 +08:00
AllenFang
8fb5364cc2 add story for #445 2019-02-06 22:56:53 +08:00
AllenFang
8b89b3de0e fix #445 2019-02-06 22:56:31 +08:00
AllenFang
4506a3dea2 add story for #778 2019-02-06 18:06:08 +08:00
AllenFang
ecea3efdaa fix #778 2019-02-06 18:05:53 +08:00
AllenFang
8bef7eb348 fix #672 2019-02-06 15:15:26 +08:00
AllenFang
408a004f58 Publish
- react-bootstrap-table2-example@1.0.15
 - react-bootstrap-table2-filter@1.1.2
 - react-bootstrap-table2-toolkit@1.2.0
 - react-bootstrap-table-next@2.1.0
2019-01-20 22:50:11 +08:00
Allen
bf46dfa026 Merge pull request #766 from react-bootstrap-table/develop
20190120 release
2019-01-20 22:47:34 +08:00
AllenFang
f1b39e3dd6 patch docs for clear search button 2019-01-20 17:20:41 +08:00
AllenFang
029cd3ab6f add story for #751 2019-01-20 17:16:44 +08:00
AllenFang
1c68892a7b fix #751 2019-01-20 17:15:54 +08:00
AllenFang
1bf3fa50db patch docs for table footer 2019-01-20 15:40:51 +08:00
AllenFang
9988e790c1 Merge branch 'osiux-table-footer' into develop 2019-01-20 14:39:26 +08:00
AllenFang
bb071c98f9 fix typo 2019-01-20 14:19:12 +08:00
AllenFang
9c5d8aac62 Merge branch 'table-footer' of https://github.com/osiux/react-bootstrap-table2 into osiux-table-footer 2019-01-20 14:11:13 +08:00
AllenFang
7c79b64985 enhance clear filter story 2019-01-20 14:07:05 +08:00
Martins Linde
16f89989f0 Allow null date when clearing date filter with getFilter function (#759)
* fix date filter's getFilter function to accept empty value for clearing

* syntax improvements from ESlint
2019-01-20 14:03:24 +08:00
Eduardo Reveles
58cfdacfd1 fix error 2019-01-16 00:11:27 -06:00
Eduardo Reveles
78d5164056 add more examples to storybook 2019-01-15 21:40:01 -06:00
Eduardo Reveles
bd2ce5abf0 Add table footer 2019-01-15 21:40:01 -06:00
AllenFang
416fcf08d4 Publish
- react-bootstrap-table2-example@1.0.14
 - react-bootstrap-table2-filter@1.1.1
 - react-bootstrap-table2-paginator@2.0.1
 - react-bootstrap-table2-toolkit@1.1.2
 - react-bootstrap-table-next@2.0.1
2019-01-06 17:06:20 +08:00
Allen
0c650c0682 Merge pull request #747 from react-bootstrap-table/develop
20190106 release
2019-01-06 17:03:34 +08:00
Allen
7d30804da9 fix #636 (#746) 2019-01-06 16:25:02 +08:00
Allen
782c630e58 fix #734, fix #620 (#745) 2019-01-06 14:47:22 +08:00
Allen
22cc79961f fix #715 (#743) 2019-01-05 20:45:18 +08:00
Allen
a83b3d0d78 fix #729 (#742) 2019-01-05 17:00:52 +08:00
Allen
340ddb8722 fix #719 (#741) 2019-01-05 16:09:12 +08:00
AllenFang
32e455e65f add remote standalone pagination story 2019-01-05 14:37:07 +08:00
AllenFang
6c0fc2748c Publish
- react-bootstrap-table2-example@1.0.13
 - react-bootstrap-table2-paginator@2.0.0
 - react-bootstrap-table-next@2.0.0
2018-12-25 23:38:34 +08:00
Allen
973ece8b39 Merge pull request #727 from react-bootstrap-table/develop
20181225 release
2018-12-25 23:37:05 +08:00
Allen
0a94f3ce39 Merge pull request #681 from react-bootstrap-table/refactoring/pagination
Easy to customize pagination
2018-12-24 11:18:44 +08:00
AllenFang
0ca8e54ce2 Publish
- react-bootstrap-table-next@1.4.4
2018-12-23 20:30:41 +08:00
Allen
69d534e26c Merge pull request #726 from react-bootstrap-table/develop
20181223 release
2018-12-23 20:29:20 +08:00
AllenFang
c2a30cb716 fix #725 2018-12-23 19:44:26 +08:00
AllenFang
83dc888d17 patch docs for new pagination 2018-12-22 15:56:14 +08:00
AllenFang
41da9afbcb paginationBaseProps -> paginationTableProps 2018-12-22 15:55:02 +08:00
AllenFang
91816fcc01 add stories for custom pagination 2018-12-22 14:03:02 +08:00
AllenFang
620309115f extract useless function from pagination ro paginationHandler 2018-12-22 14:03:02 +08:00
AllenFang
297f3e0c4f implement SizePerPageDropdownStandalone 2018-12-22 14:03:02 +08:00
AllenFang
d42a10bbae implement PaginationListStandalone 2018-12-22 14:03:02 +08:00
AllenFang
5c52412542 fix remote pagination broken 2018-12-22 14:03:02 +08:00
AllenFang
a5f74cecfe implement sizePerPageRenderer 2018-12-22 14:03:02 +08:00
AllenFang
a30a8fd96b implement sizePerPageOptionRenderer 2018-12-22 14:03:02 +08:00
AllenFang
424dbea270 implement pageListRenderer 2018-12-22 14:03:02 +08:00
AllenFang
b261c33e37 implement pageButtonRenderer 2018-12-22 14:03:02 +08:00
AllenFang
7dbdc1943b patch pagination context test suites 2018-12-22 14:03:02 +08:00
AllenFang
d4be1675db refactoring pagination context 2018-12-22 14:03:02 +08:00
AllenFang
d3161f02eb Publish
- react-bootstrap-table2-example@1.0.12
 - react-bootstrap-table-next@1.4.3
2018-12-21 16:51:04 +08:00
Allen
28ba6d5677 Merge pull request #721 from react-bootstrap-table/develop
20181221 release
2018-12-21 16:49:35 +08:00
AllenFang
4ddbfd4972 fix #711 2018-12-21 16:23:38 +08:00
AllenFang
d84dc46ff1 Merge branch 'vinzentt-fix-nonExpandable-indicator-and-behaviour' into develop 2018-12-20 23:52:42 +08:00
AllenFang
e0163625d4 add expandable for expandColumnRenderer 2018-12-20 23:47:30 +08:00
AllenFang
24ab58a464 row expandable already calculated from row expand consumer, just use it instead do the logic again 2018-12-20 23:45:20 +08:00
Allen
6e19368733 Merge branch 'develop' into fix-nonExpandable-indicator-and-behaviour 2018-12-20 23:27:48 +08:00
AllenFang
bc4697bf95 fix #701 2018-12-20 23:20:12 +08:00
Vincent Degroote
aedd1f5942 Add expandColumn in storybook example for nonExpandable rows 2018-12-05 11:37:32 +01:00
Vincent Degroote
5a5f10f609 fix tests when nonExpndable is not given 2018-12-05 11:23:38 +01:00
Vincent Degroote
e041a3d736 revert not needed changes 2018-12-05 10:55:04 +01:00
Vincent Degroote
f175fd4186 Hide expand indicator if row is not expandable and prevent it from being expanded 2018-12-05 10:50:52 +01:00
154 changed files with 8712 additions and 2010 deletions

View File

@@ -28,10 +28,6 @@ See [getting started](https://react-bootstrap-table.github.io/react-bootstrap-ta
See `react-bootstrap-table2` [storybook](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html).
## Roadmap
See [release plans](https://react-bootstrap-table.github.io/react-bootstrap-table2/blog/2018/01/24/release-plan.html).
## Development
Please check the [development guide](./docs/development.md).

View File

@@ -27,11 +27,13 @@
* [rowStyle](#rowStyle)
* [rowClasses](#rowClasses)
* [rowEvents](#rowEvents)
* [hiddenRows](#hiddenRows)
* [defaultSorted](#defaultSorted)
* [defaultSortDirection](#defaultSortDirection)
* [pagination](#pagination)
* [filter](#filter)
* [onTableChange](#onTableChange)
* [onDataSizeChange](#onDataSizeChange)
### <a name='keyField'>keyField(**required**) - [String]</a>
Tells `react-bootstrap-table2` which column is unique.
@@ -181,6 +183,14 @@ const rowEvents = {
<BootstrapTable data={ data } columns={ columns } rowEvents={ rowEvents } />
```
### <a name='hiddenRows'>hiddenRows - [Array]</a>
Hide rows, this props accept an array of row keys:
```js
const hiddenRows = [1, 4];
<BootstrapTable data={ data } columns={ columns } hiddenRows={ hiddenRows } />
```
### <a name='defaultSorted'>defaultSorted - [Array]</a>
`defaultSorted` accept an object array which allow you to define the default sort columns when first render.
@@ -309,3 +319,19 @@ Following is a shape of `newState`
}
}
```
### <a name='onDataSizeChange'>onDataSizeChange - [Function]</a>
This callback function will be called only when data size change by search/filter etc. This function have one argument which is an object contains below props:
* `dataSize`: The new data size
```js
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
<BootstrapTable
onDataSizeChange={ handleDataChange }
....
/>
```

View File

@@ -69,7 +69,7 @@ const cellEdit: {
// omit...
beforeSaveCell(oldValue, newValue, row, column, done) {
setTimeout(() => {
if (confirm('Do you want to accep this change?')) {
if (confirm('Do you want to accept this change?')) {
done(); // contine to save the changes
} else {
done(false); // reject the changes

View File

@@ -30,6 +30,14 @@ Available properties in a column object:
* [headerAttrs](#headerAttrs)
* [headerSortingClasses](#headerSortingClasses)
* [headerSortingStyle](#headerSortingStyle)
* [footer](#footer)
* [footerFormatter](#footerFormatter)
* [footerClasses](#footerClasses)
* [footerStyle](#footerStyle)
* [footerTitle](#footerTitle)
* [footerEvents](#footerEvents)
* [footerAlign](#footerAlign)
* [footerAttrs](#footerAttrs)
* [editable](#editable)
* [validator](#validator)
* [editCellStyle](#editCellStyle)
@@ -156,7 +164,7 @@ Enable the column sort via a `true` value given.
```
## <a name='sortCaret'>column.sortCaret - [Function]</a>
Use`column.sortCaret` to custom the sort caret. This callback function accept two arguments: `order` and `column`
Use`column.sortCaret` to customize the sort caret. This callback function accept two arguments: `order` and `column`
```js
{
@@ -302,7 +310,7 @@ A new `Object` will be the result of element headerStyle.
A new `String` will be the result of element title.
## <a name='headerTitle'>column.headerTitle - [Bool | Function]</a>
`headerTitle` is only for the title on header column, default is disable. The usage almost same as [`column.title`](#title),
Configure the title on header column, default is disable. The usage almost same as [`column.title`](#title),
```js
{
@@ -518,6 +526,162 @@ const sortingHeaderStyle = {
};
```
### <a name='footer'>footer - [String | Function]</a>
Give a string to render the string value on the footer column.
```js
const columns = [{
dataField: 'id',
text: 'Product ID',
footerAlign: 'center',
footer: 'Footer 1'
}, .....];
```
This prop also accept a function:
```js
{
dataField: 'price',
text: 'Product Price',
footer: column => column.reduce((acc, item) => acc + item, 0)
}
```
## <a name='footerFormatter'>column.footerFormatter - [Function]</a>
`footerFormatter` allow you to customize the table footer column and only accept a callback function which take two arguments and a JSX/String are expected for return.
* `column`
* `columnIndex`
## <a name='footerClasses'>column.footerClasses - [String | Function]</a>
It's similar to [`column.classes`](#classes), `footerClasses` is available to have customized class on table footer column:
```js
{
// omit...
footerClasses: 'id-custom-cell'
}
```
Furthermore, it also accept a callback function which takes 2 arguments and a `String` is expect to return:
```js
{
footerClasses: function callback(column, colIndex) { ... }
}
```
**Parameters**
* `column`: The value of current column.
* `colIndex`: The index of the current `column` being processed in `BootstrapTable`.
## <a name='footerStyle'>column.footerStyle - [Object | Function]</a>
Customized the inline-style on table footer column:
```js
{
// omit...
footerStyle: { backgroundColor: 'green' }
}
```
Moreover, it also accept a callback function which takes **2** arguments and an `Object` is expect to return:
```js
{
footerStyle: function callback(column, colIndex) { ... }
}
```
**Parameters**
* `column`: The value of current column.
* `colIndex`: The index of the current `column` being processed in `BootstrapTable`.
## <a name='footerTitle'>column.footerTitle - [Bool | Function]</a>
Configure the title on footer column, default is disable. The usage is almost same as [`column.title`](#title),
```js
{
// omit...
footerTitle: true
}
```
It's also available to custom via a callback function:
```js
{
footerTitle: function callback(column, colIndex) { ... }
}
```
**Parameters**
* `column`: The value of current column.
* `colIndex`: The index of the current `column` being processed in `BootstrapTable`.
## <a name='footerEvents'>column.footerEvents - [Object]</a>
`footerEvents` same as [`column.events`](#events) but it is for footer column:
```js
{
// omit...
footerEvents: {
onClick: e => { ... }
}
}
```
## <a name='footerAlign'>column.footerAlign - [String | Function]</a>
It's almost same as [`column.align`](#align), but it's for the [CSS text-align](https://www.w3schools.com/cssref/pr_text_text-align.asp) on footer column.
```js
{
// omit...
footerAlign: 'center'
}
```
Also, you can custom the align by a callback function:
```js
{
// omit...
footerAlign: (column, colIndex) => {
// column is an object and perform itself
// return custom title here
}
}
```
**Parameters**
* `column`: The value of current column.
* `colIndex`: The index of the current `column` being processed in `BootstrapTable`.
## <a name='footerAttrs'>column.footerAttrs - [Object | Function]</a>
`footerAttrs` is similar to [`column.attrs`](#attrs) but it works for footer column.
```js
{
// omit...
footerAttrs: {
title: 'bar',
'data-test': 'foo'
}
}
```
Additionally, customize the header attributes by a **2** arguments callback function:
```js
{
// omit...
footerAttrs: (column, colIndex) => ({
// return customized HTML attribute here
})
}
```
**Parameters**
* `column`: The value of current column.
* `colIndex`: The index of the current `column` being processed in `BootstrapTable`.
## <a name='editable'>column.editable - [Bool | Function]</a>
`column.editable` default is true, means every column is editable if you configure [`cellEdit`](./README.md#cellEdit). But you can disable some columns editable via setting `false`.

View File

@@ -23,7 +23,7 @@ Currently, **I still can't implement all the mainly features in legacy `react-bo
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
* Overlay/Loading Addons
* [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit)
* Table Toolkits, like search, csv etc.
* Table Toolkits, like search, csv, column toggle etc.
This can help your application with less bundled size and also help `react-bootstrap-table2` have clean design to avoid handling to much logic in kernel module(SRP). Hence, which means you probably need to install above addons when you need specific features.
@@ -113,14 +113,12 @@ Please see [available pagination configurations](https://react-bootstrap-table.g
Remember to install [`react-bootstrap-table2-paginator`](https://www.npmjs.com/package/react-bootstrap-table2-paginator) firstly.
No big changes for pagination, but still can't custom the pagination list, button and sizePerPage dropdown.
## Table Search
he usage of search functionality is a little bit different from legacy search. The mainly different thing is developer have to render the search input field, we do believe it will be very flexible for all the developers who want to custom the search position or search field itself.
- [x] Custom search component and position
- [x] Custom search value
- [ ] Clear search
- [x] Clear search
- [ ] Multiple search
- [ ] Strict search

View File

@@ -2,7 +2,7 @@
# Row expand
`react-bootstrap-table2` supports the row expand feature. By passing prop `expandRow` to enable this functionality.
> Default is click to expand/collapse a row. In addition, we don't support any way to chagne this mechanism!
> Default is click to expand/collapse a row. In addition, we don't support any way to change this mechanism!
## Required
* [renderer (**required**)](#renderer)
@@ -15,6 +15,7 @@
* [showExpandColumn](#showExpandColumn)
* [onlyOneExpanding](#onlyOneExpanding)
* [expandByColumnOnly](#expandByColumnOnly)
* [expandColumnPosition](#expandColumnPosition)
* [expandColumnRenderer](#expandColumnRenderer)
* [expandHeaderColumnRenderer](#expandHeaderColumnRenderer)
@@ -92,13 +93,16 @@ const expandRow = {
```
### <a name='expandColumnRenderer'>expandRow.expandColumnRenderer - [Function]</a>
Provide a callback function which allow you to custom the expand indicator. This callback only have one argument which is an object and contain one property `expanded` which indicate if current row is expanded
Provide a callback function which allow you to custom the expand indicator. This callback only have one argument which is an object and contain these properties:
* `expanded`: If current row is expanded or not
* `rowKey`: Current row key
* `expandable`: If currnet row is expandable or not
```js
const expandRow = {
renderer: (row) => ...
expandColumnRenderer: ({ expanded }) => (
expandColumnRenderer: ({ expanded, rowKey, expandable }) => (
// ....
)
};
@@ -150,3 +154,14 @@ const expandRow = {
expandByColumnOnly: true
};
```
### <a name='expandColumnPosition'>expandRow.expandColumnPosition - [String]</a>
Default is `left`. You can give this as `right` for rendering expand column in the right side.
```js
const expandRow = {
renderer: (row) => ...,
showExpandColumn: true,
expandColumnPosition: 'right'
};
```

View File

@@ -176,7 +176,7 @@ Provide a callback function which allow you to custom the checkbox/radio box. Th
```js
const selectRow = {
mode: 'checkbox',
selectionRenderer: ({ mode, checked, disabled }) => (
selectionRenderer: ({ mode, checked, disabled, rowIndex }) => (
// ....
)
};

View File

@@ -89,7 +89,8 @@
"jest": {
"collectCoverageFrom": [
"packages/*/src/**/*.js",
"packages/*/index.js"
"packages/*/index.js",
"!packages/react-bootstrap-table2-example/**/*.js"
],
"roots": [
"<rootDir>/packages"

View File

@@ -132,7 +132,7 @@ const columns = [
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`;
return `${('0' + dateObj.getUTCDate()).slice(-2)}/${('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/${dateObj.getUTCFullYear()}`;
},
editor: {
type: Type.DATE

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-editor",
"version": "1.2.2",
"version": "1.2.3",
"description": "it's the editor addon for react-bootstrap-table2",
"main": "./lib/index.js",
"scripts": {

View File

@@ -9,6 +9,7 @@ const sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style');
const toolkitSourcePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/index.js');
const toolkitStylePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/style');
const storyPath = path.join(__dirname, '../stories');
const examplesPath = path.join(__dirname, '../examples');
const srcPath = path.join(__dirname, '../src');
@@ -43,7 +44,13 @@ const loaders = [{
}, {
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: [storyPath, sourceStylePath, paginationStylePath, filterStylePath],
include: [
storyPath,
sourceStylePath,
paginationStylePath,
filterStylePath,
toolkitStylePath
],
}, {
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000',

View File

@@ -0,0 +1,81 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { ToggleList } = ColumnToggle;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
const { ToggleList } = ColumnToggle;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,90 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { SearchBar, ClearSearchButton } = Search;
const { ExportCSVButton } = CSVExport;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search, CSVExport } from 'react-bootstrap-table2-toolkit';
const { SearchBar, ClearSearchButton } = Search;
const { ExportCSVButton } = CSVExport;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<ClearSearchButton { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<ClearSearchButton { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -18,6 +18,11 @@ class QualityRanger extends React.Component {
static defaultProps = {
value: 0
}
componentDidMount() {
this.range.focus();
}
getValue() {
return parseInt(this.range.value, 10);
}

View File

@@ -23,7 +23,7 @@ const columns = [{
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`;
return `${('0' + dateObj.getUTCDate()).slice(-2)}/${('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/${dateObj.getUTCFullYear()}`;
},
editor: {
type: Type.DATE
@@ -48,7 +48,7 @@ const columns = [{
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return \`$\{('0' + dateObj.getDate()).slice(-2)}/$\{('0' + (dateObj.getMonth() + 1)).slice(-2)}/$\{dateObj.getFullYear()}\`;
return \`$\{('0' + dateObj.getUTCDate()).slice(-2)}/$\{('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/$\{dateObj.getUTCFullYear()}\`;
},
editor: {
type: Type.DATE

View File

@@ -1,13 +1,14 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import filterFactory, { textFilter, dateFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
import { stockGenerator } from 'utils/common';
const products = productsGenerator(8);
const products = stockGenerator(8);
let nameFilter;
let priceFilter;
let stockDateFilter;
const columns = [{
dataField: 'id',
@@ -22,25 +23,36 @@ const columns = [{
})
}, {
dataField: 'price',
text: 'Product Price',
text: 'Price',
filter: textFilter({
getFilter: (filter) => {
priceFilter = filter;
}
})
}, {
dataField: 'inStockDate',
text: 'InStock Date',
formatter: cell => cell.toString(),
filter: dateFilter({
getFilter: (filter) => {
stockDateFilter = filter;
}
})
}];
const handleClick = () => {
nameFilter('');
priceFilter('');
stockDateFilter();
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import filterFactory, { textFilter, dateFilter } from 'react-bootstrap-table2-filter';
let nameFilter;
let priceFilter;
let stockDateFilter;
const columns = [{
dataField: 'id',
@@ -55,17 +67,27 @@ const columns = [{
})
}, {
dataField: 'price',
text: 'Product Price',
text: 'Price',
filter: textFilter({
getFilter: (filter) => {
priceFilter = filter;
}
})
}, {
dataField: 'inStockDate',
text: 'InStock Date',
formatter: cell => cell.toString(),
filter: dateFilter({
getFilter: (filter) => {
stockDateFilter = filter;
}
})
}];
const handleClick = () => {
nameFilter('');
priceFilter('');
stockDateFilter();
};
export default () => (

View File

@@ -0,0 +1,81 @@
/* eslint eqeqeq: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
class Table extends React.Component {
filterByPrice = filterVal =>
products.filter(product => product.price == filterVal);
render() {
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter({
onFilter: this.filterByPrice
})
}];
return (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
</div>
);
}
}
`;
export default class Table extends React.Component {
filterByPrice = filterVal =>
products.filter(product => product.price == filterVal);
render() {
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter({
onFilter: this.filterByPrice
})
}];
return (
<div>
<h2>Implement a eq filter on product price column</h2>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -0,0 +1,57 @@
/* eslint no-console: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter({
onFilter: filterVal => console.log(`Filter Value: ${filterVal}`)
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
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({
onFilter: filterVal => console.log(\`Filter Value: $\{filterVal}\`)
})
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,135 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columnsdt = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider from 'react-bootstrap-table2-toolkit';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const CustomToggleList = ({
columns,
onColumnToggle,
toggles
}) => (
<div className="btn-group btn-group-toggle btn-group-vertical" data-toggle="buttons">
{
columns
.map(column => ({
...column,
toggle: toggles[column.dataField]
}))
.map(column => (
<button
type="button"
key={ column.dataField }
className={ \`btn btn-warning \${column.toggle ? 'active' : ''}\` }
data-toggle="button"
aria-pressed={ column.toggle ? 'true' : 'false' }
onClick={ () => onColumnToggle(column.dataField) }
>
{ column.text }
</button>
))
}
</div>
);
<ToolkitProvider
keyField="id"
data={ products }
columns={ columnsdt }
columnToggle
>
{
props => (
<div>
<CustomToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
const CustomToggleList = ({
columns,
onColumnToggle,
toggles
}) => (
<div className="btn-group btn-group-toggle btn-group-vertical" data-toggle="buttons">
{
columns
.map(column => ({
...column,
toggle: toggles[column.dataField]
}))
.map(column => (
<button
type="button"
key={ column.dataField }
className={ `btn btn-warning ${column.toggle ? 'active' : ''}` }
data-toggle="button"
aria-pressed={ column.toggle ? 'true' : 'false' }
onClick={ () => onColumnToggle(column.dataField) }
>
{ column.text }
</button>
))
}
</div>
);
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columnsdt }
columnToggle
>
{
props => (
<div>
<CustomToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,81 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { ToggleList } = ColumnToggle;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
hidden: true
}, {
dataField: 'price',
text: 'Product Price',
hidden: true
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
const { ToggleList } = ColumnToggle;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
hidden: true
}, {
dataField: 'price',
text: 'Product Price',
hidden: true
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,81 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { ToggleList } = ColumnToggle;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
const { ToggleList } = ColumnToggle;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,91 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { ToggleList } = ColumnToggle;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit';
const { ToggleList } = ColumnToggle;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList
contextual="success"
className="list-custom-class"
btnClassName="list-btn-custom-class"
{ ...props.columnToggleProps }
/>
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList
contextual="success"
className="list-custom-class"
btnClassName="list-btn-custom-class"
{ ...props.columnToggleProps }
/>
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -11,141 +11,13 @@ const products = [
{ id: 14, name: 'Item 14', price: 14.5, inStock: true }
];
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = { products };
this.state = { products, count: 0 };
}
toggleInStock = () => {
@@ -163,17 +35,96 @@ class ProductList extends React.Component {
};
render() {
const columns = [
{
dataField: 'id',
text: 'Product ID',
formatter: (cell, row, rowIndex, extraData) => (
<div>
<span>ID: {row.id}</span>
<br />
<span>state: {extraData}</span>
</div>
),
formatExtraData: this.state.count
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
return (
<div>
<h1 className="h2">Products</h1>
<h3>Action 1 and Action 2 are dummy column</h3>
<button onClick={ this.toggleInStock } className="btn btn-primary">
Toggle item 13 stock status
</button>
<button
className="btn btn-success"
onClick={ () => this.setState(() => ({ count: this.state.count + 1 })) }
>
Click me to Increase counter
</button>
<BootstrapTable
keyField="id"
data={ this.state.products }
columns={ columns }
/>
<button onClick={ this.toggleInStock } className="btn btn-primary">
Toggle item 13 stock status
</button>
<Code>{ sourceCode }</Code>
</div>
);
}
@@ -183,7 +134,7 @@ class ProductList extends React.Component {
class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = { products };
this.state = { products, count: 0 };
}
toggleInStock = () => {
@@ -200,13 +151,95 @@ class ProductList extends React.Component {
this.setState(curr => ({ ...curr, products: newProducts }));
};
counter = () => {
this.setState(curr => ({ ...curr, count: this.state.count + 1 }));
}
render() {
const columns = [
{
dataField: 'id',
text: 'Product ID',
formatter: (cell, row, rowIndex, extraData) => (
<div>
<span>ID: {row.id}</span>
<br />
<span>Counter: {extraData}</span>
</div>
),
formatExtraData: this.state.count
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
},
{
dataField: 'inStock',
text: 'In Stock',
formatter: (cellContent, row) => (
<div className="checkbox disabled">
<label>
<input type="checkbox" checked={ row.inStock } disabled />
</label>
</div>
)
},
{
dataField: 'df1',
isDummyField: true,
text: 'Action 1',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
},
{
dataField: 'df2',
isDummyField: true,
text: 'Action 2',
formatter: (cellContent, row) => {
if (row.inStock) {
return (
<h5>
<span className="label label-success"> Available</span>
</h5>
);
}
return (
<h5>
<span className="label label-danger"> Backordered</span>
</h5>
);
}
}
];
return (
<div>
<h3>Action 1 and Action 2 are dummy column</h3>
<button onClick={ this.toggleInStock } className="btn btn-primary">
Toggle item 13 stock status
</button>
<button
className="btn btn-success"
onClick={ this.counter }
>
Click me to Increase counter
</button>
<BootstrapTable
keyField="id"
data={ this.state.products }

View File

@@ -0,0 +1,102 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport, Search } from 'react-bootstrap-table2-toolkit';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { SearchBar } = Search;
const { ExportCSVButton } = CSVExport;
const products = productsGenerator(150);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport, Search } from 'react-bootstrap-table2-toolkit';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
const { SearchBar } = Search;
const { ExportCSVButton } = CSVExport;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
clickToSelect: true
};
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV={ { onlyExportFiltered: true, exportAll: false } }
search
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<SearchBar { ...props.searchProps } />
<BootstrapTable
{ ...props.baseProps }
pagination={ paginationFactory() }
filter={ filterFactory() }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<h3>Export all the filtered/searched rows</h3>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV={ { onlyExportFiltered: true, exportAll: false } }
search
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<SearchBar { ...props.searchProps } />
<BootstrapTable
{ ...props.baseProps }
pagination={ paginationFactory() }
filter={ filterFactory() }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,151 @@
/* eslint react/no-multi-comp: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}];
const sourceCode1 = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
class Case1 extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);
}
`;
const sourceCode2 = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
class Case2 extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
pagination={ paginationFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);
}
`;
const products1 = productsGenerator(8);
class WithoutPaginationCase extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products1.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h3>Without Pagination Case</h3>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products1 }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode2 }</Code>
</div>
);
}
}
const products2 = productsGenerator(88);
class WithPaginationCase extends React.Component {
constructor(props) {
super(props);
this.state = { rowCount: products2.length };
}
handleDataChange = ({ dataSize }) => {
this.setState({ rowCount: dataSize });
}
render() {
return (
<div>
<h3>Without Pagination Case</h3>
<h5>Row Count:<span className="badge">{ this.state.rowCount }</span></h5>
<BootstrapTable
onDataSizeChange={ this.handleDataChange }
keyField="id"
data={ products2 }
columns={ columns }
filter={ filterFactory() }
pagination={ paginationFactory() }
/>
<Code>{ sourceCode1 }</Code>
</div>
);
}
}
export default () => (
<div>
<WithoutPaginationCase />
<WithPaginationCase />
</div>
);

View File

@@ -0,0 +1,70 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import { productsGenerator } from 'utils/common';
const ProductList = (props) => {
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name',
filter: textFilter({
defaultValue: '1'
})
},
{
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}
];
return (
<div style={ { paddingTop: '20px' } }>
<h1 className="h2">Products</h1>
<BootstrapTable
keyField="id"
data={ props.products }
columns={ columns }
filter={ filterFactory() }
/>
</div>
);
};
export default class DataContainer extends React.Component {
state = {
products: productsGenerator(3)
};
loadData = () => {
this.setState({
products: productsGenerator(14)
});
}
render() {
return (
<div>
<button
onClick={ this.loadData }
style={ {
fontSize: '20px',
position: 'absolute',
left: '200px',
top: '40px'
} }
>
Load Data
</button>
<ProductList products={ this.state.products } />
</div>
);
}
}

View File

@@ -0,0 +1,80 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import { productsGenerator } from 'utils/common';
const { SearchBar } = Search;
const ProductList = (props) => {
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
}
];
return (
<div style={ { paddingTop: '20px' } }>
<h1 className="h2">Products</h1>
<ToolkitProvider
keyField="id"
data={ props.products }
columns={ columns }
search={ { defaultSearch: '2101' } }
>
{
toolkitprops => (
<div>
<SearchBar { ...toolkitprops.searchProps } />
<BootstrapTable
striped
hover
{ ...toolkitprops.baseProps }
/>
</div>
)
}
</ToolkitProvider>
</div>
);
};
export default class DataContainer extends React.Component {
state = {
products: productsGenerator(3)
};
loadData = () => {
this.setState({
products: productsGenerator(14)
});
}
render() {
return (
<div>
<button
onClick={ this.loadData }
style={ {
fontSize: '20px',
position: 'absolute',
left: '200px',
top: '40px'
} }
>
Load Data
</button>
<ProductList products={ this.state.products } />
</div>
);
}
}

View File

@@ -0,0 +1,68 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import { productsGenerator } from 'utils/common';
const ProductList = (props) => {
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name',
filter: textFilter()
},
{
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}
];
return (
<div style={ { paddingTop: '20px' } }>
<h1 className="h2">Products</h1>
<BootstrapTable
keyField="id"
data={ props.products }
columns={ columns }
filter={ filterFactory() }
/>
</div>
);
};
export default class DataContainer extends React.Component {
state = {
products: []
};
loadData = () => {
this.setState({
products: productsGenerator()
});
}
render() {
return (
<div>
<button
onClick={ this.loadData }
style={ {
fontSize: '20px',
position: 'absolute',
left: '200px',
top: '40px'
} }
>
Load Data
</button>
<ProductList products={ this.state.products } />
</div>
);
}
}

View File

@@ -0,0 +1,72 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import paginationFactory from 'react-bootstrap-table2-paginator';
import { productsGenerator } from 'utils/common';
const ProductList = (props) => {
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name',
filter: textFilter({
defaultValue: '6'
})
},
{
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}
];
return (
<div style={ { paddingTop: '20px' } }>
<h1 className="h2">Products</h1>
<BootstrapTable
keyField="id"
data={ props.products }
columns={ columns }
filter={ filterFactory() }
pagination={ paginationFactory() }
/>
</div>
);
};
export default class DataContainer extends React.Component {
state = {
products: productsGenerator(60)
};
loadData = () => {
this.setState({
products: productsGenerator(14)
});
}
render() {
return (
<div>
<button
onClick={ this.loadData }
style={ {
fontSize: '20px',
position: 'absolute',
left: '200px',
top: '40px'
} }
>
Load Data
</button>
<ProductList products={ this.state.products } />
</div>
);
}
}

View File

@@ -0,0 +1,80 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import { productsGenerator } from 'utils/common';
const { SearchBar } = Search;
const ProductList = (props) => {
const columns = [
{
dataField: 'id',
text: 'Product ID'
},
{
dataField: 'name',
text: 'Product Name'
},
{
dataField: 'price',
text: 'Product Price'
}
];
return (
<div style={ { paddingTop: '20px' } }>
<h1 className="h2">Products</h1>
<ToolkitProvider
keyField="id"
data={ props.products }
columns={ columns }
search
>
{
toolkitprops => (
<div>
<SearchBar { ...toolkitprops.searchProps } />
<BootstrapTable
striped
hover
{ ...toolkitprops.baseProps }
/>
</div>
)
}
</ToolkitProvider>
</div>
);
};
export default class DataContainer extends React.Component {
state = {
products: []
};
loadData = () => {
this.setState({
products: productsGenerator()
});
}
render() {
return (
<div>
<button
onClick={ this.loadData }
style={ {
fontSize: '20px',
position: 'absolute',
left: '200px',
top: '40px'
} }
>
Load Data
</button>
<ProductList products={ this.state.products } />
</div>
);
}
}

View File

@@ -0,0 +1,53 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID',
footerAlign: 'center',
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footerAlign: (column, colIndex) => 'right',
footer: 'Footer 2'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
footerAlign: 'center',
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footerAlign: (column, colIndex) => 'right',
footer: 'Footer 2'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,54 @@
/* eslint no-unused-vars: 0 */
/* eslint no-alert: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1',
footerAttrs: { title: 'ID footer column' }
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2',
footerAttrs: (column, colIndex) => ({ 'data-test': `customized data ${colIndex}` })
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1',
footerAttrs: { title: 'ID footer column' }
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2',
footerAttrs: (column, colIndex) => ({ 'data-test': \`customized data \${colIndex}\` })
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,60 @@
/* eslint no-unused-vars: 0 */
/* eslint no-alert: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2',
footerClasses: 'demo-row-odd'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3',
footerClasses: (column, colIndex) => {
if (colIndex % 2 === 0) return 'demo-row-even';
return 'demo-row-odd';
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2',
footerClasses: 'demo-row-odd'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3',
footerClasses: (column, colIndex) => {
if (colIndex % 2 === 0) return 'demo-row-even';
return 'demo-row-odd';
}
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,56 @@
/* eslint no-unused-vars: 0 */
/* eslint no-alert: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID',
footerEvents: {
onClick: () => alert('Click on Product ID footer column')
},
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
footerEvents: {
onClick: () => alert('Click on Product ID footer column')
},
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,67 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
function priceFormatter(column, colIndex) {
return (
<h5>
<strong>$$ {column.text} $$</strong>
</h5>
);
}
const columns = [
{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
},
{
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2'
},
{
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3',
footerFormatter: priceFormatter
}
];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
function priceFormatter(column, colIndex) {
return (
<h5><strong>$$ { column.text } $$</strong></h5>
);
}
const columns = [
// omit...
{
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3',
footerFormatter: priceFormatter
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{sourceCode}</Code>
</div>
);

View File

@@ -0,0 +1,76 @@
/* eslint no-unused-vars: 0 */
/* eslint no-alert: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2',
footerStyle: {
backgroundColor: '#c8e6c9'
}
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3',
footerStyle: (column, colIndex) => {
if (colIndex % 2 === 0) {
return {
backgroundColor: '#81c784'
};
}
return {
backgroundColor: '#c8e6c9'
};
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2',
footerStyle: {
backgroundColor: '#c8e6c9'
}
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3',
footerStyle: (column, colIndex) => {
if (colIndex % 2 === 0) {
return {
backgroundColor: '#81c784'
};
}
return {
backgroundColor: '#c8e6c9'
};
}
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,53 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID',
footerTitle: true,
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footerTitle: (column, colIndex) => `this is custom title for ${column.text}`,
footer: 'Footer 2'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
footerTitle: true,
footer: 'Footer 1'
}, {
dataField: 'name',
text: 'Product Name',
footerTitle: (column, colIndex) => \`this is custom title for \${column.text}\`,
footer: 'Footer 2'
}, {
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,61 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [
{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
},
{
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2'
},
{
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}
];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [
{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
},
{
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2'
},
{
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}
];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
footerClasses="footer-class"
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } footerClasses="footer-class" />
<Code>{sourceCode}</Code>
</div>
);

View File

@@ -0,0 +1,52 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [
{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
},
{
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2'
},
{
dataField: 'price',
text: 'Product Price',
footer: columnData => columnData.reduce((acc, item) => acc + item, 0)
}
];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [
// omit...
{
dataField: 'price',
text: 'Product Price',
footer: columnData => columnData.reduce((acc, item) => acc + item, 0)
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{sourceCode}</Code>
</div>
);

View File

@@ -0,0 +1,52 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [
{
dataField: 'id',
text: 'Product ID',
footer: 'Footer 1'
},
{
dataField: 'name',
text: 'Product Name',
footer: 'Footer 2'
},
{
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}
];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [
// omit...
{
dataField: 'price',
text: 'Product Price',
footer: 'Footer 3'
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{sourceCode}</Code>
</div>
);

View File

@@ -0,0 +1,106 @@
/* eslint react/prefer-stateless-function: 0 */
/* eslint react/prop-types: 0 */
/* eslint jsx-a11y/href-no-hash: 0 */
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// ...
const pageButtonRenderer = ({
page,
active,
disable,
title,
onPageChange
}) => {
const handleClick = (e) => {
e.preventDefault();
onPageChange(page);
};
const activeStyle = {};
if (active) {
activeStyle.backgroundColor = 'black';
activeStyle.color = 'white';
} else {
activeStyle.backgroundColor = 'gray';
activeStyle.color = 'black';
}
if (typeof page === 'string') {
activeStyle.backgroundColor = 'white';
activeStyle.color = 'black';
}
return (
<li className="page-item">
<a href="#" onClick={ handleClick } style={ activeStyle }>{ page }</a>
</li>
);
};
const options = {
pageButtonRenderer
};
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />
`;
const pageButtonRenderer = ({
page,
active,
disable,
title,
onPageChange
}) => {
const handleClick = (e) => {
e.preventDefault();
onPageChange(page);
};
const activeStyle = {};
if (active) {
activeStyle.backgroundColor = 'black';
activeStyle.color = 'white';
} else {
activeStyle.backgroundColor = 'gray';
activeStyle.color = 'black';
}
if (typeof page === 'string') {
activeStyle.backgroundColor = 'white';
activeStyle.color = 'black';
}
return (
<li className="page-item">
<a href="#" onClick={ handleClick } style={ activeStyle }>{ page }</a>
</li>
);
};
const options = {
pageButtonRenderer
};
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,159 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(40);
const { SearchBar } = Search;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsGenerator(17) });
}
render() {
const options = {
custom: true,
paginationSize: 4,
pageStartIndex: 1,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
totalSize: this.state.products.length
};
const contentTable = ({ paginationProps, paginationTableProps }) => (
<div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } />
<ToolkitProvider
keyField="id"
columns={ columns }
data={ this.state.products }
search
>
{
toolkitprops => (
<div>
<SearchBar { ...toolkitprops.searchProps } />
<BootstrapTable
striped
hover
{ ...toolkitprops.baseProps }
{ ...paginationTableProps }
/>
</div>
)
}
</ToolkitProvider>
<PaginationListStandalone { ...paginationProps } />
</div>
);
return (
<div>
<h2>PaginationProvider will care the data size change. You dont do anything</h2>
<PaginationProvider
pagination={
paginationFactory(options)
}
>
{ contentTable }
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div >
);
}
}
`;
export default class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsGenerator(17) });
}
render() {
const options = {
custom: true,
paginationSize: 4,
pageStartIndex: 1,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
totalSize: this.state.products.length
};
const contentTable = ({ paginationProps, paginationTableProps }) => (
<div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } />
<ToolkitProvider
keyField="id"
columns={ columns }
data={ this.state.products }
search
>
{
toolkitprops => (
<div>
<SearchBar { ...toolkitprops.searchProps } />
<BootstrapTable
striped
hover
{ ...toolkitprops.baseProps }
{ ...paginationTableProps }
/>
</div>
)
}
</ToolkitProvider>
<PaginationListStandalone { ...paginationProps } />
</div>
);
return (
<div>
<h2>PaginationProvider will care the data size change. You dont do anything</h2>
<PaginationProvider
pagination={
paginationFactory(options)
}
>
{ contentTable }
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div >
);
}
}

View File

@@ -0,0 +1,78 @@
/* eslint react/prefer-stateless-function: 0 */
/* eslint react/prop-types: 0 */
/* eslint jsx-a11y/href-no-hash: 0 */
/* eslint jsx-a11y/no-noninteractive-element-interactions: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// ...
const pageListRenderer = ({
pages,
onPageChange
}) => {
const pageWithoutIndication = pages.filter(p => typeof p.page !== 'string');
return (
<div>
{
pageWithoutIndication.map(p => (
<button className="btn btn-success" onClick={ () => onPageChange(p.page) }>{ p.page }</button>
))
}
</div>
);
};
const options = {
pageListRenderer
};
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />
`;
const pageListRenderer = ({
pages,
onPageChange
}) => {
const pageWithoutIndication = pages.filter(p => typeof p.page !== 'string');
return (
<div>
{
pageWithoutIndication.map(p => (
<button className="btn btn-success" onClick={ () => onPageChange(p.page) }>{ p.page }</button>
))
}
</div>
);
};
const options = {
pageListRenderer
};
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,96 @@
/* eslint react/prop-types: 0 */
/* eslint jsx-a11y/href-no-hash: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// ...
const sizePerPageOptionRenderer = ({
text,
page,
onSizePerPageChange
}) => (
<li
key={ text }
role="presentation"
className="dropdown-item"
>
<a
href="#"
tabIndex="-1"
role="menuitem"
data-page={ page }
onMouseDown={ (e) => {
e.preventDefault();
onSizePerPageChange(page);
} }
style={ { color: 'red' } }
>
{ text }
</a>
</li>
);
const options = {
sizePerPageOptionRenderer
};
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />
`;
const sizePerPageOptionRenderer = ({
text,
page,
onSizePerPageChange
}) => (
<li
key={ text }
role="presentation"
className="dropdown-item"
>
<a
href="#"
tabIndex="-1"
role="menuitem"
data-page={ page }
onMouseDown={ (e) => {
e.preventDefault();
onSizePerPageChange(page);
} }
style={ { color: 'red' } }
>
{ text }
</a>
</li>
);
const options = {
sizePerPageOptionRenderer
};
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,89 @@
/* eslint react/prop-types: 0 */
/* eslint jsx-a11y/href-no-hash: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// ...
const sizePerPageRenderer = ({
options,
currSizePerPage,
onSizePerPageChange
}) => (
<div className="btn-group" role="group">
{
options.map((option) => {
const isSelect = currSizePerPage === \`$\{option.page}\`;
return (
<button
key={ option.text }
type="button"
onClick={ () => onSizePerPageChange(option.page) }
className={ \`btn $\{isSelect ? 'btn-secondary' : 'btn-warning'}\` }
>
{ option.text }
</button>
);
})
}
</div>
);
const options = {
sizePerPageRenderer
};
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />s
`;
const sizePerPageRenderer = ({
options,
currSizePerPage,
onSizePerPageChange
}) => (
<div className="btn-group" role="group">
{
options.map(option => (
<button
key={ option.text }
type="button"
onClick={ () => onSizePerPageChange(option.page) }
className={ `btn ${currSizePerPage === `${option.page}` ? 'btn-secondary' : 'btn-warning'}` }
>
{ option.text }
</button>
))
}
</div>
);
const options = {
sizePerPageRenderer
};
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } pagination={ paginationFactory(options) } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,181 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
import filterFactory, { textFilter, selectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(21);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: selectFilter({
options: selectOptions,
defaultValue: 0
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
import filterFactory, { textFilter, selectFilter } from 'react-bootstrap-table2-filter';
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: selectFilter({
options: selectOptions,
defaultValue: 0
})
}];
class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsQualityGenerator(40, 7) });
}
render() {
const options = {
custom: true,
paginationSize: 4,
pageStartIndex: 1,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
totalSize: this.state.products.length
};
const contentTable = ({ paginationProps, paginationTableProps }) => (
<div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } />
<div>
<div>
<BootstrapTable
striped
hover
keyField="id"
data={ this.state.products }
columns={ columns }
filter={ filterFactory() }
{ ...paginationTableProps }
/>
</div>
</div>
<PaginationListStandalone { ...paginationProps } />
</div>
);
return (
<div>
<h2>PaginationProvider will care the data size change. You dont do anything</h2>
<PaginationProvider
pagination={
paginationFactory(options)
}
>
{ contentTable }
</PaginationProvider>
</div >
);
}
}
`;
export default class Table extends React.Component {
state = { products }
loadData = () => {
this.setState({ products: productsQualityGenerator(40, 7) });
}
render() {
const options = {
custom: true,
paginationSize: 4,
pageStartIndex: 1,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
totalSize: this.state.products.length
};
const contentTable = ({ paginationProps, paginationTableProps }) => (
<div>
<button className="btn btn-default" onClick={ this.loadData }>Load Another Data</button>
<PaginationListStandalone { ...paginationProps } />
<div>
<div>
<BootstrapTable
striped
hover
keyField="id"
data={ this.state.products }
columns={ columns }
filter={ filterFactory() }
{ ...paginationTableProps }
/>
</div>
</div>
<PaginationListStandalone { ...paginationProps } />
</div>
);
return (
<div>
<h2>PaginationProvider will care the data size change. You dont do anything</h2>
<PaginationProvider
pagination={
paginationFactory(options)
}
>
{ contentTable }
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div >
);
}
}

View File

@@ -0,0 +1,166 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider } from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const options = {
custom: true,
totalSize: products.length
};
class FullyCustomPagination extends React.Component {
handleNextPage = ({
page,
onPageChange
}) => () => {
onPageChange(page + 1);
}
handlePrevPage = ({
page,
onPageChange
}) => () => {
onPageChange(page - 1);
}
handleSizePerPage = ({
page,
onSizePerPageChange
}, newSizePerPage) => {
onSizePerPageChange(newSizePerPage, page);
}
render() {
return (
<div>
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<div>
<p>Current Page: { paginationProps.page }</p>
<p>Current SizePerPage: { paginationProps.sizePerPage }</p>
</div>
<div className="btn-group" role="group">
<button className="btn btn-primary" onClick={ this.handleNextPage(paginationProps) }>Next Page</button>
<button className="btn btn-success" onClick={ this.handlePrevPage(paginationProps) }>Prev Page</button>
<button className="btn btn-danger" onClick={ () => this.handleSizePerPage(paginationProps, 10) }>Size Per Page: 10</button>
<button className="btn btn-warning" onClick={ () => this.handleSizePerPage(paginationProps, 25) }>Size Per Page: 25</button>
</div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div>
);
}
}
`;
const options = {
custom: true,
totalSize: products.length
};
export default class FullyCustomPagination extends React.Component {
handleNextPage = ({
page,
onPageChange
}) => () => {
onPageChange(page + 1);
}
handlePrevPage = ({
page,
onPageChange
}) => () => {
onPageChange(page - 1);
}
handleSizePerPage = ({
page,
onSizePerPageChange
}, newSizePerPage) => {
onSizePerPageChange(newSizePerPage, page);
}
render() {
return (
<div>
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<div>
<p>Current Page: { paginationProps.page }</p>
<p>Current SizePerPage: { paginationProps.sizePerPage }</p>
</div>
<div className="btn-group" role="group">
<button className="btn btn-primary" onClick={ this.handleNextPage(paginationProps) }>Next Page</button>
<button className="btn btn-success" onClick={ this.handlePrevPage(paginationProps) }>Prev Page</button>
<button className="btn btn-danger" onClick={ () => this.handleSizePerPage(paginationProps, 10) }>Size Per Page: 10</button>
<button className="btn btn-warning" onClick={ () => this.handleSizePerPage(paginationProps, 25) }>Size Per Page: 25</button>
</div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -0,0 +1,150 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
import Code from 'components/common/code-block';
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
class BookList extends React.Component {
state = {
books: [
{ id: '1', name: 'Book 1' },
{ id: '2', name: 'Book 2' },
{ id: '3', name: 'Book 3' },
{ id: '4', name: 'Book 4' },
{ id: '5', name: 'Book 5' },
{ id: '6', name: 'Book 6' }
]
};
deleteBookWithId = () => {
const lastOneId = this.state.books.length;
const updatedBooks = this.state.books.filter(m => m.id !== lastOneId.toString());
this.setState({ books: updatedBooks });
};
addBook = () => {
const lastOneId = this.state.books.length + 1;
this.setState({ books: [...this.state.books, {
id: \`$\{lastOneId}\`, name: \`Book $\{lastOneId}\`
}] });
}
render() {
const options = {
// pageStartIndex: 0,
sizePerPage: 5,
hideSizePerPage: true,
hidePageListOnlyOnePage: true
};
const columns = [
{
dataField: 'id',
text: 'Product ID',
Cell: row => (
<div>
<span title={ row.value }>{ row.value }</span>
</div>
)
},
{
dataField: 'name',
text: 'Product Name'
}
];
return (
<React.Fragment>
<BootstrapTable
keyField="id"
data={ this.state.books }
columns={ columns }
pagination={ paginationFactory(options) }
/>
<button className="btn btn-default" onClick={ () => this.deleteBookWithId() }>
delete last one book
</button>
<button className="btn btn-default" onClick={ () => this.addBook() }>
Add a book to the end
</button>
<Code>{ sourceCode }</Code>
</React.Fragment>
);
}
`;
export default class BookList extends React.Component {
state = {
books: [
{ id: '1', name: 'Book 1' },
{ id: '2', name: 'Book 2' },
{ id: '3', name: 'Book 3' },
{ id: '4', name: 'Book 4' },
{ id: '5', name: 'Book 5' },
{ id: '6', name: 'Book 6' },
{ id: '7', name: 'Book 6' },
{ id: '8', name: 'Book 6' },
{ id: '9', name: 'Book 6' },
{ id: '10', name: 'Book 6' },
{ id: '11', name: 'Book 6' }
]
};
deleteBookWithId = () => {
const lastOneId = this.state.books.length;
const updatedBooks = this.state.books.filter(m => m.id !== lastOneId.toString());
this.setState({ books: updatedBooks });
};
addBook = () => {
const lastOneId = this.state.books.length + 1;
this.setState({ books: [...this.state.books, {
id: `${lastOneId}`, name: `Book ${lastOneId}`
}] });
}
render() {
const options = {
// pageStartIndex: 0,
sizePerPage: 5,
hideSizePerPage: true,
hidePageListOnlyOnePage: true
};
const columns = [
{
dataField: 'id',
text: 'Product ID',
Cell: row => (
<div>
<span title={ row.value }>{ row.value }</span>
</div>
)
},
{
dataField: 'name',
text: 'Product Name'
}
];
return (
<React.Fragment>
<BootstrapTable
keyField="id"
data={ this.state.books }
columns={ columns }
pagination={ paginationFactory(options) }
/>
<button className="btn btn-default" onClick={ () => this.deleteBookWithId() }>
delete last one book
</button>
<button className="btn btn-default" onClick={ () => this.addBook() }>
Add a book to the end
</button>
<Code>{ sourceCode }</Code>
</React.Fragment>
);
}
}

View File

@@ -0,0 +1,190 @@
/* eslint react/no-multi-comp: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
// ...
const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div>
<PaginationProvider
pagination={
paginationFactory({
custom: true,
page,
sizePerPage,
totalSize
})
}
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<div>
<p>Current Page: { paginationProps.page }</p>
<p>Current SizePerPage: { paginationProps.sizePerPage }</p>
</div>
<div>
<PaginationListStandalone
{ ...paginationProps }
/>
</div>
<BootstrapTable
remote
keyField="id"
data={ data }
columns={ columns }
onTableChange={ onTableChange }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
</div>
);
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1,
data: products.slice(0, 10),
sizePerPage: 10
};
}
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({
page,
data: products.slice(currentIndex, currentIndex + sizePerPage),
sizePerPage
}));
}, 2000);
}
render() {
const { data, sizePerPage, page } = this.state;
return (
<RemotePagination
data={ data }
page={ page }
sizePerPage={ sizePerPage }
totalSize={ products.length }
onTableChange={ this.handleTableChange }
/>
);
}
}
`;
const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div>
<PaginationProvider
pagination={
paginationFactory({
custom: true,
page,
sizePerPage,
totalSize
})
}
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<div>
<p>Current Page: { paginationProps.page }</p>
<p>Current SizePerPage: { paginationProps.sizePerPage }</p>
</div>
<div>
<PaginationListStandalone
{ ...paginationProps }
/>
</div>
<BootstrapTable
remote
keyField="id"
data={ data }
columns={ columns }
onTableChange={ onTableChange }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div>
);
RemotePagination.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),
sizePerPage: 10
};
}
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({
page,
data: products.slice(currentIndex, currentIndex + sizePerPage),
sizePerPage
}));
}, 2000);
}
render() {
const { data, sizePerPage, page } = this.state;
return (
<RemotePagination
data={ data }
page={ page }
sizePerPage={ sizePerPage }
totalSize={ products.length }
onTableChange={ this.handleTableChange }
/>
);
}
}
export default Container;

View File

@@ -0,0 +1,102 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const options = {
custom: true,
totalSize: products.length
};
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<PaginationListStandalone
{ ...paginationProps }
/>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
`;
const options = {
custom: true,
totalSize: products.length
};
// const pagination = paginationFactory(options);
export default class StandalonePaginationList extends React.Component {
render() {
return (
<div>
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<PaginationListStandalone
{ ...paginationProps }
/>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -0,0 +1,107 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationTotalStandalone, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationTotalStandalone, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const options = {
custom: true,
totalSize: products.length
};
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<PaginationTotalStandalone
{ ...paginationProps }
/>
<PaginationListStandalone
{ ...paginationProps }
/>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
`;
const options = {
custom: true,
totalSize: products.length
};
export default class StandalonePaginationList extends React.Component {
render() {
return (
<div>
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<PaginationTotalStandalone
{ ...paginationProps }
/>
<PaginationListStandalone
{ ...paginationProps }
/>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -0,0 +1,103 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, SizePerPageDropdownStandalone } from 'react-bootstrap-table2-paginator';
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'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, SizePerPageDropdownStandalone } from 'react-bootstrap-table2-paginator';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const options = {
custom: true,
totalSize: products.length
};
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<SizePerPageDropdownStandalone
{ ...paginationProps }
/>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
`;
const options = {
custom: true,
totalSize: products.length
};
// const pagination = paginationFactory(options);
export default class StandaloneSizePerPage extends React.Component {
render() {
return (
<div>
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<SizePerPageDropdownStandalone
{ ...paginationProps }
btnContextual="btn btn-primary"
/>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -1,4 +1,5 @@
/* eslint react/prop-types: 0 */
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
@@ -33,7 +34,7 @@ const expandRow = {
}
return <b>+</b>;
},
expandColumnRenderer: ({ expanded }) => {
expandColumnRenderer: ({ expanded, rowKey, expandable }) => {
if (expanded) {
return (
<b>-</b>

View File

@@ -0,0 +1,76 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsExpandRowsGenerator } from 'utils/common';
const products = productsExpandRowsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const expandRow = {
renderer: row => (
<div>
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true,
expandColumnPosition: 'right'
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const expandRow = {
renderer: row => (
<div>
<p>{ \`This Expand row is belong to rowKey $\{row.id}\` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true,
expandColumnPosition: 'right'
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -25,6 +25,7 @@ const expandRow = {
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true,
nonExpandable: [1, 3]
};
@@ -50,6 +51,7 @@ const expandRow = {
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true,
nonExpandable: [1, 3]
};

View File

@@ -0,0 +1,57 @@
/* eslint no-unused-vars: 0 */
/* eslint no-console: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const rowEvents = {
onClick: (e, row, rowIndex) => {
console.log(`clicked on row with index: ${rowIndex}`);
},
onMouseEnter: (e, row, rowIndex) => {
console.log(`enter on row with index: ${rowIndex}`);
}
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const hiddenRowKeys = [1, 3];
<BootstrapTable keyField="id" data={ products } columns={ columns } hiddenRows={ hiddenRowKeys } />
`;
const hiddenRowKeys = [1, 3];
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } hiddenRows={ hiddenRowKeys } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,86 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { SearchBar, ClearSearchButton } = Search;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
const { SearchBar, ClearSearchButton } = Search;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<ClearSearchButton { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<ClearSearchButton { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-example",
"version": "1.0.11",
"version": "1.0.24",
"description": "",
"main": "index.js",
"private": true,

View File

@@ -29,10 +29,10 @@ export const withOnSale = rows => rows.map((row) => {
return row;
});
export const productsQualityGenerator = (quantity = 5) =>
export const productsQualityGenerator = (quantity = 5, factor = 0) =>
Array.from({ length: quantity }, (value, index) => ({
id: index,
name: `Item name ${index}`,
id: index + factor,
name: `Item name ${index + factor}`,
quality: index % 3
}));
@@ -70,6 +70,7 @@ export const stockGenerator = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({
id: index,
name: `Todo item ${index}`,
price: Math.floor((Math.random() * 2) + 1),
inStockDate:
new Date(startDate.getTime() + Math.random() * (endDate.getTime() - startDate.getTime()))
}));

View File

@@ -20,6 +20,8 @@ import TabIndexCellTable from 'examples/basic/tabindex-column';
import Bootstrap4DefaultSortTable from 'examples/bootstrap4/sort';
import Bootstrap4RowSelectionTable from 'examples/bootstrap4/row-selection';
import Bootstrap4PaginationTable from 'examples/bootstrap4/pagination';
import Bootstrap4ColumnToggleTable from 'examples/bootstrap4/column-toggle';
import ToolkitsTable from 'examples/bootstrap4/toolkits';
// work on columns
import NestedDataTable from 'examples/columns/nested-data-table';
@@ -45,6 +47,18 @@ import HeaderColumnStyleTable from 'examples/header-columns/column-style-table';
import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
import HeaderClassTable from 'examples/header-columns/header-class-table';
// footer
import SimpleFooter from 'examples/footer/simple-footer';
import FunctionFooter from 'examples/footer/function-footer';
import FooterClassTable from 'examples/footer/footer-class-table';
import FooterColumnFormatTable from 'examples/footer/column-format-table';
import FooterColumnAlignTable from 'examples/footer/column-align-table';
import FooterColumnTitleTable from 'examples/footer/column-title-table.js';
import FooterColumnEventsTable from 'examples/footer/column-event-table.js';
import FooterColumnClassTable from 'examples/footer/column-class-table.js';
import FooterColumnStyleTable from 'examples/footer/column-style-table.js';
import FooterColumnAttrsTable from 'examples/footer/column-attrs-table.js';
// column filter
import TextFilter from 'examples/column-filter/text-filter';
import TextFilterWithDefaultValue from 'examples/column-filter/text-filter-default-value';
@@ -74,11 +88,14 @@ import ProgrammaticallyMultiSelectFilter from 'examples/column-filter/programmat
import CustomFilter from 'examples/column-filter/custom-filter';
import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter';
import ClearAllFilters from 'examples/column-filter/clear-all-filters';
import FilterHooks from 'examples/column-filter/filter-hooks';
import CustomFilterLogic from 'examples/column-filter/custom-filter-logic';
// work on rows
import RowStyleTable from 'examples/rows/row-style';
import RowClassTable from 'examples/rows/row-class';
import RowEventTable from 'examples/rows/row-event';
import RowHiddenTable from 'examples/rows/row-hidden';
// table sort
import EnableSortTable from 'examples/sort/enable-sort-table';
@@ -140,15 +157,29 @@ import ExpandColumn from 'examples/row-expand/expand-column';
import OnlyExpandByColumn from 'examples/row-expand/expand-by-column-only.js';
import ExpandOnlyOne from 'examples/row-expand/expand-only-one';
import CustomExpandColumn from 'examples/row-expand/custom-expand-column';
import ExpandColumnPosition from 'examples/row-expand/expand-column-position';
import ExpandHooks from 'examples/row-expand/expand-hooks';
// pagination
import PaginationTable from 'examples/pagination';
import PaginationHooksTable from 'examples/pagination/pagination-hooks';
import PaginationWithDynamicData from 'examples/pagination/pagination-with-dynamic-data';
import CustomPaginationTable from 'examples/pagination/custom-pagination';
import CustomPageButtonTable from 'examples/pagination/custom-page-button';
import CustomSizePerPageOptionTable from 'examples/pagination/custom-size-per-page-option';
import CustomSizePerPageTable from 'examples/pagination/custom-size-per-page';
import CustomPageListTable from 'examples/pagination/custom-page-list';
import StandalonePaginationList from 'examples/pagination/standalone-pagination-list';
import StandaloneSizePerPage from 'examples/pagination/standalone-size-per-page';
import StandalonePaginationTotal from 'examples/pagination/standalone-pagination-total';
import FullyCustomPaginationTable from 'examples/pagination/fully-custom-pagination';
import RemoteStandalonePaginationTable from 'examples/pagination/remote-standalone-pagination';
import CustomePaginationWithFilter from 'examples/pagination/custome-page-list-with-filter';
import CustomePaginationWithSearch from 'examples/pagination/custom-page-list-with-search';
// search
import SearchTable from 'examples/search';
import ClearSearchButton from 'examples/search/clear-search-button';
import DefaultSearch from 'examples/search/default-search';
import DefaultCustomSearch from 'examples/search/default-custom-search';
import FullyCustomSearch from 'examples/search/fully-custom-search';
@@ -161,11 +192,18 @@ import CSVFormatter from 'examples/csv/csv-column-formatter';
import CustomCSVHeader from 'examples/csv/custom-csv-header';
import HideCSVColumn from 'examples/csv/hide-column';
import ExportOnlySelected from 'examples/csv/export-only-selected';
import ExportOnlyFiltered from 'examples/csv/export-only-filtered';
import CSVColumnType from 'examples/csv/csv-column-type';
import CustomCSVButton from 'examples/csv/custom-csv-button';
import ExportCustomData from 'examples/csv/export-custom-data';
import CustomCSV from 'examples/csv/custom-csv';
// Column toggle
import BasicColumnToggle from 'examples/column-toggle';
import DefaultVisibility from 'examples/column-toggle/default-visibility';
import StylingColumnToggle from 'examples/column-toggle/styling-toggle-list';
import CustomToggleList from 'examples/column-toggle/custom-toggle-list';
// loading overlay
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
import TableOverlay from 'examples/loading-overlay/table-overlay';
@@ -178,18 +216,26 @@ import RemoteSearch from 'examples/remote/remote-search';
import RemoteCellEdit from 'examples/remote/remote-celledit';
import RemoteAll from 'examples/remote/remote-all';
// data
import DataChangeListener from 'examples/data/data-change-listener';
import LoadDataWithFilter from 'examples/data/load-data-on-the-fly-with-filter';
import LoadDataWithDefaultFilter from 'examples/data/load-data-on-the-fly-with-default-filter';
import LoadDataWithSearch from 'examples/data/load-data-on-the-fly-with-search';
import LoadDataWithDefaultSearch from 'examples/data/load-data-on-the-fly-with-default-search';
import LoadDataWithPaginationAndFilter from 'examples/data/load-data-on-the-fly-with-pagination-and-filter';
// css style
import 'stories/stylesheet/tomorrow.min.css';
import 'stories/stylesheet/storybook.scss';
import '../../react-bootstrap-table2/style/react-bootstrap-table2.scss';
import '../../react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss';
import '../../react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss';
import '../../react-bootstrap-table2-toolkit/style/react-bootstrap-table2-toolkit.scss';
// import bootstrap style by given version
import bootstrapStyle, { BOOTSTRAP_VERSION } from './bootstrap-style';
storiesOf('Welcome', module)
.add('react bootstrap table 2 ', () => <Welcome />);
storiesOf('Welcome', module).add('react bootstrap table 2 ', () => <Welcome />);
storiesOf('Basic Table', module)
.addDecorator(bootstrapStyle())
@@ -207,7 +253,9 @@ storiesOf('Bootstrap 4', module)
.addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR))
.add('Sort table with bootstrap 4', () => <Bootstrap4DefaultSortTable />)
.add('Row selection table with bootstrap 4', () => <Bootstrap4RowSelectionTable />)
.add('Pagination table with bootstrap 4', () => <Bootstrap4PaginationTable />);
.add('Pagination table with bootstrap 4', () => <Bootstrap4PaginationTable />)
.add('Column Toggle with bootstrap 4', () => <Bootstrap4ColumnToggleTable />)
.add('toolkits Table bootstrap 4', () => <ToolkitsTable />);
storiesOf('Work on Columns', module)
.addDecorator(bootstrapStyle())
@@ -265,14 +313,30 @@ storiesOf('Column Filter', module)
.add('Custom Filter', () => <CustomFilter />)
.add('Advance Custom Filter', () => <AdvanceCustomFilter />)
.add('Preserved Option Order on Select Filter', () => <SelectFilterWithPreservedOptionsOrder />)
.add('Clear All Filters', () => <ClearAllFilters />);
.add('Clear All Filters', () => <ClearAllFilters />)
.add('Filter Hooks', () => <FilterHooks />)
.add('Implement custom filter logic', () => <CustomFilterLogic />);
storiesOf('Work on Rows', module)
.addDecorator(bootstrapStyle())
.add('Customize Row Style', () => <RowStyleTable />)
.add('Customize Row Class', () => <RowClassTable />)
.add('Hide Rows', () => <RowHiddenTable />)
.add('Row Event', () => <RowEventTable />);
storiesOf('Footer', module)
.addDecorator(bootstrapStyle())
.add('Simple Footer', () => <SimpleFooter />)
.add('Function Footer', () => <FunctionFooter />)
.add('Column Formatter', () => <FooterColumnFormatTable />)
.add('Column Align', () => <FooterColumnAlignTable />)
.add('Column Title', () => <FooterColumnTitleTable />)
.add('Column Events', () => <FooterColumnEventsTable />)
.add('Customize Column Class', () => <FooterColumnClassTable />)
.add('Customize Column Style', () => <FooterColumnStyleTable />)
.add('Customize Column HTML attribute', () => <FooterColumnAttrsTable />)
.add('Footer Class', () => <FooterClassTable />);
storiesOf('Sort Table', module)
.addDecorator(bootstrapStyle())
.add('Enable Sort', () => <EnableSortTable />)
@@ -337,23 +401,44 @@ storiesOf('Row Expand', module)
.add('Only Expand by Indicator', () => <OnlyExpandByColumn />)
.add('Expand Only One Row at The Same Time', () => <ExpandOnlyOne />)
.add('Custom Expand Indicator', () => <CustomExpandColumn />)
.add('Expand Column Position', () => <ExpandColumnPosition />)
.add('Expand Hooks', () => <ExpandHooks />);
storiesOf('Pagination', module)
.addDecorator(bootstrapStyle())
.add('Basic Pagination Table', () => <PaginationTable />)
.add('Pagination Hooks', () => <PaginationHooksTable />)
.add('Custom Pagination', () => <CustomPaginationTable />);
.add('Pagination with Dynamic Data', () => <PaginationWithDynamicData />)
.add('Custom Pagination', () => <CustomPaginationTable />)
.add('Custom Page Button', () => <CustomPageButtonTable />)
.add('Custom Page List', () => <CustomPageListTable />)
.add('Custom SizePerPage Option', () => <CustomSizePerPageOptionTable />)
.add('Custom SizePerPage', () => <CustomSizePerPageTable />)
.add('Standalone Pagination List', () => <StandalonePaginationList />)
.add('Standalone SizePerPage Dropdown', () => <StandaloneSizePerPage />)
.add('Standalone Pagination Total', () => <StandalonePaginationTotal />)
.add('Fully Custom Pagination', () => <FullyCustomPaginationTable />)
.add('Remote Fully Custom Pagination', () => <RemoteStandalonePaginationTable />)
.add('Custom Pagination with Filter', () => <CustomePaginationWithFilter />)
.add('Custom Pagination with Search', () => <CustomePaginationWithSearch />);
storiesOf('Table Search', module)
.addDecorator(bootstrapStyle())
.add('Basic Search Table', () => <SearchTable />)
.add('Clear Search Button', () => <ClearSearchButton />)
.add('Default Search Table', () => <DefaultSearch />)
.add('Default Custom Search', () => <DefaultCustomSearch />)
.add('Fully Custom Search', () => <FullyCustomSearch />)
.add('Search Fromatted Value', () => <SearchFormattedData />)
.add('Custom Search Value', () => <CustomSearchValue />);
storiesOf('Column Toggle', module)
.addDecorator(bootstrapStyle())
.add('Basic Column Toggle', () => <BasicColumnToggle />)
.add('Default Visibility', () => <DefaultVisibility />)
.add('Styling Column Toggle', () => <StylingColumnToggle />)
.add('Custom Column Toggle', () => <CustomToggleList />);
storiesOf('Export CSV', module)
.addDecorator(bootstrapStyle())
.add('Basic Export CSV', () => <ExportCSV />)
@@ -361,6 +446,7 @@ storiesOf('Export CSV', module)
.add('Custom CSV Header', () => <CustomCSVHeader />)
.add('Hide CSV Column', () => <HideCSVColumn />)
.add('Only Export Selected Rows', () => <ExportOnlySelected />)
.add('Only Export Filtered/Searched Rows', () => <ExportOnlyFiltered />)
.add('CSV Column Type', () => <CSVColumnType />)
.add('Custom CSV Button', () => <CustomCSVButton />)
.add('Export Custom Data', () => <ExportCustomData />)
@@ -379,3 +465,12 @@ storiesOf('Remote', module)
.add('Remote Search', () => <RemoteSearch />)
.add('Remote Cell Editing', () => <RemoteCellEdit />)
.add('Remote All', () => <RemoteAll />);
storiesOf('Data', module)
.addDecorator(bootstrapStyle())
.add('Data Change Listener', () => <DataChangeListener />)
.add('Load data with Filter', () => <LoadDataWithFilter />)
.add('Load data with Default Filter', () => <LoadDataWithDefaultFilter />)
.add('Load data with Search', () => <LoadDataWithSearch />)
.add('Load data with Default Search', () => <LoadDataWithDefaultSearch />)
.add('Load data with Filter and Pagination', () => <LoadDataWithPaginationAndFilter />);

View File

@@ -14,3 +14,7 @@
.header-class {
background-color: $green-lighten-4;
}
.footer-class {
background-color: $green-lighten-4;
}

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-filter",
"version": "1.1.0",
"version": "1.1.9",
"description": "it's a column filter addon for react-bootstrap-table2",
"main": "./lib/index.js",
"repository": {

View File

@@ -18,7 +18,7 @@ const legalComparators = [
];
function dateParser(d) {
return `${d.getFullYear()}-${('0' + (d.getMonth() + 1)).slice(-2)}-${('0' + d.getDate()).slice(-2)}`;
return `${d.getUTCFullYear()}-${('0' + (d.getUTCMonth() + 1)).slice(-2)}-${('0' + d.getUTCDate()).slice(-2)}`;
}
class DateFilter extends Component {
@@ -42,10 +42,11 @@ class DateFilter extends Component {
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
this.dateFilterComparator.value = filterVal.comparator;
this.inputDate.value = dateParser(filterVal.date);
const nullableFilterVal = filterVal || { date: null, comparator: null };
this.dateFilterComparator.value = nullableFilterVal.comparator;
this.inputDate.value = nullableFilterVal.date ? dateParser(nullableFilterVal.date) : null;
this.applyFilter(filterVal.date, filterVal.comparator);
this.applyFilter(nullableFilterVal.date, nullableFilterVal.comparator);
});
}
}
@@ -131,8 +132,14 @@ class DateFilter extends Component {
className={ `filter date-filter ${className}` }
style={ style }
>
<label
className="filter-label"
htmlFor={ `date-filter-comparator-${text}` }
>
<span className="sr-only">Filter comparator</span>
<select
ref={ n => this.dateFilterComparator = n }
id={ `date-filter-comparator-${text}` }
style={ comparatorStyle }
className={ `date-filter-comparator form-control ${comparatorClassName}` }
onChange={ this.onChangeComparator }
@@ -140,8 +147,12 @@ class DateFilter extends Component {
>
{ this.getComparatorOptions() }
</select>
</label>
<label htmlFor={ `date-filter-column-${text}` }>
<span className="sr-only">Enter ${ text }</span>
<input
ref={ n => this.inputDate = n }
id={ `date-filter-column-${text}` }
className={ `filter date-filter-input form-control ${dateClassName}` }
style={ dateStyle }
type="date"
@@ -149,6 +160,7 @@ class DateFilter extends Component {
placeholder={ placeholder || `Enter ${text}...` }
defaultValue={ this.getDefaultDate() }
/>
</label>
</div>
);
}

View File

@@ -111,9 +111,15 @@ class MultiSelectFilter extends Component {
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return (
<label
className="filter-label"
htmlFor={ `multiselect-filter-column-${column.text}` }
>
<span className="sr-only">Filter by {column.text}</span>
<select
{ ...rest }
ref={ n => this.selectInput = n }
id={ `multiselect-filter-column-${column.text}` }
style={ style }
multiple
className={ selectClass }
@@ -123,6 +129,7 @@ class MultiSelectFilter extends Component {
>
{ this.getOptions() }
</select>
</label>
);
}
}

View File

@@ -173,28 +173,45 @@ class NumberFilter extends Component {
className={ `filter number-filter ${className}` }
style={ style }
>
<label
className="filter-label"
htmlFor={ `number-filter-comparator-${column.text}` }
>
<span className="sr-only">Filter comparator</span>
<select
ref={ n => this.numberFilterComparator = n }
style={ comparatorStyle }
id={ `number-filter-comparator-${column.text}` }
className={ `number-filter-comparator form-control ${comparatorClassName}` }
onChange={ this.onChangeComparator }
defaultValue={ defaultValue ? defaultValue.comparator : '' }
>
{ this.getComparatorOptions() }
</select>
</label>
{
options ?
<label
className="filter-label"
htmlFor={ `number-filter-column-${column.text}` }
>
<span className="sr-only">{`Select ${column.text}`}</span>
<select
ref={ n => this.numberFilter = n }
id={ `number-filter-column-${column.text}` }
style={ numberStyle }
className={ selectClass }
onChange={ this.onChangeNumberSet }
defaultValue={ defaultValue ? defaultValue.number : '' }
>
{ this.getNumberOptions() }
</select> :
</select>
</label> :
<label htmlFor={ `number-filter-column-${column.text}` }>
<span className="sr-only">{`Enter ${column.text}`}</span>
<input
ref={ n => this.numberFilter = n }
id={ `number-filter-column-${column.text}` }
type="number"
style={ numberStyle }
className={ `number-filter-input form-control ${numberClassName}` }
@@ -202,6 +219,7 @@ class NumberFilter extends Component {
onChange={ this.onChangeNumber }
defaultValue={ defaultValue ? defaultValue.number : '' }
/>
</label>
}
</div>
);

View File

@@ -8,6 +8,7 @@ import { FILTER_TYPE } from '../const';
function optionsEquals(currOpts, prevOpts) {
if (Array.isArray(currOpts)) {
if (currOpts.length === prevOpts.length) {
for (let i = 0; i < currOpts.length; i += 1) {
if (
currOpts[i].value !== prevOpts[i].value ||
@@ -16,7 +17,9 @@ function optionsEquals(currOpts, prevOpts) {
return false;
}
}
return currOpts.length === prevOpts.length;
return true;
}
return false;
}
const keys = Object.keys(currOpts);
for (let i = 0; i < keys.length; i += 1) {
@@ -136,9 +139,15 @@ class SelectFilter extends Component {
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return (
<label
className="filter-label"
htmlFor={ `select-filter-column-${column.text}` }
>
<span className="sr-only">Filter by { column.text }</span>
<select
{ ...rest }
ref={ n => this.selectInput = n }
id={ `select-filter-column-${column.text}` }
style={ style }
className={ selectClass }
onChange={ this.filter }
@@ -147,6 +156,7 @@ class SelectFilter extends Component {
>
{ this.getOptions() }
</select>
</label>
);
}
}

View File

@@ -94,10 +94,16 @@ class TextFilter extends Component {
// stopPropagation for onClick event is try to prevent sort was triggered.
return (
<label
className="filter-label"
htmlFor={ `text-filter-column-${text}` }
>
<span className="sr-only">Filter by {text}</span>
<input
{ ...rest }
ref={ n => this.input = n }
type="text"
id={ `text-filter-column-${text}` }
className={ `filter text-filter form-control ${className}` }
style={ style }
onChange={ this.filter }
@@ -105,6 +111,7 @@ class TextFilter extends Component {
placeholder={ placeholder || `Enter ${text}...` }
value={ this.state.value }
/>
</label>
);
}
}

View File

@@ -17,14 +17,18 @@ export default (
class FilterProvider extends React.Component {
static propTypes = {
data: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired
columns: PropTypes.array.isRequired,
dataChangeListener: PropTypes.object
}
constructor(props) {
super(props);
this.currFilters = {};
this.onFilter = this.onFilter.bind(this);
this.doFilter = this.doFilter.bind(this);
this.onExternalFilter = this.onExternalFilter.bind(this);
this.data = props.data;
this.isEmitDataChange = false;
}
componentDidMount() {
@@ -33,6 +37,15 @@ export default (
}
}
componentWillReceiveProps(nextProps) {
// let nextData = nextProps.data;
if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.data)) {
this.doFilter(nextProps, undefined, this.isEmitDataChange);
} else {
this.data = nextProps.data;
}
}
onFilter(column, filterType, initialize = false) {
return (filterVal) => {
// watch out here if migration to context API, #334
@@ -64,7 +77,11 @@ export default (
return;
}
this.forceUpdate();
let result;
if (filter.props.onFilter) {
result = filter.props.onFilter(filterVal);
}
this.doFilter(this.props, result);
};
}
@@ -74,14 +91,29 @@ export default (
};
}
render() {
let { data } = this.props;
if (!isRemoteFiltering()) {
data = filters(data, this.props.columns, _)(this.currFilters);
getFiltered() {
return this.data;
}
doFilter(props, customResult, ignoreEmitDataChange = false) {
let result = customResult;
const { dataChangeListener, data, columns } = props;
result = result || filters(data, columns, _)(this.currFilters);
this.data = result;
if (dataChangeListener && !ignoreEmitDataChange) {
this.isEmitDataChange = true;
dataChangeListener.emit('filterChanged', result.length);
} else {
this.isEmitDataChange = false;
this.forceUpdate();
}
}
render() {
return (
<FilterContext.Provider value={ {
data,
data: this.data,
onFilter: this.onFilter,
onExternalFilter: this.onExternalFilter
} }

View File

@@ -98,9 +98,9 @@ export const filterByDate = _ => (
customFilterValue
) => {
if (!date || !comparator) return data;
const filterDate = date.getDate();
const filterMonth = date.getMonth();
const filterYear = date.getFullYear();
const filterDate = date.getUTCDate();
const filterMonth = date.getUTCMonth();
const filterYear = date.getUTCFullYear();
return data.filter((row) => {
let valid = true;
@@ -114,9 +114,9 @@ export const filterByDate = _ => (
cell = new Date(cell);
}
const targetDate = cell.getDate();
const targetMonth = cell.getMonth();
const targetYear = cell.getFullYear();
const targetDate = cell.getUTCDate();
const targetMonth = cell.getUTCMonth();
const targetYear = cell.getUTCFullYear();
switch (comparator) {

View File

@@ -1,3 +1,7 @@
.react-bootstrap-table > table > thead > tr > th .filter-label {
display: block !important;
}
.react-bootstrap-table > table > thead > tr > th .filter {
font-weight: normal;
}

View File

@@ -44,7 +44,9 @@ describe('FilterContext', () => {
const handleFilterChange = jest.fn();
function shallowContext(
enableRemote = false
enableRemote = false,
tableColumns = columns,
dataChangeListener,
) {
mockBase.mockReset();
handleFilterChange.mockReset();
@@ -56,8 +58,9 @@ describe('FilterContext', () => {
return (
<FilterContext.Provider
columns={ columns }
columns={ tableColumns }
data={ data }
dataChangeListener={ dataChangeListener }
>
<FilterContext.Consumer>
{
@@ -225,6 +228,84 @@ describe('FilterContext', () => {
});
});
describe('if filter.props.onFilter is defined', () => {
const filterVal = '3';
const onFilter = jest.fn();
const customColumns = columns.map((column, i) => {
if (i === 1) {
return {
...column,
filter: textFilter({ onFilter })
};
}
return column;
});
beforeEach(() => {
wrapper = shallow(shallowContext(false, customColumns));
wrapper.render();
instance = wrapper.instance();
});
it('should call filter.props.onFilter correctly', () => {
instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal);
expect(onFilter).toHaveBeenCalledTimes(1);
expect(onFilter).toHaveBeenCalledWith(filterVal);
});
});
describe('if filter.props.onFilter is defined and return an undefined data', () => {
const mockReturn = [{
id: 1,
name: 'A'
}];
const filterVal = 'A';
const onFilter = jest.fn().mockReturnValue(mockReturn);
const customColumns = columns.map((column, i) => {
if (i === 1) {
return {
...column,
filter: textFilter({ onFilter })
};
}
return column;
});
beforeEach(() => {
wrapper = shallow(shallowContext(false, customColumns));
wrapper.render();
instance = wrapper.instance();
});
it('should call filter.props.onFilter correctly', () => {
instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal);
expect(onFilter).toHaveBeenCalledTimes(1);
expect(onFilter).toHaveBeenCalledWith(filterVal);
});
it('should set data correctly', () => {
instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal);
expect(instance.data).toEqual(mockReturn);
});
});
describe('when props.dataChangeListener is defined', () => {
const filterVal = '3';
const newDataLength = 0;
const dataChangeListener = { emit: jest.fn() };
beforeEach(() => {
wrapper = shallow(shallowContext(false, columns, dataChangeListener));
wrapper.render();
instance = wrapper.instance();
});
it('should call dataChangeListener.emit correctly', () => {
instance.onFilter(columns[1], FILTER_TYPE.TEXT)(filterVal);
expect(dataChangeListener.emit).toHaveBeenCalledWith('filterChanged', newDataLength);
});
});
describe('combination', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());

View File

@@ -37,4 +37,210 @@ import paginationFactory from 'react-bootstrap-table2-paginator';
## Customization
See [pagination props](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html)
### Basic Customization
`react-bootstrap-table2` give some simple ways to customize something like text, styling etc, following is all the props we support for basic customization:
* [paginationSize](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationpaginationsize-number)
* [sizePerPageList](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationsizeperpagelist-array)
* [withFirstAndLast](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationwithfirstandlast-bool)
* [alwaysShowAllBtns](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationalwaysshowallbtns-bool)
* [firstPageText](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationfirstpagetext-any)
* [prePageText](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationprepagetext-any)
* [nextPageText](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationnextpagetext-any)
* [lastPageText](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationlastpagetext-any)
* [firstPageTitle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationfirstpagetitle-any)
* [prePageTitle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationprepagetitle-any)
* [nextPageTitle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationnextpagetitle-any)
* [lastPageTitle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationlastpagetitle-any)
* [hideSizePerPage](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationhidesizeperpage-bool)
* [hidePageListOnlyOnePage](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationhidepagelistonlyonepage-bool)
* [showTotal](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationshowtotal-bool)
You can check [this online demo](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html?selectedKind=Pagination&selectedStory=Custom%20Pagination&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybook%2Factions%2Factions-panel) for above props usage.
### Advance Customization
Sometime, you may feel above props is not satisfied with your requirement, don't worry, we provide following renderer for each part of pagination:
* [pageListRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationpagelistrenderer-function)
* [pageButtonRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationpagebuttonrenderer-function)
* [sizePerPageRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationsizeperpagerenderer-function)
* [sizePerPageOptionRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationsizeperpageoptionrenderer-function)
* [paginationTotalRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html#paginationpaginationtotalrenderer-function)
### Fully Customization
If you want to customize the pagination component completely, you may get interesting on following solutions:
* Standalone
* Non-standalone
`react-bootstrap-table2-paginator` have a `PaginationProvider` which is a react context and that will be easier to customize the pagination components under the scope of `PaginationProvider`. Let's introduce it step by step:
#### 1. Import PaginationProvider
```js
import paginationFactory, { PaginationProvider } from 'react-bootstrap-table2-paginator';
```
#### 2. Declare custom and totalSize in pagination option:
```js
const paginationOption = {
custom: true,
totalSize: products.length
};
```
#### 3. Render PaginationProvider
```js
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
.....
)
}
</PaginationProvider>
```
`PaginationProvider` actually is a wrapper for the concumser of react context, so that now you have to get the props from context provide then render to your compoennt and `BootstrapTable`:
* `paginationProps`: this include everything about pagination, you will use it when you render standalone component or your custom component.
* `paginationTableProps`: you don't need to know about this, but you have to render this as props to `BootstrapTable`.
So far, your customization pagination is supposed to look like it:
```js
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
```
Now, you have to choose which solution you like: standalone or non-standalone ?
#### 4.1 Use Standalone Component
`react-bootstrap-table2-paginator` provider three standalone components:
* Size Per Page Dropdwn Standalone
* Pagination List Standalone
* Pagination Total Standalone
When render each standalone, you just need to pass the `paginationProps` props to standalone component:
```js
import paginationFactory, {
PaginationProvider,
PaginationListStandalone,
SizePerPageDropdownStandalone,
PaginationTotalStandalone
} from 'react-bootstrap-table2-paginator';
<PaginationProvider
pagination={ paginationFactory(options) }
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<SizePerPageDropdownStandalone
{ ...paginationProps }
/>
<PaginationTotalStandalone
{ ...paginationProps }
/>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
{ ...paginationTableProps }
/>
<PaginationListStandalone
{ ...paginationProps }
/>
</div>
)
}
</PaginationProvider>
```
That's it!! The benifit for using standalone is you can much easier to render the standalone component in any posistion. In the future, we will implement more featue like applying `style`, `className` etc on standalone components.
##### Customizable props for `PaginationListStandalone`
* N/A
##### Customizable props for `SizePerPageDropdownStandalone`
* `open`: `true` to make dropdown show.
* `hidden`: `true` to hide the size per page dropdown.
* `btnContextual`: Set the button contextual
* `variation`: Variation for dropdown, available value is `dropdown` and `dropup`.
* `className`: Custom the class on size per page dropdown
##### Customizable props for `SizePerPageDropdownStandalone`
* N/A
#### 4.2 Customization Everything
If you choose to custom the pagination component by yourself, the `paginationProps` will be important for you. Becasue you have to know for example how to change page or what's the current page etc. Hence, following is all the props in `paginationProps` object:
```js
page,
sizePerPage,
pageStartIndex,
hidePageListOnlyOnePage,
hideSizePerPage,
alwaysShowAllBtns,
withFirstAndLast,
dataSize,
sizePerPageList,
paginationSize,
showTotal,
pageListRenderer,
pageButtonRenderer,
sizePerPageRenderer,
paginationTotalRenderer,
sizePerPageOptionRenderer,
firstPageText,
prePageText,
nextPageText,
lastPageText,
prePageTitle,
nextPageTitle,
firstPageTitle,
lastPageTitle,
onPageChange,
onSizePerPageChange
```
In most of case, `page`, `sizePerPage`, `onPageChange` and `onSizePerPageChange` are most important things for developer.
* `page`: Current page.
* `sizePerPage`: Current size per page.
* `onPageChange`: Call it when you nede to change page. This function accept one number argument which indicate the new page
* `onSizePerPageChange`: Call it when you nede to change size per page. This function accept two number argument which indicate the new sizePerPage and new page
[Here](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html?selectedKind=Pagination&selectedStory=Fully%20Custom%20Pagination&full=0&addons=1&stories=1&panelRight=0&addonPanel=storybook%2Factions%2Factions-panel) is a online example.

View File

@@ -1,6 +1,27 @@
import createContext from './src/context';
import React from 'react';
import PropTypes from 'prop-types';
import createBaseContext from './src/state-context';
import createDataContext from './src/data-context';
import PaginationListStandalone from './src/pagination-list-standalone';
import SizePerPageDropdownStandalone from './src/size-per-page-dropdown-standalone';
import PaginationTotalStandalone from './src/pagination-total-standalone';
export default (options = {}) => ({
createContext,
createContext: createDataContext,
options
});
const { Provider, Consumer } = createBaseContext();
const CustomizableProvider = props => (
<Provider { ...props }>
<Consumer>{ paginationProps => props.children(paginationProps) }</Consumer>
</Provider>
);
CustomizableProvider.propTypes = {
children: PropTypes.func.isRequired
};
export const PaginationProvider = CustomizableProvider;
export { PaginationListStandalone, SizePerPageDropdownStandalone, PaginationTotalStandalone };

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-paginator",
"version": "1.0.4",
"version": "2.0.6",
"description": "it's the pagination addon for react-bootstrap-table2",
"main": "./lib/index.js",
"repository": {

View File

@@ -1,182 +0,0 @@
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
/* eslint no-lonely-if: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import Const from './const';
import { BootstrapContext } from './bootstrap';
import Pagination from './pagination';
import { getByCurrPage, alignPage } from './page';
export default (
isRemotePagination,
handleRemotePageChange
) => {
const PaginationContext = React.createContext();
class PaginationProvider extends React.Component {
static propTypes = {
data: PropTypes.array.isRequired
}
constructor(props) {
super(props);
this.handleChangePage = this.handleChangePage.bind(this);
this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this);
let currPage;
let currSizePerPage;
const { options } = props.pagination;
const sizePerPageList = options.sizePerPageList || Const.SIZE_PER_PAGE_LIST;
// initialize current page
if (typeof options.page !== 'undefined') {
currPage = options.page;
} else if (typeof options.pageStartIndex !== 'undefined') {
currPage = options.pageStartIndex;
} else {
currPage = Const.PAGE_START_INDEX;
}
// initialize current sizePerPage
if (typeof options.sizePerPage !== 'undefined') {
currSizePerPage = options.sizePerPage;
} else if (typeof sizePerPageList[0] === 'object') {
currSizePerPage = sizePerPageList[0].value;
} else {
currSizePerPage = sizePerPageList[0];
}
this.currPage = currPage;
this.currSizePerPage = currSizePerPage;
}
componentWillReceiveProps(nextProps) {
let needNewState = false;
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;
// 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;
}
} else {
this.currPage = nextProps.pagination.options.page;
this.currSizePerPage = nextProps.pagination.options.sizePerPage;
}
if (needNewState) {
if (onPageChange) {
onPageChange(currPage, currSizePerPage);
}
this.currPage = currPage;
this.currSizePerPage = currSizePerPage;
}
}
handleChangePage(currPage) {
const { currSizePerPage } = this;
const { pagination: { options } } = this.props;
if (options.onPageChange) {
options.onPageChange(currPage, currSizePerPage);
}
this.currPage = currPage;
if (isRemotePagination()) {
handleRemotePageChange(currPage, currSizePerPage);
return;
}
this.forceUpdate();
}
handleChangeSizePerPage(currSizePerPage, currPage) {
const { pagination: { options } } = this.props;
if (options.onSizePerPageChange) {
options.onSizePerPageChange(currSizePerPage, currPage);
}
this.currPage = currPage;
this.currSizePerPage = currSizePerPage;
if (isRemotePagination()) {
handleRemotePageChange(currPage, currSizePerPage);
return;
}
this.forceUpdate();
}
render() {
let { data } = this.props;
const { pagination: { options }, bootstrap4 } = this.props;
const { currPage, currSizePerPage } = this;
const withFirstAndLast = typeof options.withFirstAndLast === 'undefined' ?
Const.With_FIRST_AND_LAST : options.withFirstAndLast;
const alwaysShowAllBtns = typeof options.alwaysShowAllBtns === 'undefined' ?
Const.SHOW_ALL_PAGE_BTNS : options.alwaysShowAllBtns;
const hideSizePerPage = typeof options.hideSizePerPage === 'undefined' ?
Const.HIDE_SIZE_PER_PAGE : options.hideSizePerPage;
const hidePageListOnlyOnePage = typeof options.hidePageListOnlyOnePage === 'undefined' ?
Const.HIDE_PAGE_LIST_ONLY_ONE_PAGE : options.hidePageListOnlyOnePage;
const pageStartIndex = typeof options.pageStartIndex === 'undefined' ?
Const.PAGE_START_INDEX : options.pageStartIndex;
data = isRemotePagination() ?
data :
getByCurrPage(
data,
currPage,
currSizePerPage,
pageStartIndex
);
return (
<PaginationContext.Provider value={ { data } }>
{ this.props.children }
<BootstrapContext.Provider value={ { bootstrap4 } }>
<Pagination
key="pagination"
dataSize={ options.totalSize || this.props.data.length }
currPage={ currPage }
currSizePerPage={ currSizePerPage }
onPageChange={ this.handleChangePage }
onSizePerPageChange={ this.handleChangeSizePerPage }
sizePerPageList={ options.sizePerPageList || Const.SIZE_PER_PAGE_LIST }
paginationSize={ options.paginationSize || Const.PAGINATION_SIZE }
pageStartIndex={ pageStartIndex }
withFirstAndLast={ withFirstAndLast }
alwaysShowAllBtns={ alwaysShowAllBtns }
hideSizePerPage={ hideSizePerPage }
hidePageListOnlyOnePage={ hidePageListOnlyOnePage }
showTotal={ options.showTotal }
paginationTotalRenderer={ options.paginationTotalRenderer }
firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT }
prePageText={ options.prePageText || Const.PRE_PAGE_TEXT }
nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT }
lastPageText={ options.lastPageText || Const.LAST_PAGE_TEXT }
prePageTitle={ options.prePageTitle || Const.PRE_PAGE_TITLE }
nextPageTitle={ options.nextPageTitle || Const.NEXT_PAGE_TITLE }
firstPageTitle={ options.firstPageTitle || Const.FIRST_PAGE_TITLE }
lastPageTitle={ options.lastPageTitle || Const.LAST_PAGE_TITLE }
/>
</BootstrapContext.Provider>
</PaginationContext.Provider>
);
}
}
return {
Provider: PaginationProvider,
Consumer: PaginationContext.Consumer
};
};

View File

@@ -0,0 +1,108 @@
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
/* eslint no-lonely-if: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import Const from './const';
import { BootstrapContext } from './bootstrap';
import Pagination from './pagination';
import { getByCurrPage, alignPage } from './page';
import createBaseContext from './state-context';
const { Provider } = createBaseContext();
const PaginationDataContext = React.createContext();
class PaginationDataProvider extends Provider {
static propTypes = {
data: PropTypes.array.isRequired,
remoteEmitter: PropTypes.object.isRequired,
isRemotePagination: PropTypes.func.isRequired
}
componentWillReceiveProps(nextProps) {
super.componentWillReceiveProps(nextProps);
const { currSizePerPage } = this;
const { custom, onPageChange } = nextProps.pagination.options;
const pageStartIndex = typeof nextProps.pagination.options.pageStartIndex !== 'undefined' ?
nextProps.pagination.options.pageStartIndex : Const.PAGE_START_INDEX;
// user should align the page when the page is not fit to the data size when remote enable
if (!this.isRemotePagination() && !custom) {
const newPage = alignPage(
nextProps.data.length,
this.props.data.length,
this.currPage,
currSizePerPage,
pageStartIndex
);
if (this.currPage !== newPage) {
if (onPageChange) {
onPageChange(newPage, currSizePerPage);
}
this.currPage = newPage;
}
}
if (nextProps.onDataSizeChange && nextProps.data.length !== this.props.data.length) {
nextProps.onDataSizeChange({ dataSize: nextProps.data.length });
}
}
isRemotePagination = () => this.props.isRemotePagination();
renderDefaultPagination = () => {
if (!this.props.pagination.options.custom) {
const {
bootstrap4,
page: currPage,
sizePerPage: currSizePerPage,
dataSize,
...rest
} = this.getPaginationProps();
return (
<BootstrapContext.Provider value={ { bootstrap4 } }>
<Pagination
{ ...rest }
key="pagination"
dataSize={ dataSize || this.props.data.length }
currPage={ currPage }
currSizePerPage={ currSizePerPage }
/>
</BootstrapContext.Provider>
);
}
return null;
}
render() {
let { data } = this.props;
const { pagination: { options } } = this.props;
const { currPage, currSizePerPage } = this;
const pageStartIndex = typeof options.pageStartIndex === 'undefined' ?
Const.PAGE_START_INDEX : options.pageStartIndex;
data = this.isRemotePagination() ?
data :
getByCurrPage(
data,
currPage,
currSizePerPage,
pageStartIndex
);
return (
<PaginationDataContext.Provider value={ { data, setRemoteEmitter: this.setRemoteEmitter } }>
{ this.props.children }
{ this.renderDefaultPagination() }
</PaginationDataContext.Provider>
);
}
}
export default () => ({
Provider: PaginationDataProvider,
Consumer: PaginationDataContext.Consumer
});

View File

@@ -20,13 +20,14 @@ class PageButton extends Component {
page,
title,
active,
disabled
disabled,
className
} = this.props;
const classes = cs({
active,
disabled,
'page-item': true
});
}, className);
return (
<li className={ classes } title={ title }>
@@ -41,6 +42,7 @@ PageButton.propTypes = {
page: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
active: PropTypes.bool.isRequired,
disabled: PropTypes.bool.isRequired,
className: PropTypes.string,
title: PropTypes.string
};

View File

@@ -8,16 +8,10 @@ export default ExtendBase =>
return (currPage - 1) < pageStartIndex ? pageStartIndex : currPage - 1;
}
goToNextPage() {
const { currPage } = this.props;
const { lastPage } = this.state;
return (currPage + 1) > lastPage ? lastPage : currPage + 1;
}
initialState() {
const totalPages = this.calculateTotalPage();
const lastPage = this.calculateLastPage(totalPages);
return { totalPages, lastPage, dropdownOpen: false };
return { totalPages, lastPage };
}
calculateTotalPage(sizePerPage = this.props.currSizePerPage, dataSize = this.props.dataSize) {
@@ -47,8 +41,9 @@ export default ExtendBase =>
}
calculatePages(
totalPages = this.state.totalPages,
lastPage = this.state.lastPage) {
totalPages,
lastPage
) {
const {
currPage,
paginationSize,
@@ -61,7 +56,7 @@ export default ExtendBase =>
alwaysShowAllBtns
} = this.props;
let pages;
let pages = [];
let endPage = totalPages;
if (endPage <= 0) return [];
@@ -73,28 +68,46 @@ export default ExtendBase =>
startPage = endPage - paginationSize + 1;
}
if (startPage !== pageStartIndex && totalPages > paginationSize && withFirstAndLast) {
if (alwaysShowAllBtns) {
if (withFirstAndLast) {
pages = [firstPageText, prePageText];
} else if (totalPages > 1 || alwaysShowAllBtns) {
pages = [prePageText];
} else {
pages = [];
pages = [prePageText];
}
}
if (startPage !== pageStartIndex &&
totalPages > paginationSize &&
withFirstAndLast &&
pages.length === 0
) {
pages = [firstPageText, prePageText];
} else if (totalPages > 1 && pages.length === 0) {
pages = [prePageText];
}
for (let i = startPage; i <= endPage; i += 1) {
if (i >= pageStartIndex) pages.push(i);
}
if (endPage <= lastPage && pages.length > 1) {
if (alwaysShowAllBtns || (endPage <= lastPage && pages.length > 1)) {
pages.push(nextPageText);
}
if (endPage !== lastPage && withFirstAndLast) {
if ((endPage !== lastPage && withFirstAndLast) || (withFirstAndLast && alwaysShowAllBtns)) {
pages.push(lastPageText);
}
// if ((endPage <= lastPage && pages.length > 1) || alwaysShowAllBtns) {
// pages.push(nextPageText);
// }
// if (endPage !== lastPage && withFirstAndLast) {
// pages.push(lastPageText);
// }
return pages;
}
calculatePageStatus(pages = [], lastPage = this.state.lastPage) {
calculatePageStatus(pages = [], lastPage) {
const {
currPage,
pageStartIndex,
@@ -140,8 +153,8 @@ export default ExtendBase =>
calculateSizePerPageStatus() {
const { sizePerPageList } = this.props;
return sizePerPageList.map((_sizePerPage) => {
const pageText = _sizePerPage.text || _sizePerPage;
const pageNumber = _sizePerPage.value || _sizePerPage;
const pageText = typeof _sizePerPage.text !== 'undefined' ? _sizePerPage.text : _sizePerPage;
const pageNumber = typeof _sizePerPage.value !== 'undefined' ? _sizePerPage.value : _sizePerPage;
return {
text: `${pageText}`,
page: pageNumber

View File

@@ -1,3 +1,5 @@
import Const from './const';
const getNormalizedPage = (
page,
pageStartIndex
@@ -18,15 +20,21 @@ const startIndex = (
) => end - (sizePerPage - 1);
export const alignPage = (
data,
dataSize,
prevDataSize,
page,
sizePerPage,
pageStartIndex
) => {
const dataSize = data.length;
if (page < pageStartIndex || page > (Math.floor(dataSize / sizePerPage) + pageStartIndex)) {
return pageStartIndex;
if (prevDataSize < dataSize) return page;
if (page < pageStartIndex) return pageStartIndex;
if (dataSize <= 0) return pageStartIndex;
if ((page >= (Math.floor(dataSize / sizePerPage) + pageStartIndex)) && pageStartIndex === 1) {
return Math.ceil(dataSize / sizePerPage);
}
if (page >= Math.floor(dataSize / sizePerPage) && pageStartIndex === 0) {
const newPage = Math.ceil(dataSize / sizePerPage);
return newPage - Math.abs((Const.PAGE_START_INDEX - pageStartIndex));
}
return page;
};

View File

@@ -0,0 +1,77 @@
/* eslint react/prop-types: 0 */
import React, { Component } from 'react';
import pageResolver from './page-resolver';
export default WrappedComponent =>
class PaginationHandler extends pageResolver(Component) {
constructor(props) {
super(props);
this.handleChangePage = this.handleChangePage.bind(this);
this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this);
this.state = this.initialState();
}
componentWillReceiveProps(nextProps) {
const { dataSize, currSizePerPage } = nextProps;
if (currSizePerPage !== this.props.currSizePerPage || dataSize !== this.props.dataSize) {
const totalPages = this.calculateTotalPage(currSizePerPage, dataSize);
const lastPage = this.calculateLastPage(totalPages);
this.setState({ totalPages, lastPage });
}
}
handleChangeSizePerPage(sizePerPage) {
const { currSizePerPage, onSizePerPageChange } = this.props;
const selectedSize = typeof sizePerPage === 'string' ? parseInt(sizePerPage, 10) : sizePerPage;
let { currPage } = this.props;
if (selectedSize !== currSizePerPage) {
const newTotalPages = this.calculateTotalPage(selectedSize);
const newLastPage = this.calculateLastPage(newTotalPages);
if (currPage > newLastPage) currPage = newLastPage;
onSizePerPageChange(selectedSize, currPage);
}
}
handleChangePage(newPage) {
let page;
const {
currPage,
pageStartIndex,
prePageText,
nextPageText,
lastPageText,
firstPageText,
onPageChange
} = this.props;
const { lastPage } = this.state;
if (newPage === prePageText) {
page = this.backToPrevPage();
} else if (newPage === nextPageText) {
page = (currPage + 1) > lastPage ? lastPage : currPage + 1;
} else if (newPage === lastPageText) {
page = lastPage;
} else if (newPage === firstPageText) {
page = pageStartIndex;
} else {
page = parseInt(newPage, 10);
}
if (page !== currPage) {
onPageChange(page);
}
}
render() {
return (
<WrappedComponent
{ ...this.props }
lastPage={ this.state.lastPage }
totalPages={ this.state.totalPages }
onPageChange={ this.handleChangePage }
onSizePerPageChange={ this.handleChangeSizePerPage }
/>
);
}
};

View File

@@ -0,0 +1,24 @@
/* eslint react/prop-types: 0 */
import React, { Component } from 'react';
import pageResolver from './page-resolver';
import PaginationList from './pagination-list';
const paginationListAdapter = WrappedComponent =>
class PaginationListAdapter extends pageResolver(Component) {
render() {
const { lastPage, totalPages, pageButtonRenderer, onPageChange } = this.props;
const pages = this.calculatePageStatus(this.calculatePages(totalPages, lastPage), lastPage);
return (
<WrappedComponent
pageButtonRenderer={ pageButtonRenderer }
onPageChange={ onPageChange }
pages={ pages }
/>
);
}
};
export const PaginationListWithAdapter = paginationListAdapter(PaginationList);
export default paginationListAdapter;

View File

@@ -0,0 +1,12 @@
import React from 'react';
import PaginationList from './pagination-list';
import standaloneAdapter from './standalone-adapter';
import PaginationHandler from './pagination-handler';
import paginationListAdapter from './pagination-list-adapter';
const PaginationListStandalone = props => (
<PaginationList { ...props } />
);
export default
standaloneAdapter(PaginationHandler(paginationListAdapter(PaginationListStandalone)));

View File

@@ -6,13 +6,21 @@ import PageButton from './page-button';
const PaginatonList = props => (
<ul className="pagination react-bootstrap-table-page-btns-ul">
{
props.pages.map(pageProps => (
props.pages.map((pageProps) => {
if (props.pageButtonRenderer) {
return props.pageButtonRenderer({
...pageProps,
onPageChange: props.onPageChange
});
}
return (
<PageButton
key={ pageProps.page }
{ ...pageProps }
onPageChange={ props.onPageChange }
/>
))
);
})
}
</ul>
);
@@ -24,7 +32,12 @@ PaginatonList.propTypes = {
disable: PropTypes.bool,
title: PropTypes.string
})).isRequired,
onPageChange: PropTypes.func.isRequired
onPageChange: PropTypes.func.isRequired,
pageButtonRenderer: PropTypes.func
};
PaginatonList.defaultProps = {
pageButtonRenderer: null
};
export default PaginatonList;

View File

@@ -0,0 +1,24 @@
/* eslint react/prop-types: 0 */
import React, { Component } from 'react';
import pageResolver from './page-resolver';
import PaginationTotal from './pagination-total';
const paginationTotalAdapter = WrappedComponent =>
class PaginationTotalAdapter extends pageResolver(Component) {
render() {
const [from, to] = this.calculateFromTo();
return (
<WrappedComponent
from={ from }
to={ to }
dataSize={ this.props.dataSize }
paginationTotalRenderer={ this.props.paginationTotalRenderer }
/>
);
}
};
export const PaginationTotalWithAdapter = paginationTotalAdapter(PaginationTotal);
export default paginationTotalAdapter;

View File

@@ -0,0 +1,11 @@
import React from 'react';
import PaginationTotal from './pagination-total';
import standaloneAdapter from './standalone-adapter';
import paginationTotalAdapter from './pagination-total-adapter';
const PaginationTotalStandalone = props => (
<PaginationTotal { ...props } />
);
export default
standaloneAdapter(paginationTotalAdapter(PaginationTotalStandalone));

View File

@@ -1,16 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
const PaginationTotal = props => (
const PaginationTotal = (props) => {
if (props.paginationTotalRenderer) {
return props.paginationTotalRenderer(props.from, props.to, props.dataSize);
}
return (
<span className="react-bootstrap-table-pagination-total">
&nbsp;Showing rows { props.from } to&nbsp;{ props.to } of&nbsp;{ props.dataSize }
</span>
);
);
};
PaginationTotal.propTypes = {
from: PropTypes.number.isRequired,
to: PropTypes.number.isRequired,
dataSize: PropTypes.number.isRequired
dataSize: PropTypes.number.isRequired,
paginationTotalRenderer: PropTypes.func
};
PaginationTotal.defaultProps = {
paginationTotalRenderer: undefined
};
export default PaginationTotal;

View File

@@ -4,118 +4,36 @@ import cs from 'classnames';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import pageResolver from './page-resolver';
import SizePerPageDropDown from './size-per-page-dropdown';
import PaginationList from './pagination-list';
import PaginationTotal from './pagination-total';
import paginationHandler from './pagination-handler';
import { SizePerPageDropdownWithAdapter } from './size-per-page-dropdown-adapter';
import { PaginationListWithAdapter } from './pagination-list-adapter';
import { PaginationTotalWithAdapter } from './pagination-total-adapter';
import Const from './const';
class Pagination extends pageResolver(Component) {
constructor(props) {
super(props);
this.closeDropDown = this.closeDropDown.bind(this);
this.toggleDropDown = this.toggleDropDown.bind(this);
this.handleChangePage = this.handleChangePage.bind(this);
this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this);
this.state = this.initialState();
}
componentWillReceiveProps(nextProps) {
const { dataSize, currSizePerPage } = nextProps;
if (currSizePerPage !== this.props.currSizePerPage || dataSize !== this.props.dataSize) {
const totalPages = this.calculateTotalPage(currSizePerPage, dataSize);
const lastPage = this.calculateLastPage(totalPages);
this.setState({ totalPages, lastPage });
}
}
toggleDropDown() {
const dropdownOpen = !this.state.dropdownOpen;
this.setState(() => {
return { dropdownOpen };
});
}
closeDropDown() {
this.setState(() => {
return { dropdownOpen: false };
});
}
handleChangeSizePerPage(sizePerPage) {
const { currSizePerPage, onSizePerPageChange } = this.props;
const selectedSize = typeof sizePerPage === 'string' ? parseInt(sizePerPage, 10) : sizePerPage;
let { currPage } = this.props;
if (selectedSize !== currSizePerPage) {
const newTotalPages = this.calculateTotalPage(selectedSize);
const newLastPage = this.calculateLastPage(newTotalPages);
if (currPage > newLastPage) currPage = newLastPage;
onSizePerPageChange(selectedSize, currPage);
}
this.closeDropDown();
}
handleChangePage(newPage) {
let page;
render() {
const {
currPage,
pageStartIndex,
prePageText,
nextPageText,
lastPageText,
firstPageText,
onPageChange
// keepSizePerPageState
} = this.props;
const { lastPage } = this.state;
if (newPage === prePageText) {
page = this.backToPrevPage();
} else if (newPage === nextPageText) {
page = (currPage + 1) > lastPage ? lastPage : currPage + 1;
} else if (newPage === lastPageText) {
page = lastPage;
} else if (newPage === firstPageText) {
page = pageStartIndex;
} else {
page = parseInt(newPage, 10);
}
// if (keepSizePerPageState) { this.closeDropDown(); }
if (page !== currPage) {
onPageChange(page);
}
}
defaultTotal = (from, to, size) => (
<PaginationTotal
from={ from }
to={ to }
dataSize={ size }
/>
);
setTotal = (from, to, size, total) => {
if (total && (typeof total === 'function')) {
return total(from, to, size);
}
return this.defaultTotal(from, to, size);
};
render() {
const { totalPages, lastPage, dropdownOpen: open } = this.state;
const {
showTotal,
dataSize,
pageListRenderer,
pageButtonRenderer,
paginationTotalRenderer,
hidePageListOnlyOnePage,
totalPages,
lastPage,
onPageChange,
sizePerPageList,
currSizePerPage,
hideSizePerPage,
hidePageListOnlyOnePage
sizePerPageRenderer,
sizePerPageOptionRenderer,
onSizePerPageChange,
...rest
} = this.props;
const pages = this.calculatePageStatus(this.calculatePages(totalPages), lastPage);
const [from, to] = this.calculateFromTo();
const pages = this.calculatePageStatus(this.calculatePages(totalPages, lastPage), lastPage);
const pageListClass = cs(
'react-bootstrap-table-pagination-list',
'col-md-6 col-xs-6 col-sm-6 col-lg-6', {
@@ -124,32 +42,44 @@ class Pagination extends pageResolver(Component) {
return (
<div className="row react-bootstrap-table-pagination">
<div className="col-md-6 col-xs-6 col-sm-6 col-lg-6">
{
sizePerPageList.length > 1 && !hideSizePerPage ?
(
<SizePerPageDropDown
currSizePerPage={ `${currSizePerPage}` }
options={ this.calculateSizePerPageStatus() }
onSizePerPageChange={ this.handleChangeSizePerPage }
onClick={ this.toggleDropDown }
onBlur={ this.closeDropDown }
open={ open }
<SizePerPageDropdownWithAdapter
sizePerPageList={ sizePerPageList }
currSizePerPage={ currSizePerPage }
hideSizePerPage={ hideSizePerPage }
sizePerPageRenderer={ sizePerPageRenderer }
sizePerPageOptionRenderer={ sizePerPageOptionRenderer }
onSizePerPageChange={ onSizePerPageChange }
/>
) : null
}
{
showTotal ?
this.setTotal(
from,
to,
dataSize,
paginationTotalRenderer
) : null
<PaginationTotalWithAdapter
currPage={ currPage }
currSizePerPage={ currSizePerPage }
pageStartIndex={ pageStartIndex }
dataSize={ dataSize }
paginationTotalRenderer={ paginationTotalRenderer }
/> : null
}
</div>
{
pageListRenderer ? pageListRenderer({
pages,
onPageChange
}) : (
<div className={ pageListClass }>
<PaginationList pages={ pages } onPageChange={ this.handleChangePage } />
<PaginationListWithAdapter
{ ...rest }
currPage={ currPage }
currSizePerPage={ currSizePerPage }
pageStartIndex={ pageStartIndex }
lastPage={ lastPage }
totalPages={ totalPages }
pageButtonRenderer={ pageButtonRenderer }
onPageChange={ onPageChange }
/>
</div>
)
}
</div>
);
}
@@ -165,7 +95,11 @@ Pagination.propTypes = {
pageStartIndex: PropTypes.number,
paginationSize: PropTypes.number,
showTotal: PropTypes.bool,
pageListRenderer: PropTypes.func,
pageButtonRenderer: PropTypes.func,
sizePerPageRenderer: PropTypes.func,
paginationTotalRenderer: PropTypes.func,
sizePerPageOptionRenderer: PropTypes.func,
firstPageText: PropTypes.string,
prePageText: PropTypes.string,
nextPageText: PropTypes.string,
@@ -186,7 +120,11 @@ Pagination.defaultProps = {
withFirstAndLast: Const.With_FIRST_AND_LAST,
alwaysShowAllBtns: Const.SHOW_ALL_PAGE_BTNS,
showTotal: Const.SHOW_TOTAL,
pageListRenderer: null,
pageButtonRenderer: null,
sizePerPageRenderer: null,
paginationTotalRenderer: Const.PAGINATION_TOTAL,
sizePerPageOptionRenderer: null,
firstPageText: Const.FIRST_PAGE_TEXT,
prePageText: Const.PRE_PAGE_TEXT,
nextPageText: Const.NEXT_PAGE_TEXT,
@@ -200,4 +138,4 @@ Pagination.defaultProps = {
hidePageListOnlyOnePage: Const.HIDE_PAGE_LIST_ONLY_ONE_PAGE
};
export default Pagination;
export default paginationHandler(Pagination);

View File

@@ -0,0 +1,68 @@
/* eslint react/prop-types: 0 */
import React, { Component } from 'react';
import pageResolver from './page-resolver';
import SizePerPageDropDown from './size-per-page-dropdown';
const sizePerPageDropdownAdapter = WrappedComponent =>
class SizePerPageDropdownAdapter extends pageResolver(Component) {
constructor(props) {
super(props);
this.closeDropDown = this.closeDropDown.bind(this);
this.toggleDropDown = this.toggleDropDown.bind(this);
this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this);
this.state = { dropdownOpen: false };
}
toggleDropDown() {
const dropdownOpen = !this.state.dropdownOpen;
this.setState(() => ({ dropdownOpen }));
}
closeDropDown() {
this.setState(() => ({ dropdownOpen: false }));
}
handleChangeSizePerPage(sizePerPage) {
this.props.onSizePerPageChange(sizePerPage);
this.closeDropDown();
}
render() {
const {
sizePerPageList,
currSizePerPage,
hideSizePerPage,
sizePerPageRenderer,
sizePerPageOptionRenderer
} = this.props;
const { dropdownOpen: open } = this.state;
if (sizePerPageList.length > 1 && !hideSizePerPage) {
if (sizePerPageRenderer) {
return sizePerPageRenderer({
options: this.calculateSizePerPageStatus(),
currSizePerPage: `${currSizePerPage}`,
onSizePerPageChange: this.handleChangeSizePerPage
});
}
return (
<WrappedComponent
{ ...this.props }
currSizePerPage={ `${currSizePerPage}` }
options={ this.calculateSizePerPageStatus() }
optionRenderer={ sizePerPageOptionRenderer }
onSizePerPageChange={ this.handleChangeSizePerPage }
onClick={ this.toggleDropDown }
onBlur={ this.closeDropDown }
open={ open }
/>
);
}
return null;
}
};
export const SizePerPageDropdownWithAdapter = sizePerPageDropdownAdapter(SizePerPageDropDown);
export default sizePerPageDropdownAdapter;

View File

@@ -0,0 +1,12 @@
import React from 'react';
import SizePerPageDropdown from './size-per-page-dropdown';
import standaloneAdapter from './standalone-adapter';
import paginationHandler from './pagination-handler';
import sizePerPageDropdownAdapter from './size-per-page-dropdown-adapter';
const SizePerPageDropdownStandalone = props => (
<SizePerPageDropdown { ...props } />
);
export default
standaloneAdapter(paginationHandler(sizePerPageDropdownAdapter(SizePerPageDropdownStandalone)));

View File

@@ -16,6 +16,7 @@ const SizePerPageDropDown = (props) => {
className,
variation,
btnContextual,
optionRenderer,
currSizePerPage,
onSizePerPageChange
} = props;
@@ -61,14 +62,22 @@ const SizePerPageDropDown = (props) => {
aria-labelledby="pageDropDown"
>
{
options.map(option => (
options.map((option) => {
if (optionRenderer) {
return optionRenderer({
...option,
onSizePerPageChange
});
}
return (
<SizePerPageOption
{ ...option }
key={ option.text }
bootstrap4={ bootstrap4 }
onSizePerPageChange={ onSizePerPageChange }
/>
))
);
})
}
</ul>
</span>
@@ -88,14 +97,16 @@ SizePerPageDropDown.propTypes = {
hidden: PropTypes.bool,
btnContextual: PropTypes.string,
variation: PropTypes.oneOf(['dropdown', 'dropup']),
className: PropTypes.string
className: PropTypes.string,
optionRenderer: PropTypes.func
};
SizePerPageDropDown.defaultProps = {
open: false,
hidden: false,
btnContextual: 'btn-default btn-secondary',
variation: 'dropdown',
className: ''
className: '',
optionRenderer: null
};

View File

@@ -0,0 +1,14 @@
/* eslint react/prop-types: 0 */
import React from 'react';
export default WrappedComponent => ({
page,
sizePerPage,
...rest
}) => (
<WrappedComponent
{ ...rest }
currPage={ page }
currSizePerPage={ sizePerPage }
/>
);

View File

@@ -0,0 +1,198 @@
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
/* eslint no-lonely-if: 0 */
import React from 'react';
import EventEmitter from 'events';
import Const from './const';
import { alignPage } from './page';
const StateContext = React.createContext();
class StateProvider extends React.Component {
constructor(props) {
super(props);
this.handleChangePage = this.handleChangePage.bind(this);
this.handleDataSizeChange = this.handleDataSizeChange.bind(this);
this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this);
let currPage;
let currSizePerPage;
const { options } = props.pagination;
const sizePerPageList = options.sizePerPageList || Const.SIZE_PER_PAGE_LIST;
// initialize current page
if (typeof options.page !== 'undefined') {
currPage = options.page;
} else if (typeof options.pageStartIndex !== 'undefined') {
currPage = options.pageStartIndex;
} else {
currPage = Const.PAGE_START_INDEX;
}
// initialize current sizePerPage
if (typeof options.sizePerPage !== 'undefined') {
currSizePerPage = options.sizePerPage;
} else if (typeof sizePerPageList[0] === 'object') {
currSizePerPage = sizePerPageList[0].value;
} else {
currSizePerPage = sizePerPageList[0];
}
this.currPage = currPage;
this.dataSize = options.totalSize;
this.currSizePerPage = currSizePerPage;
this.dataChangeListener = new EventEmitter();
this.dataChangeListener.on('filterChanged', this.handleDataSizeChange);
}
componentWillReceiveProps(nextProps) {
const { custom } = nextProps.pagination.options;
// user should align the page when the page is not fit to the data size when remote enable
if (this.isRemotePagination() || custom) {
if (typeof nextProps.pagination.options.page !== 'undefined') {
this.currPage = nextProps.pagination.options.page;
}
if (typeof nextProps.pagination.options.sizePerPage !== 'undefined') {
this.currSizePerPage = nextProps.pagination.options.sizePerPage;
}
if (typeof nextProps.pagination.options.totalSize !== 'undefined') {
this.dataSize = nextProps.pagination.options.totalSize;
}
}
}
getPaginationProps = () => {
const { pagination: { options }, bootstrap4 } = this.props;
const { currPage, currSizePerPage, dataSize } = this;
const withFirstAndLast = typeof options.withFirstAndLast === 'undefined' ?
Const.With_FIRST_AND_LAST : options.withFirstAndLast;
const alwaysShowAllBtns = typeof options.alwaysShowAllBtns === 'undefined' ?
Const.SHOW_ALL_PAGE_BTNS : options.alwaysShowAllBtns;
const hideSizePerPage = typeof options.hideSizePerPage === 'undefined' ?
Const.HIDE_SIZE_PER_PAGE : options.hideSizePerPage;
const hidePageListOnlyOnePage = typeof options.hidePageListOnlyOnePage === 'undefined' ?
Const.HIDE_PAGE_LIST_ONLY_ONE_PAGE : options.hidePageListOnlyOnePage;
const pageStartIndex = typeof options.pageStartIndex === 'undefined' ?
Const.PAGE_START_INDEX : options.pageStartIndex;
return {
...options,
bootstrap4,
page: currPage,
sizePerPage: currSizePerPage,
pageStartIndex,
hidePageListOnlyOnePage,
hideSizePerPage,
alwaysShowAllBtns,
withFirstAndLast,
dataSize,
sizePerPageList: options.sizePerPageList || Const.SIZE_PER_PAGE_LIST,
paginationSize: options.paginationSize || Const.PAGINATION_SIZE,
showTotal: options.showTotal,
pageListRenderer: options.pageListRenderer,
pageButtonRenderer: options.pageButtonRenderer,
sizePerPageRenderer: options.sizePerPageRenderer,
paginationTotalRenderer: options.paginationTotalRenderer,
sizePerPageOptionRenderer: options.sizePerPageOptionRenderer,
firstPageText: options.firstPageText || Const.FIRST_PAGE_TEXT,
prePageText: options.prePageText || Const.PRE_PAGE_TEXT,
nextPageText: options.nextPageText || Const.NEXT_PAGE_TEXT,
lastPageText: options.lastPageText || Const.LAST_PAGE_TEXT,
prePageTitle: options.prePageTitle || Const.PRE_PAGE_TITLE,
nextPageTitle: options.nextPageTitle || Const.NEXT_PAGE_TITLE,
firstPageTitle: options.firstPageTitle || Const.FIRST_PAGE_TITLE,
lastPageTitle: options.lastPageTitle || Const.LAST_PAGE_TITLE,
onPageChange: this.handleChangePage,
onSizePerPageChange: this.handleChangeSizePerPage
};
}
setPaginationRemoteEmitter = (remoteEmitter) => {
this.remoteEmitter = remoteEmitter;
}
getPaginationRemoteEmitter = () => this.remoteEmitter || this.props.remoteEmitter;
isRemotePagination = () => {
const e = {};
this.remoteEmitter.emit('isRemotePagination', e);
return e.result;
};
handleDataSizeChange(newDataSize) {
const { pagination: { options } } = this.props;
const pageStartIndex = typeof options.pageStartIndex === 'undefined' ?
Const.PAGE_START_INDEX : options.pageStartIndex;
this.currPage = alignPage(
newDataSize,
this.dataSize,
this.currPage,
this.currSizePerPage,
pageStartIndex
);
this.dataSize = newDataSize;
this.forceUpdate();
}
handleChangePage(currPage) {
const { currSizePerPage } = this;
const { pagination: { options } } = this.props;
if (options.onPageChange) {
options.onPageChange(currPage, currSizePerPage);
}
this.currPage = currPage;
if (this.isRemotePagination()) {
this.getPaginationRemoteEmitter().emit('paginationChange', currPage, currSizePerPage);
return;
}
this.forceUpdate();
}
handleChangeSizePerPage(currSizePerPage, currPage) {
const { pagination: { options } } = this.props;
if (options.onSizePerPageChange) {
options.onSizePerPageChange(currSizePerPage, currPage);
}
this.currPage = currPage;
this.currSizePerPage = currSizePerPage;
if (this.isRemotePagination()) {
this.getPaginationRemoteEmitter().emit('paginationChange', currPage, currSizePerPage);
return;
}
this.forceUpdate();
}
render() {
const paginationProps = this.getPaginationProps();
const pagination = {
...this.props.pagination,
options: paginationProps
};
return (
<StateContext.Provider
value={ {
paginationProps,
paginationTableProps: {
pagination,
setPaginationRemoteEmitter: this.setPaginationRemoteEmitter,
dataChangeListener: this.dataChangeListener
}
} }
>
{ this.props.children }
</StateContext.Provider>
);
}
}
export default () => ({
Provider: StateProvider,
Consumer: StateContext.Consumer
});

View File

@@ -1,711 +0,0 @@
import 'jsdom-global/register';
import React from 'react';
import { shallow } from 'enzyme';
import BootstrapTable from 'react-bootstrap-table-next/src/bootstrap-table';
import Pagination from '../src/pagination';
import Const from '../src/const';
import createPaginationContext from '../src/context';
import paginationFactory from '../index';
const data = [];
for (let i = 0; i < 100; i += 1) {
data.push({
id: i,
name: `itme name ${i}`
});
}
describe('PaginationContext', () => {
let wrapper;
let PaginationContext;
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}];
const defaultPagination = { options: {} };
const mockBase = jest.fn((props => (
<BootstrapTable
keyField="id"
data={ data }
columns={ columns }
{ ...props }
/>
)));
const handleRemotePaginationChange = jest.fn();
function shallowContext(
customPagination = defaultPagination,
enableRemote = false
) {
mockBase.mockReset();
handleRemotePaginationChange.mockReset();
PaginationContext = createPaginationContext(
jest.fn().mockReturnValue(enableRemote),
handleRemotePaginationChange
);
return (
<PaginationContext.Provider
pagination={ paginationFactory(customPagination) }
columns={ columns }
data={ data }
>
<PaginationContext.Consumer>
{
paginationProps => mockBase(paginationProps)
}
</PaginationContext.Consumer>
</PaginationContext.Provider>
);
}
describe('default render', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.render();
});
it('should have correct Provider property after calling createPaginationContext', () => {
expect(PaginationContext.Provider).toBeDefined();
});
it('should have correct Consumer property after calling createPaginationContext', () => {
expect(PaginationContext.Consumer).toBeDefined();
});
it('should have correct currPage', () => {
expect(wrapper.instance().currPage).toEqual(Const.PAGE_START_INDEX);
});
it('should have correct currSizePerPage', () => {
expect(wrapper.instance().currSizePerPage).toEqual(Const.SIZE_PER_PAGE_LIST[0]);
});
it('should render Pagination component correctly', () => {
expect(wrapper.length).toBe(1);
const instance = wrapper.instance();
const pagination = wrapper.find(Pagination);
expect(pagination).toHaveLength(1);
expect(pagination.prop('dataSize')).toEqual(data.length);
expect(pagination.prop('currPage')).toEqual(instance.currPage);
expect(pagination.prop('currSizePerPage')).toEqual(instance.currSizePerPage);
expect(pagination.prop('onPageChange')).toEqual(instance.handleChangePage);
expect(pagination.prop('onSizePerPageChange')).toEqual(instance.handleChangeSizePerPage);
expect(pagination.prop('sizePerPageList')).toEqual(Const.SIZE_PER_PAGE_LIST);
expect(pagination.prop('paginationSize')).toEqual(Const.PAGINATION_SIZE);
expect(pagination.prop('pageStartIndex')).toEqual(Const.PAGE_START_INDEX);
expect(pagination.prop('withFirstAndLast')).toEqual(Const.With_FIRST_AND_LAST);
expect(pagination.prop('alwaysShowAllBtns')).toEqual(Const.SHOW_ALL_PAGE_BTNS);
expect(pagination.prop('firstPageText')).toEqual(Const.FIRST_PAGE_TEXT);
expect(pagination.prop('prePageText')).toEqual(Const.PRE_PAGE_TEXT);
expect(pagination.prop('nextPageText')).toEqual(Const.NEXT_PAGE_TEXT);
expect(pagination.prop('lastPageText')).toEqual(Const.LAST_PAGE_TEXT);
expect(pagination.prop('firstPageTitle')).toEqual(Const.FIRST_PAGE_TITLE);
expect(pagination.prop('prePageTitle')).toEqual(Const.PRE_PAGE_TITLE);
expect(pagination.prop('nextPageTitle')).toEqual(Const.NEXT_PAGE_TITLE);
expect(pagination.prop('lastPageTitle')).toEqual(Const.LAST_PAGE_TITLE);
expect(pagination.prop('hideSizePerPage')).toEqual(Const.HIDE_SIZE_PER_PAGE);
expect(pagination.prop('hideSizePerPage')).toEqual(Const.HIDE_SIZE_PER_PAGE);
expect(pagination.prop('paginationTotalRenderer')).toBeNull();
});
it('should pass correct cell editing props to children element', () => {
expect(mockBase.mock.calls[0][0].data).toHaveLength(Const.SIZE_PER_PAGE_LIST[0]);
});
});
describe('componentWillReceiveProps', () => {
let instance;
let nextProps;
describe('when nextProps.pagination.options.page is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
page: 3
}));
instance = wrapper.instance();
wrapper.render();
nextProps = { data, pagination: defaultPagination };
instance.componentWillReceiveProps(nextProps);
});
it('should not set currPage', () => {
expect(instance.currPage).toEqual(3);
});
});
describe('when nextProps.pagination.options.sizePerPage is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
sizePerPage: Const.SIZE_PER_PAGE_LIST[2]
}));
instance = wrapper.instance();
wrapper.render();
nextProps = { data, pagination: defaultPagination };
instance.componentWillReceiveProps(nextProps);
});
it('should not set currSizePerPage', () => {
expect(instance.currSizePerPage).toEqual(Const.SIZE_PER_PAGE_LIST[2]);
});
});
describe('when remote pagination is enable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({ ...defaultPagination }, true));
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: { ...defaultPagination, options: { page: 3, sizePerPage: 5 } }
};
instance.componentWillReceiveProps(nextProps);
});
it('should always set currPage from nextProps.pagination.options.page', () => {
expect(instance.currPage).toEqual(nextProps.pagination.options.page);
});
it('should always set currSizePerPage from nextProps.pagination.options.sizePerPage', () => {
expect(instance.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
});
});
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', () => {
let instance;
const newPage = 3;
describe('should update component correctly', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
jest.spyOn(instance, 'forceUpdate');
instance.handleChangePage(newPage);
});
it('', () => {
expect(instance.currPage).toEqual(newPage);
expect(instance.forceUpdate).toHaveBeenCalledTimes(1);
});
});
describe('if options.onPageChange is defined', () => {
const onPageChange = jest.fn();
beforeEach(() => {
onPageChange.mockClear();
wrapper = shallow(shallowContext({
...defaultPagination,
onPageChange
}));
instance = wrapper.instance();
jest.spyOn(instance, 'forceUpdate');
instance.handleChangePage(newPage);
});
it('should still update component correctly', () => {
expect(instance.currPage).toEqual(newPage);
expect(instance.forceUpdate).toHaveBeenCalledTimes(1);
});
it('should call options.onPageChange correctly', () => {
expect(onPageChange).toHaveBeenCalledTimes(1);
expect(onPageChange).toHaveBeenCalledWith(newPage, instance.currSizePerPage);
});
});
describe('if remote pagination is enable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination
}, true));
instance = wrapper.instance();
jest.spyOn(instance, 'forceUpdate');
instance.handleChangePage(newPage);
});
it('should still update component correctly', () => {
expect(instance.currPage).toEqual(newPage);
expect(instance.forceUpdate).toHaveBeenCalledTimes(0);
});
it('should call handleRemotePageChange correctly', () => {
expect(handleRemotePaginationChange).toHaveBeenCalledTimes(1);
expect(handleRemotePaginationChange)
.toHaveBeenCalledWith(newPage, instance.currSizePerPage);
});
});
});
describe('handleChangeSizePerPage', () => {
let instance;
const newPage = 2;
const newSizePerPage = 15;
describe('should update component correctly', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
jest.spyOn(instance, 'forceUpdate');
instance.handleChangeSizePerPage(newSizePerPage, newPage);
});
it('', () => {
expect(instance.currPage).toEqual(newPage);
expect(instance.currSizePerPage).toEqual(newSizePerPage);
expect(instance.forceUpdate).toHaveBeenCalledTimes(1);
});
});
describe('if options.onSizePerPageChange is defined', () => {
const onSizePerPageChange = jest.fn();
beforeEach(() => {
onSizePerPageChange.mockClear();
wrapper = shallow(shallowContext({
...defaultPagination,
onSizePerPageChange
}));
instance = wrapper.instance();
jest.spyOn(instance, 'forceUpdate');
instance.handleChangeSizePerPage(newSizePerPage, newPage);
});
it('should still update component correctly', () => {
expect(instance.currPage).toEqual(newPage);
expect(instance.currSizePerPage).toEqual(newSizePerPage);
expect(instance.forceUpdate).toHaveBeenCalledTimes(1);
});
it('should call options.onSizePerPageChange correctly', () => {
expect(onSizePerPageChange).toHaveBeenCalledTimes(1);
expect(onSizePerPageChange).toHaveBeenCalledWith(newSizePerPage, newPage);
});
});
describe('if remote pagination is enable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination
}, true));
instance = wrapper.instance();
jest.spyOn(instance, 'forceUpdate');
instance.handleChangeSizePerPage(newSizePerPage, newPage);
});
it('should still update component correctly', () => {
expect(instance.currPage).toEqual(newPage);
expect(instance.currSizePerPage).toEqual(newSizePerPage);
expect(instance.forceUpdate).toHaveBeenCalledTimes(0);
});
it('should call handleRemotePageChange correctly', () => {
expect(handleRemotePaginationChange).toHaveBeenCalledTimes(1);
expect(handleRemotePaginationChange)
.toHaveBeenCalledWith(newPage, newSizePerPage);
});
});
});
describe('when options.page is defined', () => {
const page = 3;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
page
}));
wrapper.render();
});
it('should set correct currPage', () => {
expect(wrapper.instance().currPage).toEqual(page);
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('currPage')).toEqual(page);
});
});
describe('when options.sizePerPage is defined', () => {
const sizePerPage = Const.SIZE_PER_PAGE_LIST[2];
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
sizePerPage
}));
wrapper.render();
});
it('should set correct currSizePerPage', () => {
expect(wrapper.instance().currSizePerPage).toEqual(sizePerPage);
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('currSizePerPage')).toEqual(sizePerPage);
});
});
describe('when options.totalSize is defined', () => {
const totalSize = 100;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
totalSize
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('dataSize')).toEqual(totalSize);
});
});
describe('when options.showTotal is defined', () => {
const showTotal = true;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
showTotal
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('showTotal')).toEqual(showTotal);
});
});
describe('when options.pageStartIndex is defined', () => {
const pageStartIndex = -1;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
pageStartIndex
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('pageStartIndex')).toEqual(pageStartIndex);
});
});
describe('when options.sizePerPageList is defined', () => {
const sizePerPageList = [10, 40];
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
sizePerPageList
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('sizePerPageList')).toEqual(sizePerPageList);
});
});
describe('when options.paginationSize is defined', () => {
const paginationSize = 10;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
paginationSize
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('paginationSize')).toEqual(paginationSize);
});
});
describe('when options.withFirstAndLast is defined', () => {
const withFirstAndLast = false;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
withFirstAndLast
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('withFirstAndLast')).toEqual(withFirstAndLast);
});
});
describe('when options.alwaysShowAllBtns is defined', () => {
const alwaysShowAllBtns = true;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
alwaysShowAllBtns
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('alwaysShowAllBtns')).toEqual(alwaysShowAllBtns);
});
});
describe('when options.firstPageText is defined', () => {
const firstPageText = '1st';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
firstPageText
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('firstPageText')).toEqual(firstPageText);
});
});
describe('when options.prePageText is defined', () => {
const prePageText = 'PRE';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
prePageText
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('prePageText')).toEqual(prePageText);
});
});
describe('when options.nextPageText is defined', () => {
const nextPageText = 'NEXT';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
nextPageText
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('nextPageText')).toEqual(nextPageText);
});
});
describe('when options.lastPageText is defined', () => {
const lastPageText = 'LAST';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
lastPageText
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('lastPageText')).toEqual(lastPageText);
});
});
describe('when options.firstPageTitle is defined', () => {
const firstPageTitle = '1st';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
firstPageTitle
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('firstPageTitle')).toEqual(firstPageTitle);
});
});
describe('when options.prePageTitle is defined', () => {
const prePageTitle = 'PRE';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
prePageTitle
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('prePageTitle')).toEqual(prePageTitle);
});
});
describe('when options.nextPageTitle is defined', () => {
const nextPageTitle = 'NEXT';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
nextPageTitle
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('nextPageTitle')).toEqual(nextPageTitle);
});
});
describe('when options.lastPageTitle is defined', () => {
const lastPageTitle = 'nth';
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
lastPageTitle
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('lastPageTitle')).toEqual(lastPageTitle);
});
});
describe('when options.hideSizePerPage is defined', () => {
const hideSizePerPage = true;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
hideSizePerPage
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('hideSizePerPage')).toEqual(hideSizePerPage);
});
});
describe('when options.hidePageListOnlyOnePage is defined', () => {
const hidePageListOnlyOnePage = true;
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
hidePageListOnlyOnePage
}));
wrapper.render();
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('hidePageListOnlyOnePage')).toEqual(hidePageListOnlyOnePage);
});
});
});

View File

@@ -0,0 +1,208 @@
import 'jsdom-global/register';
import React from 'react';
import { shallow } from 'enzyme';
import paginationFactory from '../index';
import Const from '../src/const';
import createStateContext from '../src/data-context';
import Pagination from '../src/pagination';
import { getByCurrPage } from '../src/page';
const data = [];
for (let i = 0; i < 100; i += 1) {
data.push({
id: i,
name: `itme name ${i}`
});
}
describe('PaginationDataContext', () => {
let wrapper;
let PaginationDataContext;
const defaultPagination = { options: { totalSize: data.length }, createContext: jest.fn() };
const MockComponent = () => null;
const renderMockComponent = jest.fn((props => (
<MockComponent { ...props } />
)));
const handleRemotePaginationChange = jest.fn();
function shallowContext(
customPagination = defaultPagination,
remoteEnabled = false
) {
renderMockComponent.mockReset();
handleRemotePaginationChange.mockReset();
PaginationDataContext = createStateContext();
const isRemotePagination = jest.fn().mockReturnValue(remoteEnabled);
const remoteEmitter = { emit: jest.fn() };
return (
<PaginationDataContext.Provider
pagination={ paginationFactory(customPagination) }
data={ data }
remoteEmitter={ remoteEmitter }
isRemotePagination={ isRemotePagination }
>
<PaginationDataContext.Consumer>
{
paginationProps => renderMockComponent(paginationProps)
}
</PaginationDataContext.Consumer>
</PaginationDataContext.Provider>
);
}
describe('default render', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.render();
});
it('should have correct Provider property after calling createPaginationDataContext', () => {
expect(PaginationDataContext.Provider).toBeDefined();
});
it('should have correct Consumer property after calling createPaginationDataContext', () => {
expect(PaginationDataContext.Consumer).toBeDefined();
});
it('should have correct currPage', () => {
expect(wrapper.instance().currPage).toEqual(Const.PAGE_START_INDEX);
});
it('should have correct currSizePerPage', () => {
expect(wrapper.instance().currSizePerPage).toEqual(Const.SIZE_PER_PAGE_LIST[0]);
});
it('should render correct data props to childrens', () => {
const instance = wrapper.instance();
expect(renderMockComponent).toHaveBeenCalledTimes(1);
expect(renderMockComponent).toHaveBeenCalledWith({
data: getByCurrPage(
data,
instance.currPage,
instance.currSizePerPage,
Const.PAGE_START_INDEX
),
setRemoteEmitter: instance.setRemoteEmitter
});
});
});
describe('default render', () => {
describe('when options.custom is negative', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.render();
});
it('should render Pagination component correctly', () => {
const instance = wrapper.instance();
const pagination = wrapper.find(Pagination);
expect(pagination).toHaveLength(1);
expect(pagination.prop('dataSize')).toEqual(data.length);
expect(pagination.prop('currPage')).toEqual(instance.currPage);
expect(pagination.prop('currSizePerPage')).toEqual(instance.currSizePerPage);
expect(pagination.prop('onPageChange')).toEqual(instance.handleChangePage);
expect(pagination.prop('onSizePerPageChange')).toEqual(instance.handleChangeSizePerPage);
expect(pagination.prop('sizePerPageList')).toEqual(Const.SIZE_PER_PAGE_LIST);
expect(pagination.prop('paginationSize')).toEqual(Const.PAGINATION_SIZE);
expect(pagination.prop('pageStartIndex')).toEqual(Const.PAGE_START_INDEX);
expect(pagination.prop('withFirstAndLast')).toEqual(Const.With_FIRST_AND_LAST);
expect(pagination.prop('alwaysShowAllBtns')).toEqual(Const.SHOW_ALL_PAGE_BTNS);
expect(pagination.prop('firstPageText')).toEqual(Const.FIRST_PAGE_TEXT);
expect(pagination.prop('prePageText')).toEqual(Const.PRE_PAGE_TEXT);
expect(pagination.prop('nextPageText')).toEqual(Const.NEXT_PAGE_TEXT);
expect(pagination.prop('lastPageText')).toEqual(Const.LAST_PAGE_TEXT);
expect(pagination.prop('firstPageTitle')).toEqual(Const.FIRST_PAGE_TITLE);
expect(pagination.prop('prePageTitle')).toEqual(Const.PRE_PAGE_TITLE);
expect(pagination.prop('nextPageTitle')).toEqual(Const.NEXT_PAGE_TITLE);
expect(pagination.prop('lastPageTitle')).toEqual(Const.LAST_PAGE_TITLE);
expect(pagination.prop('hideSizePerPage')).toEqual(Const.HIDE_SIZE_PER_PAGE);
expect(pagination.prop('hideSizePerPage')).toEqual(Const.HIDE_SIZE_PER_PAGE);
expect(pagination.prop('paginationTotalRenderer')).toBeUndefined();
});
});
describe('when options.custom is positive', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
custom: true
}));
wrapper.render();
});
it('should not render Pagination component', () => {
const pagination = wrapper.find(Pagination);
expect(pagination).toHaveLength(0);
});
});
});
describe('when remote pagination enabled', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({}, true));
wrapper.render();
});
it('just pass data props to children', () => {
const instance = wrapper.instance();
expect(renderMockComponent).toHaveBeenCalledTimes(1);
expect(renderMockComponent).toHaveBeenCalledWith({
data: instance.props.data,
setRemoteEmitter: instance.setRemoteEmitter
});
});
});
describe('componentWillReceiveProps', () => {
let instance;
let nextProps;
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);
});
});
});
});
});

View File

@@ -46,8 +46,6 @@ describe('PageResolver', () => {
expect(instance.state.lastPage).toBeDefined();
expect(instance.state.lastPage).toEqual(
instance.calculateLastPage(instance.state.totalPages));
expect(instance.state.dropdownOpen).toBeDefined();
expect(instance.state.dropdownOpen).toBeFalsy();
});
});
@@ -81,35 +79,6 @@ describe('PageResolver', () => {
});
});
describe('goToNextPage', () => {
const props = createMockProps();
describe('when props.currPage is not hitting state.lastPage', () => {
beforeEach(() => {
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
});
it('should getting previous page correctly', () => {
const instance = wrapper.instance();
expect(instance.goToNextPage()).toEqual(props.currPage + 1);
});
});
describe('when props.currPage is hitting state.lastpage', () => {
beforeEach(() => {
props.currPage = 10;
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
});
it('should always getting page which must eq props.pageStartIndex', () => {
const instance = wrapper.instance();
expect(instance.goToNextPage()).toEqual(instance.state.lastPage);
});
});
});
describe('calculateFromTo', () => {
const props = createMockProps();
beforeEach(() => {
@@ -192,7 +161,7 @@ describe('PageResolver', () => {
});
describe('calculatePages', () => {
describe('calculate by state.totalPages and state.lastPage', () => {
describe('calculate by totalPages and lastPage', () => {
const props = createMockProps();
beforeEach(() => {
const mockElement = React.createElement(MockComponent, props, null);
@@ -201,7 +170,7 @@ describe('PageResolver', () => {
it('should getting pages list correctly', () => {
const instance = wrapper.instance();
expect(instance.calculatePages()).toEqual(
expect(instance.calculatePages(instance.state.totalPages, instance.state.lastPage)).toEqual(
[props.prePageText, 1, 2, 3, 4, 5, props.nextPageText, props.lastPageText]);
expect(instance.calculatePages(4, 4)).toEqual(
@@ -218,7 +187,9 @@ describe('PageResolver', () => {
currPages.forEach((currPage) => {
props.currPage = currPage + 1;
wrapper = shallow(<MockComponent { ...props } />);
const pageList = wrapper.instance().calculatePages();
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
if (props.currPage < 4) {
expect(pageList).toEqual(
@@ -253,7 +224,9 @@ describe('PageResolver', () => {
[1, 3, 5, 8, 10].forEach((paginationSize) => {
props.paginationSize = paginationSize;
wrapper = shallow(<MockComponent { ...props } />);
const pageList = wrapper.instance().calculatePages();
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
const result = pageList.filter(p => indicators.indexOf(p) === -1);
expect(result.length).toEqual(props.paginationSize);
});
@@ -267,7 +240,9 @@ describe('PageResolver', () => {
[1, 2, 3, 4, 5, 6, 7].forEach((currPage) => {
props.currPage = currPage;
wrapper = shallow(<MockComponent { ...props } />);
const pageList = wrapper.instance().calculatePages();
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
expect(pageList.indexOf(props.lastPageText) > -1).toBeTruthy();
});
});
@@ -278,7 +253,9 @@ describe('PageResolver', () => {
[10, 9, 8, 7, 6, 5, 4].forEach((currPage) => {
props.currPage = currPage;
wrapper = shallow(<MockComponent { ...props } />);
const pageList = wrapper.instance().calculatePages();
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
expect(pageList.indexOf(props.firstPageText) > -1).toBeTruthy();
});
});
@@ -293,7 +270,9 @@ describe('PageResolver', () => {
props.currPage = currPage + 1;
props.withFirstAndLast = false;
wrapper = shallow(<MockComponent { ...props } />);
const pageList = wrapper.instance().calculatePages();
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
expect(pageList.indexOf(props.lastPageText) > -1).toBeFalsy();
expect(pageList.indexOf(props.firstPageText) > -1).toBeFalsy();
});
@@ -311,7 +290,9 @@ describe('PageResolver', () => {
});
it('should getting last page correctly', () => {
const pageList = wrapper.instance().calculatePages();
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
expect(pageList).toEqual(
[props.prePageText, -2, -1, 0, 1, 2, props.nextPageText, props.lastPageText]);
});
@@ -329,7 +310,9 @@ describe('PageResolver', () => {
});
it('should always having next and previous page indication', () => {
const pageList = wrapper.instance().calculatePages();
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
expect(pageList.indexOf(props.nextPageText) > -1).toBeTruthy();
expect(pageList.indexOf(props.prePageText) > -1).toBeTruthy();
});
@@ -345,7 +328,10 @@ describe('PageResolver', () => {
});
it('should getting empty array', () => {
expect(wrapper.instance().calculatePages()).toEqual([]);
const instance = wrapper.instance();
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
expect(pageList).toEqual([]);
});
});
});
@@ -360,7 +346,9 @@ describe('PageResolver', () => {
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
instance = wrapper.instance();
pageStatus = instance.calculatePageStatus(instance.calculatePages());
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
pageStatus = instance.calculatePageStatus(pageList, instance.state.lastPage);
});
it('should returning correct format for page status', () => {
@@ -388,8 +376,9 @@ describe('PageResolver', () => {
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
instance = wrapper.instance();
const pageList = instance.calculatePages();
pageStatus = instance.calculatePageStatus(pageList);
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
pageStatus = instance.calculatePageStatus(pageList, instance.state.lastPage);
expect(pageStatus.find(p => p.page === props.prePageText)).not.toBeDefined();
});
@@ -401,8 +390,9 @@ describe('PageResolver', () => {
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
instance = wrapper.instance();
const pageList = instance.calculatePages();
pageStatus = instance.calculatePageStatus(pageList);
const pageList = instance.calculatePages(
instance.state.totalPages, instance.state.lastPage);
pageStatus = instance.calculatePageStatus(pageList, instance.state.lastPage);
expect(pageStatus.find(p => p.page === props.nextPageText)).not.toBeDefined();
});

View File

@@ -43,32 +43,132 @@ describe('Page Functions', () => {
});
describe('alignPage', () => {
const pageStartIndex = 1;
const sizePerPage = 10;
const page = 3;
describe('if the page does not fit the pages interval calculated from the length of store.data', () => {
let newDataSize;
let prevDataSize;
let currPage;
let pageStartIndex;
let sizePerPage;
describe('if prevDataSize < newDataSize', () => {
beforeEach(() => {
data = [];
for (let i = 0; i < 15; i += 1) {
data.push({ id: i, name: `test_name${i}` });
}
newDataSize = 10;
prevDataSize = 6;
currPage = 2;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return pageStartIndex argument', () => {
expect(alignPage(data, page, sizePerPage, pageStartIndex)).toEqual(pageStartIndex);
it('should return same page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(currPage);
});
});
describe('if the length of store.data is large than the end page index', () => {
describe('if currPage < newDataSize', () => {
beforeEach(() => {
data = [];
for (let i = 0; i < 30; i += 1) {
data.push({ id: i, name: `test_name${i}` });
}
newDataSize = 10;
prevDataSize = 12;
currPage = 0;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return current page', () => {
expect(alignPage(data, page, sizePerPage, pageStartIndex)).toEqual(page);
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(pageStartIndex);
});
});
describe('if partStartIndex is default 1', () => {
describe('and currPage is bigger than newest last page', () => {
beforeEach(() => {
newDataSize = 9;
prevDataSize = 12;
currPage = 3;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(2);
});
});
describe('and currPage is short than newest last page', () => {
beforeEach(() => {
newDataSize = 11;
prevDataSize = 12;
currPage = 3;
pageStartIndex = 1;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(currPage);
});
});
});
describe('if partStartIndex is default 0', () => {
describe('and currPage is bigger than newest last page', () => {
beforeEach(() => {
newDataSize = 8;
prevDataSize = 11;
currPage = 2;
pageStartIndex = 0;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(1);
});
});
describe('and currPage is short than newest last page', () => {
beforeEach(() => {
newDataSize = 11;
prevDataSize = 12;
currPage = 2;
pageStartIndex = 0;
sizePerPage = 5;
});
it('should return correct page', () => {
expect(alignPage(
newDataSize,
prevDataSize,
currPage,
sizePerPage,
pageStartIndex
)).toEqual(currPage);
});
});
});
});

View File

@@ -0,0 +1,203 @@
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import paginationHandler from '../src/pagination-handler';
const MockComponent = () => null;
const MockComponentWithPaginationHandler = paginationHandler(MockComponent);
describe('paginationHandler', () => {
let wrapper;
let instance;
const createMockProps = props => ({
dataSize: 100,
sizePerPageList: [10, 20, 30, 50],
currPage: 1,
currSizePerPage: 10,
pageStartIndex: 1,
paginationSize: 5,
withFirstAndLast: true,
firstPageText: '<<',
prePageText: '<',
nextPageText: '>',
lastPageText: '>>',
alwaysShowAllBtns: false,
onPageChange: sinon.stub(),
onSizePerPageChange: sinon.stub(),
hidePageListOnlyOnePage: false,
hideSizePerPage: false,
...props
});
describe('default pagiantion', () => {
const props = createMockProps();
beforeEach(() => {
wrapper = shallow(<MockComponentWithPaginationHandler { ...props } />);
instance = wrapper.instance();
});
it('should having correct state', () => {
expect(instance.state).toBeDefined();
expect(instance.state.totalPages).toEqual(instance.calculateTotalPage());
expect(instance.state.lastPage).toEqual(
instance.calculateLastPage(instance.state.totalPages));
});
it('should rendering PaginationList component successfully', () => {
const wrapperedComponent = wrapper.find(MockComponent);
expect(wrapperedComponent.length).toBe(1);
expect(wrapperedComponent.props().lastPage).toEqual(instance.state.lastPage);
expect(wrapperedComponent.props().totalPages).toEqual(instance.state.totalPages);
expect(wrapperedComponent.props().onPageChange).toEqual(instance.handleChangePage);
expect(wrapperedComponent.props().onSizePerPageChange)
.toEqual(instance.handleChangeSizePerPage);
});
});
describe('handleChangePage', () => {
const props = createMockProps();
beforeEach(() => {
props.currPage = 6;
wrapper = shallow(<MockComponentWithPaginationHandler { ...props } />);
instance = wrapper.instance();
});
afterEach(() => {
props.onPageChange.reset();
});
it('should calling props.onPageChange correctly when new page is eq props.prePageText', () => {
instance.handleChangePage(props.prePageText);
expect(props.onPageChange.callCount).toBe(1);
expect(props.onPageChange.calledWith(5)).toBeTruthy();
});
it('should calling props.onPageChange correctly when new page is eq props.nextPageText', () => {
instance.handleChangePage(props.nextPageText);
expect(props.onPageChange.callCount).toBe(1);
expect(props.onPageChange.calledWith(7)).toBeTruthy();
});
it('should calling props.onPageChange correctly when new page is eq props.lastPageText', () => {
instance.handleChangePage(props.lastPageText);
expect(props.onPageChange.callCount).toBe(1);
expect(props.onPageChange.calledWith(10)).toBeTruthy();
});
it('should calling props.onPageChange correctly when new page is eq props.firstPageText', () => {
instance.handleChangePage(props.firstPageText);
expect(props.onPageChange.callCount).toBe(1);
expect(props.onPageChange.calledWith(props.pageStartIndex)).toBeTruthy();
});
it('should calling props.onPageChange correctly when new page is a numeric page', () => {
const newPage = '8';
instance.handleChangePage(newPage);
expect(props.onPageChange.callCount).toBe(1);
expect(props.onPageChange.calledWith(parseInt(newPage, 10))).toBeTruthy();
});
it('should not calling props.onPageChange correctly when page is not changed', () => {
const newPage = props.currPage;
instance.handleChangePage(newPage);
expect(props.onPageChange.callCount).toBe(0);
});
});
describe('handleChangeSizePerPage', () => {
const props = createMockProps();
beforeEach(() => {
wrapper = shallow(<MockComponentWithPaginationHandler { ...props } />);
instance = wrapper.instance();
});
it('should always setting state.dropdownOpen to false', () => {
instance.handleChangeSizePerPage(10);
expect(instance.state.dropdownOpen).toBeFalsy();
});
describe('when new sizePerPage is same as current one', () => {
it('should not calling props.onSizePerPageChange callback', () => {
instance.handleChangeSizePerPage(10);
expect(props.onSizePerPageChange.callCount).toBe(0);
});
});
describe('when new sizePerPage is diff than current one', () => {
it('should not calling props.onSizePerPageChange callback', () => {
instance.handleChangeSizePerPage(30);
expect(props.onSizePerPageChange.callCount).toBe(1);
});
describe('and new current page is still in the new lagination list', () => {
it('should calling props.onSizePerPageChange with correct argument', () => {
expect(props.onSizePerPageChange.calledWith(30, props.currPage));
});
});
describe('and new current page is still in the new lagination list', () => {
beforeEach(() => {
wrapper = shallow(
<MockComponentWithPaginationHandler { ...createMockProps({ currPage: 10 }) } />);
instance = wrapper.instance();
});
it('should calling props.onSizePerPageChange with correct argument', () => {
expect(props.onSizePerPageChange.calledWith(30, 4));
});
});
});
});
describe('componentWillReceiveProps', () => {
describe('when next props.currSizePerPage is diff than current one', () => {
const nextProps = createMockProps({ currSizePerPage: 20 });
beforeEach(() => {
wrapper = shallow(<MockComponentWithPaginationHandler { ...createMockProps() } />);
instance = wrapper.instance();
});
it('should setting correct state.totalPages', () => {
instance.componentWillReceiveProps(nextProps);
expect(instance.state.totalPages).toEqual(
instance.calculateTotalPage(nextProps.currSizePerPage));
});
it('should setting correct state.lastPage', () => {
instance.componentWillReceiveProps(nextProps);
const totalPages = instance.calculateTotalPage(nextProps.currSizePerPage);
expect(instance.state.lastPage).toEqual(
instance.calculateLastPage(totalPages));
});
});
describe('when next props.dataSize is diff than current one', () => {
const nextProps = createMockProps({ dataSize: 33 });
beforeEach(() => {
wrapper = shallow(<MockComponentWithPaginationHandler { ...createMockProps() } />);
instance = wrapper.instance();
});
it('should setting correct state.totalPages', () => {
instance.componentWillReceiveProps(nextProps);
expect(instance.state.totalPages).toEqual(
instance.calculateTotalPage(nextProps.currSizePerPage, nextProps.dataSize));
});
it('should setting correct state.lastPage', () => {
instance.componentWillReceiveProps(nextProps);
const totalPages = instance.calculateTotalPage(
nextProps.currSizePerPage, nextProps.dataSize);
expect(instance.state.lastPage).toEqual(
instance.calculateLastPage(totalPages));
});
});
});
});

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { shallow } from 'enzyme';
import paginationListAdapter from '../src/pagination-list-adapter';
const MockComponent = () => null;
const PaginationListAdapter = paginationListAdapter(MockComponent);
describe('paginationListAdapter', () => {
let wrapper;
const props = {
totalPages: 10,
lastPage: 10,
pageButtonRenderer: jest.fn(),
onPageChange: jest.fn()
};
describe('render', () => {
beforeEach(() => {
wrapper = shallow(<PaginationListAdapter { ...props } />);
});
it('should render successfully', () => {
const mockComponent = wrapper.find(MockComponent);
expect(mockComponent).toHaveLength(1);
expect(mockComponent.props().pages).toBeDefined();
expect(mockComponent.props().pageButtonRenderer).toBeDefined();
expect(mockComponent.props().onPageChange).toBeDefined();
});
});
});

View File

@@ -39,4 +39,22 @@ describe('PaginationList', () => {
expect(wrapper.find('ul.react-bootstrap-table-page-btns-ul').length).toBe(1);
expect(wrapper.find(PageButton).length).toBe(pages.length);
});
describe('when props.pageButtonRenderer is existing', () => {
const pageButtonRenderer = jest.fn().mockReturnValue(null);
beforeEach(() => {
wrapper = shallow(
<PaginationList
pages={ pages }
onPageChange={ onPageChange }
pageButtonRenderer={ pageButtonRenderer }
/>
);
});
it('should call props.pageButtonRenderer correctly', () => {
expect(wrapper.length).toBe(1);
expect(pageButtonRenderer).toHaveBeenCalledTimes(pages.length);
});
});
});

View File

@@ -0,0 +1,34 @@
import React from 'react';
import { shallow } from 'enzyme';
import paginationTotalAdapter from '../src/pagination-total-adapter';
const MockComponent = () => null;
const PaginationTotalAdapter = paginationTotalAdapter(MockComponent);
describe('paginationTotalAdapter', () => {
let wrapper;
const props = {
dataSize: 20,
currPage: 1,
currSizePerPage: 10,
paginationTotalRenderer: jest.fn()
};
describe('render', () => {
beforeEach(() => {
wrapper = shallow(<PaginationTotalAdapter { ...props } />);
});
it('should render successfully', () => {
const mockComponent = wrapper.find(MockComponent);
expect(mockComponent).toHaveLength(1);
expect(mockComponent.props().from).toBeDefined();
expect(mockComponent.props().to).toBeDefined();
expect(mockComponent.props().dataSize).toEqual(props.dataSize);
expect(mockComponent.props().paginationTotalRenderer).toEqual(props.paginationTotalRenderer);
});
});
});

Some files were not shown because too many files have changed in this diff Show More