Compare commits

..

225 Commits

Author SHA1 Message Date
AllenFang
345cb83493 Publish
- react-bootstrap-table2-example@1.0.2
 - react-bootstrap-table2-paginator@1.0.2
 - react-bootstrap-table2-toolkit@1.0.2
 - react-bootstrap-table-next@1.1.2
2018-08-21 17:05:08 +08:00
Allen
6e526f455b Merge pull request #500 from react-bootstrap-table/develop
20180821 release
2018-08-21 17:03:00 +08:00
AllenFang
b05cf48f36 Merge branch 'COzero-issue-363' into develop 2018-08-20 23:36:42 +08:00
AllenFang
28249c9089 fixed version 2018-08-20 23:36:22 +08:00
AllenFang
2b6081ab31 Merge branch 'issue-363' of https://github.com/COzero/react-bootstrap-table2 into COzero-issue-363 2018-08-20 23:23:32 +08:00
AllenFang
c935447266 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2018-08-20 22:59:36 +08:00
AllenFang
ba1d6fa3ed fix #498 2018-08-20 22:59:14 +08:00
Allen
1b3b68f8a7 fix controlled page state will effect the internal page state when internal table rerender (#492) 2018-08-19 12:43:52 +08:00
copas2
4a3486cc3c fix #488
* updated test for pagination fix

* suspected bug in page.js

Local pagination aligning returns start page when eg editing cell locally. This results in state change, too. Not really getting the purpose of the original idea. Please consider this modification. It only checks if page fits in the data size range. As I saw, this alignment only affects local pagination, not remote.

* modified fix w/ pageStartIndex

I forgot to mind pageStartIndex
2018-08-16 17:16:47 +08:00
Allen
cb49455a4e Merge pull request #489 from react-bootstrap-table/bugfix/infinite-remote-search
Bugfix/infinite remote search
2018-08-15 23:16:52 +08:00
AllenFang
5e63d6ae59 avoid infinite remote search 2018-08-15 23:08:46 +08:00
AllenFang
e3ca6f2c24 Publish
- react-bootstrap-table-next@1.1.1
2018-08-13 22:01:15 +08:00
Jeremy Nagel
96d33a60ba Add snapshot tests 2018-08-13 16:50:43 +10:00
Jeremy Nagel
03389aece0 Merge branch 'master' of github.com:react-bootstrap-table/react-bootstrap-table2 into issue-363 2018-08-13 16:45:34 +10:00
AllenFang
f86876ba51 Publish
- react-bootstrap-table2-example@1.0.1
 - react-bootstrap-table2-paginator@1.0.1
 - react-bootstrap-table2-toolkit@1.0.1
 - react-bootstrap-table-next@1.1.0
2018-08-12 14:41:44 +08:00
Allen
44569d6df9 Merge pull request #482 from react-bootstrap-table/develop
20180812 release
2018-08-12 14:39:43 +08:00
AllenFang
3663d1d4fe fix Warning: Failed prop type: Invalid prop of type supplied to , expected 2018-08-12 13:53:29 +08:00
Allen
6225f0e5cb fix #471 (#481) 2018-08-12 13:32:12 +08:00
Allen
f0fd06a5f5 fix #465 (#470) 2018-08-11 15:17:41 +08:00
Allen
b181c98a38 Merge pull request #449 from react-bootstrap-table/enhance/performance-cell-level
Enhance/performance on cell level
2018-08-11 15:17:15 +08:00
Allen
1f51f1a08d fix #464 (#469) 2018-08-11 15:03:17 +08:00
Allen
586acaed68 fix #455 (#468) 2018-08-11 13:33:52 +08:00
Allen
528be5c058 fix #453 (#467) 2018-08-11 13:32:44 +08:00
AllenFang
76575bd9f1 patch docs 2018-08-05 16:32:27 +08:00
AllenFang
54b98f41f4 implement shouldComponentUpdate on cell component 2018-08-05 16:05:22 +08:00
AllenFang
ffac3a42c5 add a large table example 2018-08-05 16:05:22 +08:00
AllenFang
97b9e1097b fix wrong title 2018-08-05 16:05:22 +08:00
AllenFang
a6de7fa84a integrate with underscroe.js 2018-08-05 16:05:22 +08:00
AllenFang
3ec849bd94 Publish
- react-bootstrap-table2-editor@1.0.0
 - react-bootstrap-table2-example@1.0.0
 - react-bootstrap-table2-filter@1.0.0
 - react-bootstrap-table2-overlay@1.0.0
 - react-bootstrap-table2-paginator@1.0.0
 - react-bootstrap-table2-toolkit@1.0.0
 - react-bootstrap-table-next@1.0.0
2018-08-04 16:03:04 +08:00
Allen
208feb9849 Merge pull request #448 from react-bootstrap-table/develop
1.0.0
2018-08-04 15:57:10 +08:00
Allen
a3ba464f40 Merge pull request #333 from react-bootstrap-table/refactor/context-api
Migrate to React@16.3 for Context API
2018-08-04 14:56:13 +08:00
AllenFang
cb970cded5 fix peerdep 2018-08-04 14:47:46 +08:00
AllenFang
d5d8c54d98 fix React doesn't allow Date Object as children 2018-08-01 20:26:01 +08:00
AllenFang
2ec55f6de9 patch for default sort and filter have potential issue when remote 2018-08-01 20:26:01 +08:00
AllenFang
f7406bcafc add toolkits styles 2018-08-01 20:26:01 +08:00
AllenFang
925d3d7841 final docs patch 2018-08-01 20:26:00 +08:00
AllenFang
62c69490f2 add bootstrap4 style links 2018-08-01 20:26:00 +08:00
AllenFang
3f957db56b fix pagination broken when bootstrap4 2018-08-01 20:26:00 +08:00
AllenFang
495875792f refine new context API tests 2018-08-01 20:26:00 +08:00
AllenFang
c0416fc307 fix selection column broken when bootstrap4 2018-08-01 20:26:00 +08:00
AllenFang
f7ba8e377d upgrade enzyme 2018-08-01 20:26:00 +08:00
AllenFang
0d64443b26 fix sort caret broken on bootstrap4 2018-08-01 20:26:00 +08:00
AllenFang
7919a4001d enhance for #402 2018-08-01 20:26:00 +08:00
AllenFang
fadbcdaa24 a workaround for fixing the _ module missing 2018-08-01 20:26:00 +08:00
AllenFang
ec77a0539d patch docs for export CSV 2018-08-01 20:26:00 +08:00
AllenFang
b792803974 add export csv stories 2018-08-01 20:26:00 +08:00
AllenFang
f0e37b130c implement export csv 2018-08-01 20:26:00 +08:00
AllenFang
03ece4b1fc refactoring search 2018-08-01 20:26:00 +08:00
AllenFang
0ec5b6cb9f add simple toolkit context wrapper for user 2018-08-01 20:26:00 +08:00
AllenFang
e6d4a9641b data, keyField and columns is necessary value for toolkits context 2018-08-01 20:26:00 +08:00
AllenFang
5a442bf7ed add missing expandRow props 2018-08-01 20:26:00 +08:00
AllenFang
a18932e9eb patch docs for expand row 2018-08-01 20:26:00 +08:00
AllenFang
c36aa24c65 fix filter context tests broken due to missing onExternalFilter 2018-08-01 20:26:00 +08:00
AllenFang
81ddd2c25b add stories for expand indicator 2018-08-01 20:26:00 +08:00
AllenFang
4af5b4f6ef implement expand indicator 2018-08-01 20:26:00 +08:00
AllenFang
dbd0f89a3d add stories for expand row 2018-08-01 20:26:00 +08:00
AllenFang
35b1e37940 implement expand row sketch 2018-08-01 20:26:00 +08:00
AllenFang
6eaffe1993 patch docs for table search 2018-08-01 20:26:00 +08:00
AllenFang
46f0ce493b prepare builds for react-bootstrap-table2-toolkit 2018-08-01 20:26:00 +08:00
AllenFang
18b785d655 update peer dependencies for react react-dom 2018-08-01 20:26:00 +08:00
AllenFang
7b15bf45d9 patch tests for search 2018-08-01 20:26:00 +08:00
AllenFang
0d4d32c6e4 patch for remote cell edit 2018-08-01 20:26:00 +08:00
AllenFang
760d459414 add example for tble search 2018-08-01 20:26:00 +08:00
AllenFang
77301c2cf1 implement table search 2018-08-01 20:26:00 +08:00
AllenFang
78ea63074e fix bug for default sort and filter have potential issue when remote is enable 2018-08-01 20:26:00 +08:00
AllenFang
9c677fe174 enhance remote all example 2018-08-01 20:26:00 +08:00
AllenFang
c13b3fa197 patch test for editor, filter, pagination 2018-08-01 20:26:00 +08:00
AllenFang
167352f199 fix selectRow doesnt pass to CellEditContext 2018-08-01 20:17:18 +08:00
AllenFang
fc0b99e8a0 patch tests for react-bootstrap-table-next 2018-08-01 20:17:18 +08:00
AllenFang
74bf885d47 remove useless code 2018-08-01 20:17:17 +08:00
AllenFang
400c307871 refine remote method 2018-08-01 20:17:17 +08:00
AllenFang
b1c086f424 fix cache context issue 2018-08-01 20:17:17 +08:00
AllenFang
d534c425d3 change to partial selection when pagination enabled 2018-08-01 20:17:17 +08:00
AllenFang
4ecf2433d0 no more state anti-pattern 2018-08-01 20:17:17 +08:00
AllenFang
6c086c3892 implement pagination context 2018-08-01 20:17:17 +08:00
AllenFang
1e72c80566 construct context dynamically 2018-08-01 20:17:17 +08:00
AllenFang
8f4dc9907a implement filter context 2018-08-01 20:17:17 +08:00
AllenFang
2f7d0104a0 add clear all filter story 2018-08-01 20:14:33 +08:00
AllenFang
4f6809de84 fix custom filter value example broken 2018-08-01 20:14:33 +08:00
AllenFang
216bc10142 implement celledit context 2018-08-01 20:14:33 +08:00
AllenFang
5307e58813 implement data operator 2018-08-01 20:14:33 +08:00
AllenFang
143acde35e refactoring remote sort 2018-08-01 20:14:33 +08:00
AllenFang
2525465a5a implement sort context 2018-08-01 20:14:33 +08:00
AllenFang
6d08a24a8f implement selection context 2018-08-01 20:14:33 +08:00
AllenFang
906180ad3f implement context-based container 2018-08-01 20:14:33 +08:00
AllenFang
0ff0c33aa9 upgrade react and react-dom 2018-08-01 20:14:33 +08:00
AllenFang
37e79a654b Publish
- react-bootstrap-table2-example@0.1.12
 - react-bootstrap-table2-filter@0.3.2
2018-08-01 19:54:44 +08:00
Allen
4e7cfdf5ea Merge pull request #443 from react-bootstrap-table/develop
20180801 release
2018-08-01 19:53:18 +08:00
Allen
6f4e779a3e Merge pull request #442 from react-bootstrap-table/enhance/multi-select
Enhance/multi select
2018-07-31 17:03:26 +08:00
AllenFang
38d3e2df05 patch docs for multiselect filter 2018-07-31 16:57:34 +08:00
AllenFang
4e204f1ccd refine multiselect 2018-07-31 16:55:17 +08:00
AllenFang
7e29999b40 patch multiselect filter's stories 2018-07-31 16:54:48 +08:00
Allen
01cf69392f Merge pull request #438 from Ignalion/multiselectv2
Multiselectv2
2018-07-31 16:29:19 +08:00
AllenFang
7d7688582b Publish
- react-bootstrap-table2-filter@0.3.1
2018-07-30 23:27:23 +08:00
Allen
e26065b116 Merge pull request #440 from react-bootstrap-table/develop
20180730 release
2018-07-30 23:16:26 +08:00
ignalion
485503c54d Added correct handling of empty selection (first line) 2018-07-29 15:45:58 +03:00
ignalion
3c37716dd2 little fix for curly brace 2018-07-29 15:45:58 +03:00
ignalion
1a7f86a321 Added tests for multiselect filter 2018-07-29 15:45:58 +03:00
ignalion
475f8c67b0 Added some examples for multi-select filter 2018-07-29 15:45:58 +03:00
ignalion
26314254be Added multiselect filter (mostly copied from default select one) 2018-07-29 15:45:58 +03:00
Benny Johnson
6522f6d964 Commented out a check which caused the date filter to not update the (#425)
table when the date or date comparator were cleared

Modified applyFilter in date.js so that it doesn't try to parse an
Invalid Date. It was parsing an empty string, which caused it to pass
through an invalid date to onFilter, and this wasn't being checked for
properly by the onFilter function. It now checks for the empty string
and passes in null, which is what the onFilter function was actually
checking for
2018-07-25 11:20:35 +08:00
Allen
ecaf439e66 Merge pull request #420 from nthgol/patch-1
import numberFilter in numberFilter example
2018-07-19 10:15:57 +08:00
Nathan
90d03676ad import numberFilter in numberFilter example 2018-07-16 18:06:51 -04:00
Jeremy Nagel
f35d644608 [BUGFIX] Fix issue with missing onChange prop for selection checkbox 2018-07-16 11:18:02 +10:00
AllenFang
2585a62697 Publish
- react-bootstrap-table2-example@0.1.11
 - react-bootstrap-table2-paginator@0.1.6
 - react-bootstrap-table-next@0.1.15
2018-07-15 16:08:59 +08:00
Allen
6afe58a081 Merge pull request #415 from react-bootstrap-table/develop
20180715 release
2018-07-15 16:02:47 +08:00
Allen
6f5bd1a13d fix #394 (#414) 2018-07-15 14:43:29 +08:00
Allen
85a9ab72af fix #402 (#412) 2018-07-15 14:18:21 +08:00
AllenFang
258ea43225 Publish
- react-bootstrap-table2-example@0.1.10
 - react-bootstrap-table2-filter@0.3.0
 - react-bootstrap-table2-paginator@0.1.5
 - react-bootstrap-table-next@0.1.14
2018-06-24 22:42:50 +08:00
Allen
7a7b708029 Merge pull request #390 from react-bootstrap-table/develop
20180624 release
2018-06-24 22:40:15 +08:00
Allen
0cf89861af Merge pull request #389 from react-bootstrap-table/feat/custom-filter
Implement custom filter
2018-06-24 15:33:22 +08:00
AllenFang
eb74625835 patch docs for custom filter 2018-06-24 15:15:57 +08:00
AllenFang
cbaec4c655 add stories for custom filter 2018-06-24 15:04:59 +08:00
AllenFang
04c21cb63d implement custom filter 2018-06-24 15:03:46 +08:00
AllenFang
7d72002b6e fix wrong eslint rule 2018-06-24 13:16:48 +08:00
AllenFang
279cc25da0 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2018-06-24 13:16:07 +08:00
AllenFang
1152bb8440 fix #380 2018-06-23 13:49:38 +08:00
AllenFang
88befb8136 fix #380 2018-06-23 13:47:53 +08:00
Allen
42c6bc0337 Merge pull request #371 from sean-ww/fix/remote-filtered-pagination
Prevent remote pagination from setting the page incorrectly
2018-06-23 12:59:02 +08:00
sean
6e753bb955 Prevent remote pagination from setting the page incorrectly 2018-06-06 15:11:54 +02:00
AllenFang
64df3e1fae Publish
- react-bootstrap-table2-example@0.1.9
 - react-bootstrap-table2-filter@0.2.0
 - react-bootstrap-table2-overlay@0.1.2
 - react-bootstrap-table2-paginator@0.1.4
 - react-bootstrap-table-next@0.1.13
2018-06-04 13:53:02 +08:00
Allen
5cdd1ad093 20180604 release #366 from react-bootstrap-table/develop
20180604 release
2018-06-04 13:49:03 +08:00
AllenFang
36e754b6bc patch docs 2018-06-03 22:58:03 +08:00
AllenFang
6730dcf60d fix overlay test bugs 2018-06-03 22:52:15 +08:00
AllenFang
fb54809dc9 fix docs 2018-06-03 21:36:09 +08:00
AllenFang
d43c622fdb fix docs 2018-06-03 21:31:13 +08:00
AllenFang
7253d7a1d7 fix overlay tests 2018-06-03 20:40:31 +08:00
AllenFang
a6daa50417 improve overlay wrapping 2018-06-03 15:50:10 +08:00
AllenFang
b11019ce20 fix #358 2018-06-03 14:49:58 +08:00
Allen
dda47f7b7d Merge pull request #365 from react-bootstrap-table/feat/349
Feat/349
2018-06-03 14:39:22 +08:00
AllenFang
4da8ba7ecc patch docs for date filter 2018-06-03 14:24:20 +08:00
Allen
2ff0b27747 Merge pull request #359 from sean-ww/feature/pagination-total
Adding custom pagination total
2018-06-03 14:18:50 +08:00
AllenFang
c3f279fb0c patch tests for date filter 2018-06-03 14:04:06 +08:00
AllenFang
06bcf1edca add stories for date filter 2018-06-03 14:03:38 +08:00
AllenFang
fc1f75cfac implement date filter 2018-06-03 14:02:39 +08:00
sean
1cf12ab707 paginationTotal renamed to paginationTotalRenderer 2018-06-02 10:43:56 +02:00
Allen
288ccc1049 Merge pull request #364 from react-bootstrap-table/feat/351
Fix #351
2018-06-02 15:37:51 +08:00
AllenFang
f13c139f63 patch docs for custom selection 2018-06-02 15:27:10 +08:00
AllenFang
e72ad0586e add story for custom selection column 2018-06-02 15:20:57 +08:00
AllenFang
c2044fe8b5 patch test for selection box 2018-06-02 15:20:40 +08:00
AllenFang
a7b3690a7c custom selection box 2018-06-02 15:20:19 +08:00
Amol Udage
68afc348db fixes sorting issue (#354)
+ when remote sort is true then disable client side sorting
2018-06-02 13:21:25 +08:00
sean-ww88
5404124a78 Added missing commas on the custom pagination example 2018-05-30 17:35:27 +02:00
sean-ww88
d592871c0d Adding custom pagination total 2018-05-30 17:29:58 +02:00
AllenFang
6019e550fd Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2018-05-28 18:07:31 +08:00
AllenFang
765a49fb07 Publish
- react-bootstrap-table-next@0.1.12
2018-05-23 22:44:40 +08:00
Allen
fe2fd93c20 fix the bool rendering issues in React (#340) (#348) 2018-05-23 22:42:46 +08:00
Allen
19ba336e32 gix the bool rendering issues in React (#340) 2018-05-19 13:07:57 +08:00
AllenFang
a50148fe85 Publish
- react-bootstrap-table2-example@0.1.8
 - react-bootstrap-table2-filter@0.1.7
 - react-bootstrap-table-next@0.1.11
2018-05-14 23:09:49 +08:00
Allen
c96156503f Merge pull request #336 from react-bootstrap-table/develop
20180513 release
2018-05-14 23:02:15 +08:00
AllenFang
ed2ba2a5c5 fix #334 2018-05-14 22:44:28 +08:00
AllenFang
f87fe3e544 patch docs, example and test for wrapperClasses 2018-05-12 13:54:40 +08:00
MikeSha
43e73313e6 implement wrapperClasses(#325) 2018-05-12 13:40:24 +08:00
Allen
888aa1d08b fix #303 (#330) 2018-05-10 14:07:24 +08:00
AllenFang
028834da8b add release command 2018-05-08 22:33:46 +08:00
AllenFang
8f3b989b00 Publish
- react-bootstrap-table2-editor@0.2.1
2018-05-08 22:23:23 +08:00
AllenFang
fe8761427d Publish
- react-bootstrap-table2-paginator@0.1.3
2018-05-08 22:22:47 +08:00
AllenFang
27a09de008 Publish
- react-bootstrap-table-next@0.1.10
2018-05-08 22:22:10 +08:00
AllenFang
20ba8cc24e Publish
- react-bootstrap-table2-editor@0.2.0
 - react-bootstrap-table2-example@0.1.7
 - react-bootstrap-table2-paginator@0.1.2
 - react-bootstrap-table-next@0.1.9
2018-05-06 16:59:28 +08:00
Allen
b8b52e7fc0 20180507 release #326
20180507 release
2018-05-06 16:57:29 +08:00
Allen
05a8c3be5f fix #309 (#324) 2018-05-06 15:50:18 +08:00
Allen
2f9bedbeeb Merge pull request #323 from react-bootstrap-table/feat/pagination-total
Implement pagination total
2018-05-06 15:16:49 +08:00
AllenFang
01be6fc275 example for #238 2018-05-06 15:08:43 +08:00
AllenFang
c20a4bb220 fix #238 2018-05-06 15:08:20 +08:00
Allen
ed21b3cb65 Merge pull request #322 from react-bootstrap-table/feat/rich-editor
Rich Editors
2018-05-06 14:05:08 +08:00
AllenFang
f2a44c976d patch docs 2018-05-06 13:50:59 +08:00
AllenFang
ca5189d8ad patch tests for rich editors 2018-05-05 18:18:52 +08:00
AllenFang
03f51c36ac add example for rich cell editor 2018-05-05 18:18:52 +08:00
AllenFang
607202b4e9 implement rich cell editor 2018-05-05 18:18:52 +08:00
AllenFang
4db4f4fb2d Publish
- react-bootstrap-table2-example@0.1.6
 - react-bootstrap-table2-filter@0.1.6
 - react-bootstrap-table-next@0.1.8
2018-04-15 21:25:09 +08:00
Allen
1d7df6819e 2018/04/01 release
2018/04/01 release
2018-04-15 21:21:50 +08:00
Allen
e4b6993692 fix #302 (#304) 2018-04-15 17:29:17 +08:00
Allen
b15d7a3412 Merge pull request #287 from react-bootstrap-table/feature/pogrammatically-filter
Feature/programmatically filter
2018-04-15 15:46:54 +08:00
Allen
b172c6801c fix #228 2018-04-08 21:17:24 +08:00
Chun-MingChen
dc1f4dcc38 update travis.yml 2018-04-05 16:35:26 +08:00
Chun-MingChen
a82e611358 [test] add test for pogrammatically filter 2018-04-04 17:58:19 +08:00
Chun-MingChen
c64951fd6f [test] correct tests for filter components
* <Text />, <Select /> and <Numbner />
2018-04-04 17:58:19 +08:00
Chun-MingChen
a35701fabf [test] correct test for filter wrapper 2018-04-04 17:58:19 +08:00
Chun-MingChen
f54c1f77b4 display filter condition correctly and make sure text filter to be String 2018-04-04 17:58:19 +08:00
Chun-MingChen
377534512a rename props and variable in samples for better readability 2018-04-04 17:45:56 +08:00
Chun-MingChen
09032349d0 [example] example for programmatically filter by text, number and select 2018-04-04 17:45:56 +08:00
Chun-MingChen
4dd39aeed8 export onFilter function to allow user to access 2018-04-04 17:45:56 +08:00
Chun-MingChen
a1477e2ad3 filter column by new onFilter 2018-04-04 17:45:56 +08:00
Chun-MingChen
f34cb4bf63 allow user to filter column without inputField
* wrap onFilter to HOF to allow filter dynamically
2018-04-04 17:45:56 +08:00
AllenFang
3dc9cd3941 Publish
- react-bootstrap-table2-editor@0.1.5
 - react-bootstrap-table2-filter@0.1.5
 - react-bootstrap-table-next@0.1.7
2018-04-02 23:46:59 +08:00
AllenFang
e8458b4b63 Publish
- react-bootstrap-table2-editor@0.1.4
 - react-bootstrap-table2-example@0.1.5
 - react-bootstrap-table2-filter@0.1.4
 - react-bootstrap-table-next@0.1.6
2018-04-01 15:56:53 +08:00
Allen
1d87ce9ffc 2018/04/01 release
2018/04/01 release
2018-04-01 15:55:45 +08:00
Allen
88e1a0774b fix #281 2018-04-01 14:14:32 +08:00
Patrick O'Meara
11d4f40089 noDataIndication (#276)
* use the correct amount of cells when the first row is select
* storybook added for development, not necessary in docs

fixes react-bootstrap-table/react-bootstrap-table2#264
2018-04-01 13:34:06 +08:00
Patrick O'Meara
41dc3ef619 empty noDataIndication when empty (#275)
* don't display unneeded empty row when noDataIndication isn't set
2018-04-01 13:32:24 +08:00
Nixon Kwok
4501ddb632 Fix textFilter() for Internet Explorer (includes() and find() are not supported) (#274)
* Fix textFilter() for Internet Explorer 11
- replace includes() with indexOf() !== -1
- replace find() with for loop

* Requested changes; more readability with for loop
- use .length of the columns instead of the Object.keys()
2018-04-01 13:30:59 +08:00
Allen
05657ee217 Merge pull request #273 from react-bootstrap-table/revert-265-264_indication_no_data
Revert "fix#264: wrong col span when enable selection in a empty tabl…
2018-03-25 17:38:32 +08:00
Allen
c91f521913 fix #258 (#268) 2018-03-25 16:44:27 +08:00
Allen
9ee9c7de43 Revert "fix#264: wrong col span when enable selection in a empty table (#265)"
This reverts commit 42dbd00fd9.
2018-03-25 16:37:27 +08:00
Patrick O'Meara
42dbd00fd9 fix#264: wrong col span when enable selection in a empty table (#265)
* noDataIndication

* use the correct amount of cells when the first row is select
* storybook added for development, not necessary in docs

fixes react-bootstrap-table/react-bootstrap-table2#264

* eslint complaints

  4:11  error  'columnLen' is never reassigned. Use 'const' instead                   prefer-const
  7:9   error  Expected an assignment or function call and instead saw an expression  no-unused-expressions

* tests updated
2018-03-25 16:36:58 +08:00
AllenFang
bd9150f88f Publish
- react-bootstrap-table2-editor@0.1.3
 - react-bootstrap-table2-example@0.1.4
 - react-bootstrap-table-next@0.1.5
2018-03-18 23:07:46 +08:00
Allen
3956fbca11 Merge pull request #263 from react-bootstrap-table/develop
2018/03/19 release
2018-03-18 23:03:55 +08:00
NickChen
240bcd75c0 Merge pull request #262 from prajapati-parth/update-readme
Add TravisCI badge to README
2018-03-18 17:48:11 +08:00
Chun-MingChen
6de57737ea allow travis to run test for master branch 2018-03-18 17:35:05 +08:00
Parth Prajapati
33a8da701b Add TravisCI badge 2018-03-18 14:28:27 +05:30
AllenFang
d5ddd8c3af add selection management example 2018-03-18 16:44:39 +08:00
Chun-MingChen
6f9361934a set state.selectedRowKeys based on store 2018-03-18 16:10:06 +08:00
Parth Prajapati
6bc81dddd0 Fixed #237 (#261)
* Fixed #237

* Solved lint errors

* Removed test cases for display: none checks
- added test cases for hidden columns check
2018-03-18 15:42:21 +08:00
AllenFang
c11539b9fb [docs] patch id and classes for BootstrapTable 2018-03-18 14:33:11 +08:00
Allen
94f1a5ee57 Merge pull request #247 from Chun-MingChen/feature/customized-class-n-id-on-table
customized classes and id on BootstrapTable (#235)
2018-03-18 14:25:55 +08:00
Allen
de27072ceb Merge pull request #260 from react-bootstrap-table/bugfix/default-selection
Bugfix/default selection
2018-03-18 14:16:49 +08:00
AllenFang
55336108a0 should recieve newest selectRow.selected 2018-03-18 14:07:44 +08:00
Chun-MingChen
923439dc81 correct typo 2018-03-17 17:29:30 +08:00
Chun-MingChen
d80ae13513 [test] test for RowSelectionWrapper#componentWillReceiveProps 2018-03-17 17:27:27 +08:00
Chun-MingChen
ceebdf5a13 refine store to set selectRow when receiving props 2018-03-17 17:27:27 +08:00
Chun-MingChen
0eda54b772 correct the typo of documents 2018-03-17 16:23:09 +08:00
Chun-MingChen
3ed4d87b29 correct attribute key of columns.headerEvent in column-event-tables 2018-03-17 15:29:23 +08:00
Chun-MingChen
8a8c2d4964 [example] add demo for customized classes and id table 2018-03-10 18:54:59 +08:00
Chun-MingChen
3cea9658c7 [test] test for customized classes and id 2018-03-10 18:54:41 +08:00
Chun-MingChen
9f9203bffa implement customized classes and id on the table 2018-03-10 18:54:26 +08:00
AllenFang
4011cae18e Publish 2018-03-06 23:59:04 +08:00
Allen
60f32f0336 Merge pull request #240 from react-bootstrap-table/develop
2018/03/06 release
2018-03-06 23:40:05 +08:00
Allen
a5cb806d98 implement default selection (#234) 2018-03-05 22:27:46 +08:00
Allen
9382ed587b implement row event delegater (#233) 2018-03-04 17:22:52 +08:00
Allen
a11913c49a fix #210 (#232) 2018-03-04 16:21:10 +08:00
AllenFang
4635b60da0 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2018-03-04 16:10:23 +08:00
AllenFang
4d9e20e9c8 fix #221 2018-03-04 16:05:10 +08:00
Parth Prajapati
931cf80450 Fixes #186 (#219)
* Fixes #186

* Solved lint error
2018-03-04 16:05:10 +08:00
AllenFang
5dd1f1e9ea fix #221 2018-02-24 23:03:09 +08:00
Parth Prajapati
a8083ac17d Fixes #186 (#219)
* Fixes #186

* Solved lint error
2018-02-24 22:53:52 +08:00
222 changed files with 14158 additions and 3930 deletions

View File

@@ -9,9 +9,10 @@ cache:
branches: branches:
only: only:
# skip master branch when it's under development phase - master
# - master
- develop - develop
except:
- gh-pages-src
before_install: before_install:
- curl -o- -L https://yarnpkg.com/install.sh | bash -s - curl -o- -L https://yarnpkg.com/install.sh | bash -s

View File

@@ -1,4 +1,5 @@
# react-bootstrap-table2 # react-bootstrap-table2
[![Build Status](https://travis-ci.org/react-bootstrap-table/react-bootstrap-table2.svg?branch=master)](https://travis-ci.org/react-bootstrap-table/react-bootstrap-table2)
Rebuilt [react-bootstrap-table](https://github.com/AllenFang/react-bootstrap-table) Rebuilt [react-bootstrap-table](https://github.com/AllenFang/react-bootstrap-table)
> `react-bootstrap-table2`'s npm module name is [**`react-bootstrap-table-next`**](https://www.npmjs.com/package/react-bootstrap-table-next) due to some guys already used it > `react-bootstrap-table2`'s npm module name is [**`react-bootstrap-table-next`**](https://www.npmjs.com/package/react-bootstrap-table-next) due to some guys already used it
@@ -10,6 +11,7 @@ Rebuilt [react-bootstrap-table](https://github.com/AllenFang/react-bootstrap-tab
* [`react-bootstrap-table2-editor`](https://www.npmjs.com/package/react-bootstrap-table2-editor) * [`react-bootstrap-table2-editor`](https://www.npmjs.com/package/react-bootstrap-table2-editor)
* [`react-bootstrap-table2-paginator`](https://www.npmjs.com/package/react-bootstrap-table2-paginator) * [`react-bootstrap-table2-paginator`](https://www.npmjs.com/package/react-bootstrap-table2-paginator)
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay) * [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
* [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit)
This can help your application with less bundled size and also help us have clean design to avoid handling to much logic in kernal module(SRP). This can help your application with less bundled size and also help us have clean design to avoid handling to much logic in kernal module(SRP).

View File

@@ -9,18 +9,25 @@
#### Optional #### Optional
* [remote](#remote) * [remote](#remote)
* [bootstrap4](#bootstrap4)
* [loading](#loading) * [loading](#loading)
* [caption](#caption) * [caption](#caption)
* [striped](#striped) * [striped](#striped)
* [bordered](#bordered) * [bordered](#bordered)
* [hover](#hover) * [hover](#hover)
* [condensed](#condensed) * [condensed](#condensed)
* [id](#id)
* [classes](#classes)
* [wrapperClasses](#wrapperClasses)
* [headerClasses](#headerClasses)
* [cellEdit](#cellEdit) * [cellEdit](#cellEdit)
* [selectRow](#selectRow) * [selectRow](#selectRow)
* [expandRow](#expandRow)
* [rowStyle](#rowStyle) * [rowStyle](#rowStyle)
* [rowClasses](#rowClasses) * [rowClasses](#rowClasses)
* [rowEvents](#rowEvents) * [rowEvents](#rowEvents)
* [defaultSorted](#defaultSorted) * [defaultSorted](#defaultSorted)
* [defaultSortDirection](#defaultSortDirection)
* [pagination](#pagination) * [pagination](#pagination)
* [filter](#filter) * [filter](#filter)
* [onTableChange](#onTableChange) * [onTableChange](#onTableChange)
@@ -59,14 +66,17 @@ A special case for remote pagination:
remote={ { pagination: true, filter: false, sort: false } } remote={ { pagination: true, filter: false, sort: false } }
``` ```
There is a apecial case for remote pagination, even you only specified the paignation need to handle as remote, `react-bootstrap-table2` will handle all the table changes(filter, sort etc) as remote mode, because `react-bootstrap-table2` only know the data of current page, but filtering, searching or sort need to work on overall datas. There is a special case for remote pagination, even you only specified the pagination need to handle as remote, `react-bootstrap-table2` will handle all the table changes(filter, sort etc) as remote mode, because `react-bootstrap-table2` only know the data of current page, but filtering, searching or sort need to work on overall data.
### <a name='bootstrap4'>bootstrap4 - [Bool]</a>
`true` to indicate your bootstrap version is 4. Default version is 3.
### <a name='loading'>loading - [Bool]</a> ### <a name='loading'>loading - [Bool]</a>
Telling if table is loading or not, for example: waiting data loading, filtering etc. It's **only** valid when [`remote`](#remote) is enabled. Telling if table is loading or not, for example: waiting data loading, filtering etc. It's **only** valid when [`remote`](#remote) is enabled.
When `loading` is `true`, `react-bootstrap-table2` will attend to render a overlay on table via [`overlay`](#overlay) prop, if [`overlay`](#overlay) prop is not given, `react-bootstrap-table2` will ignore the overlay rendering. When `loading` is `true`, `react-bootstrap-table2` will attend to render a overlay on table via [`overlay`](#overlay) prop, if [`overlay`](#overlay) prop is not given, `react-bootstrap-table2` will ignore the overlay rendering.
### <a name='overlay'>overlay - [Function]</a> ### <a name='overlay'>overlay - [Function]</a>
`overlay` accept a factory funtion which should returning a higher order component. By default, `react-bootstrap-table2-overlay` can be a good option for you: `overlay` accept a factory function which should returning a higher order component. By default, `react-bootstrap-table2-overlay` can be a good option for you:
```sh ```sh
$ npm install react-bootstrap-table2-overlay $ npm install react-bootstrap-table2-overlay
@@ -100,12 +110,26 @@ Same as bootstrap `.table-hover` class for adding mouse hover effect (grey backg
### <a name='condensed'>condensed - [Bool]</a> ### <a name='condensed'>condensed - [Bool]</a>
Same as bootstrap `.table-condensed` class for making a table more compact by cutting cell padding in half. Same as bootstrap `.table-condensed` class for making a table more compact by cutting cell padding in half.
### <a name='id'>id - [String]</a>
Customize id on `table` element.
### <a name='classes'>classes - [String]</a>
Customize class on `table` element.
### <a name='wrapperClasses'>wrapperClasses - [String]</a>
Customize class on the outer element which wrap up the `table` element.
### <a name='headerClasses'>headerClasses - [String]</a>
Customize class on the header row(`tr`).
### <a name='cellEdit'>cellEdit - [Object]</a> ### <a name='cellEdit'>cellEdit - [Object]</a>
Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail. Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail.
### <a name='selectRow'>selectRow - [Object]</a> ### <a name='selectRow'>selectRow - [Object]</a>
Makes table rows selectable, please see [selectRow definition](./row-selection.md) for more detail. Makes table rows selectable, please see [selectRow definition](./row-selection.md) for more detail.
### <a name='expandRow'>expandRow - [Object]</a>
Makes table rows expandable, please see [expandRow definition](./row-expand.md) for more detail.
### <a name='rowStyle'>rowStyle = [Object | Function]</a> ### <a name='rowStyle'>rowStyle = [Object | Function]</a>
Custom the style of table rows: Custom the style of table rows:
@@ -162,8 +186,11 @@ const defaultSorted = [{
}]; }];
``` ```
### <a name='defaultSortDirection'>defaultSortDirection - [String]</a>
Default sort direction when user click on header column at first time, available value is `asc` and `desc`. Default is `desc`.
### <a name='pagination'>pagination - [Object]</a> ### <a name='pagination'>pagination - [Object]</a>
`pagination` allow user to render a pagination panel on the bottom of table. But pagination funcitonality is separated from core of `react-bootstrap-table2` so that you are suppose to install `react-bootstrap-table2-paginator` additionaly. `pagination` allow user to render a pagination panel on the bottom of table. But pagination functionality is separated from core of `react-bootstrap-table2` so that you are suppose to install `react-bootstrap-table2-paginator` additionally.
```sh ```sh
$ npm install react-bootstrap-table2-paginator --save $ npm install react-bootstrap-table2-paginator --save
@@ -188,6 +215,7 @@ paginator({
totalSize, // Total data size. It's necessary when remote is enabled totalSize, // Total data size. It's necessary when remote is enabled
pageStartIndex: 0, // first page will be 0, default is 1 pageStartIndex: 0, // first page will be 0, default is 1
paginationSize: 3, // the pagination bar size, default is 5 paginationSize: 3, // the pagination bar size, default is 5
showTotal: true, // display pagination information
sizePerPageList: [ { sizePerPageList: [ {
text: '5', value: 5 text: '5', value: 5
}, { }, {
@@ -205,15 +233,16 @@ paginator({
prePageTitle: 'Go to previous', // the title of previous page button prePageTitle: 'Go to previous', // the title of previous page button
firstPageTitle: 'Go to first', // the title of first page button firstPageTitle: 'Go to first', // the title of first page button
lastPageTitle: 'Go to last', // the title of last page button lastPageTitle: 'Go to last', // the title of last page button
hideSizePerPage: true, // hide the size per page dorpdown hideSizePerPage: true, // hide the size per page dropdown
hidePageListOnlyOnePage: true, // hide pagination bar when only one page, default is false hidePageListOnlyOnePage: true, // hide pagination bar when only one page, default is false
onPageChange: (page, sizePerPage) => {}, // callback function when page was changing onPageChange: (page, sizePerPage) => {}, // callback function when page was changing
onSizePerPageChange: (sizePerPage, page) => {}, // callback function when page size was changing onSizePerPageChange: (sizePerPage, page) => {}, // callback function when page size was changing
paginationTotalRenderer: (from, to, size) => { ... } // custom the pagination total
}) })
``` ```
### <a name='filter'>filter - [Object]</a> ### <a name='filter'>filter - [Object]</a>
`filter` allow user to filter data by column. However, filter funcitonality is separated from core of `react-bootstrap-table2` so that you are suppose to install `react-bootstrap-table2-filter` firstly. `filter` allow user to filter data by column. However, filter functionality is separated from core of `react-bootstrap-table2` so that you are suppose to install `react-bootstrap-table2-filter` firstly.
```sh ```sh
$ npm install react-bootstrap-table2-filter --save $ npm install react-bootstrap-table2-filter --save

View File

@@ -32,8 +32,16 @@ Available properties in a column object:
* [validator](#validator) * [validator](#validator)
* [editCellStyle](#editCellStyle) * [editCellStyle](#editCellStyle)
* [editCellClasses](#editCellClasses) * [editCellClasses](#editCellClasses)
* [editorStyle](#editorStyle)
* [editorClasses](#editorClasses)
* [editor](#editor)
* [editorRenderer](#editorRenderer)
* [filter](#filter) * [filter](#filter)
* [filterValue](#filterValue) * [filterValue](#filterValue)
* [csvType](#csvType)
* [csvFormatter](#csvFormatter)
* [csvText](#csvText)
* [csvExport](#csvExport)
Following is a most simplest and basic usage: Following is a most simplest and basic usage:
@@ -87,6 +95,10 @@ dataField: 'address.city'
* `rowIndex` * `rowIndex`
* [`formatExtraData`](#formatExtraData) * [`formatExtraData`](#formatExtraData)
> Attention:
> Don't use any state data or any external data in formatter function, please pass them via [`formatExtraData`](#formatExtraData).
> In addition, please make formatter function as pure function as possible as you can.
## <a name='headerFormatter'>column.headerFormatter - [Function]</a> ## <a name='headerFormatter'>column.headerFormatter - [Function]</a>
`headerFormatter` allow you to customize the header column and only accept a callback function which take three arguments and a JSX/String are expected for return. `headerFormatter` allow you to customize the header column and only accept a callback function which take three arguments and a JSX/String are expected for return.
@@ -137,7 +149,7 @@ Enable the column sort via a `true` value given.
``` ```
## <a name='classes'>column.classes - [String | Function]</a> ## <a name='classes'>column.classes - [String | Function]</a>
It's availabe to have custom class on table column: It's available to have custom class on table column:
```js ```js
{ {
@@ -165,7 +177,7 @@ In addition, `classes` also accept a callback function which have more power to
A new `String` will be the result as element class. A new `String` will be the result as element class.
## <a name='headerClasses'>column.headerClasses - [String | Function]</a> ## <a name='headerClasses'>column.headerClasses - [String | Function]</a>
It's similar to [`column.classes`](#classes), `headerClasses` is availabe to have customized class on table header column: It's similar to [`column.classes`](#classes), `headerClasses` is available to have customized class on table header column:
```js ```js
{ {
@@ -190,7 +202,7 @@ Furthermore, it also accept a callback function which takes 2 arguments and a `S
A new `String` will be the result of element headerClasses. A new `String` will be the result of element headerClasses.
## <a name='style'>column.style - [Object | Function]</a> ## <a name='style'>column.style - [Object | Function]</a>
It's availabe to have custom style on table column: It's available to have custom style on table column:
```js ```js
{ {
@@ -220,7 +232,7 @@ A new `Object` will be the result of element style.
## <a name='headerStyle'>column.headerStyle - [Object | Function]</a> ## <a name='headerStyle'>column.headerStyle - [Object | Function]</a>
It's availabe to have customized inline-style on table header column: It's available to have customized inline-style on table header column:
```js ```js
{ {
@@ -278,7 +290,7 @@ A new `String` will be the result of element title.
} }
``` ```
It's also availabe to custom via a callback function: It's also available to custom via a callback function:
```js ```js
{ {
headerTitle: function callback(column, colIndex) { ... } headerTitle: function callback(column, colIndex) { ... }
@@ -401,7 +413,7 @@ A new `Object` will be the result of element HTML attributes.
> Caution: > Caution:
> If `column.classes`, `column.style`, `column.title`, `column.hidden` or `column.align` was given at the same time, property `attrs` has lower priorty and it will be overwrited. > If `column.classes`, `column.style`, `column.title`, `column.hidden` or `column.align` was given at the same time, property `attrs` has lower priority and it will be overwritten.
```js ```js
{ {
@@ -412,7 +424,7 @@ A new `Object` will be the result of element HTML attributes.
``` ```
## <a name='headerAttrs'>column.headerAttrs - [Object | Function]</a> ## <a name='headerAttrs'>column.headerAttrs - [Object | Function]</a>
`headerAttrs` is similiar to [`column.attrs`](#attrs) but it works for header column. `headerAttrs` is similar to [`column.attrs`](#attrs) but it works for header column.
```js ```js
{ {
// omit... // omit...
@@ -444,7 +456,7 @@ A new `Object` will be the result of element headerAttrs.
> Caution: > Caution:
> Same as [column.attrs](#attrs), it has lower priority and will be > Same as [column.attrs](#attrs), it has lower priority and will be
> overwrited when other props related to HTML attributes were given. > overwritten when other props related to HTML attributes were given.
### <a name='headerSortingClasses'>headerSortingClasses - [String | Function]</a> ### <a name='headerSortingClasses'>headerSortingClasses - [String | Function]</a>
@@ -467,7 +479,7 @@ const headerSortingClasses = (column, sortOrder, isLastSorting, colIndex) => { .
### <a name='headerSortingStyle'>headerSortingStyle - [Object | Function]</a> ### <a name='headerSortingStyle'>headerSortingStyle - [Object | Function]</a>
It's similiar to [headerSortingClasses](#headerSortingClasses). It allows to customize the style of header cell when this column is sorting. A style `Object` and `callback` are acceptable. `callback` takes **4** arguments and an `Object` is expected to return: It's similar to [headerSortingClasses](#headerSortingClasses). It allows to customize the style of header cell when this column is sorting. A style `Object` and `callback` are acceptable. `callback` takes **4** arguments and an `Object` is expected to return:
```js ```js
const sortingHeaderStyle = { const sortingHeaderStyle = {
@@ -502,7 +514,7 @@ If a callback function given, you can control the editable level as cell level:
} }
``` ```
The return value can be a bool or an object. If your valiation is pass, return `true` explicitly. If your valiation is invalid, return following object instead: The return value can be a bool or an object. If your validation is pass, return `true` explicitly. If your validation is invalid, return following object instead:
```js ```js
{ {
valid: false, valid: false,
@@ -552,11 +564,100 @@ Or take a callback function
} }
``` ```
## <a name='editorStyle'>column.editorStyle - [Object | Function]</a>
This is almost same as [`column.editCellStyle`](#editCellStyle), but `column.editorStyle` is custom the style on editor instead of cell(`td`).
## <a name='editorClasses'>column.editorClasses - [Object | Function]</a>
This is almost same as [`column.editCellClasses`](#editCellClasses), but `column.editorClasses` is custom the class on editor instead of cell(`td`).
## <a name='editor'>column.editor - [Object]</a>
`column.editor` allow you to custom the type of cell editor by following predefined type:
* Text(Default)
* Dropdown
* Date
* Textarea
* Checkbox
Following is a quite example:
```js
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [
//...
, {
dataField: 'done',
text: 'Done',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}
];
```
If you want more information, please check [here](https://github.com/react-bootstrap-table/react-bootstrap-table2/tree/master/packages/react-bootstrap-table2-editor).
## <a name='editorRenderer'>column.editorRenderer - [Function]</a>
If you feel above predefined editors are not satisfied to your requirement, you can totally custom the editor via `column.editorRenderer`:
```js
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
// Custom Editor
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [
//...
, {
dataField: 'done',
text: 'Done',
editorRenderer: (editorProps, value, row, column, rowIndex, columnIndex) =>
<QualityRanger { ...editorProps } value={ value } />;
}
];
```
## <a name='filter'>column.filter - [Object]</a> ## <a name='filter'>column.filter - [Object]</a>
Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters: Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters:
* Text(`textFilter`) * Text(`textFilter`)
* Select(`selectFilter`) * Select(`selectFilter`)
* Number(`numberFilter`)
* Date(`dateFilter`)
We have a quick example to show you how to use `column.filter`: We have a quick example to show you how to use `column.filter`:
@@ -574,7 +675,7 @@ import { textFilter } from 'react-bootstrap-table2-filter';
For some reason of simple customization, `react-bootstrap-table2` allow you to pass some props to filter factory function. Please check [here](https://github.com/react-bootstrap-table/react-bootstrap-table2/tree/master/packages/react-bootstrap-table2-filter/README.md) for more detail tutorial. For some reason of simple customization, `react-bootstrap-table2` allow you to pass some props to filter factory function. Please check [here](https://github.com/react-bootstrap-table/react-bootstrap-table2/tree/master/packages/react-bootstrap-table2-filter/README.md) for more detail tutorial.
## <a name='filterValue'>column.filterValue - [Function]</a> ## <a name='filterValue'>column.filterValue - [Function]</a>
Sometimes, if the cell/column value that you don't want to filter on them, you can define `filterValue` to return a actual value you wanna be filterd: Sometimes, if the cell/column value that you don't want to filter on them, you can define `filterValue` to return a actual value you wanna be filtered:
**Parameters** **Parameters**
* `cell`: The value of current cell. * `cell`: The value of current cell.
@@ -593,3 +694,16 @@ A final `String` value you want to be filtered.
filterValue: (cell, row) => owners[cell] filterValue: (cell, row) => owners[cell]
} }
``` ```
## <a name='csvType'>column.csvType - [Object]</a>
Default is `String`. Currently, the available value is `String` and `Number`. If `Number` assigned, the cell value will not wrapped with double quote.
## <a name='csvFormatter'>column.csvFormatter - [Function]</a>
This is same as [`column.formatter`](#formatter). But `csvFormatter` only for CSV export and called when export CSV.
## <a name='csvText'>column.csvText - [String]</a>
Custom the CSV header cell, Default is [`column.text`](#text).
## <a name='csvExport'>column.csvExport - [Bool]</a>
Default is `true`, `false` will hide this column when export CSV.

View File

@@ -1,8 +1,8 @@
# Migration Guide # Migration Guide
* Please see the [CHANGELOG](https://react-bootstrap-table.github.io/react-bootstrap-table2/blog/2018/01/24/new-version-0.1.0.html) for `react-bootstrap-table2` first drop. * Please see the [CHANGELOG](https://react-bootstrap-table.github.io/react-bootstrap-table2/blog/2018/01/24/new-version-0.1.0.html) for `react-bootstrap-table2` first drop.
* Please see the [Roadmap](https://react-bootstrap-table.github.io/react-bootstrap-table2/blog/2018/01/24/release-plan.html) for `react-bootstrap-table2` in 2018/Q1. * Please see the [Road Map](https://react-bootstrap-table.github.io/react-bootstrap-table2/blog/2018/01/24/release-plan.html) for `react-bootstrap-table2` in 2018/Q1.
* Feel free to see the [offical docs](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/about.html), we list all the basic usage here!! * Feel free to see the [official docs](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/about.html), we list all the basic usage here!!
## Preface ## Preface
@@ -22,12 +22,14 @@ Currently, **I still can't implement all the mainly features in legacy `react-bo
* Pagination Addons * Pagination Addons
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay) * [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
* Overlay/Loading Addons * Overlay/Loading Addons
* [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit)
* Table Toolkits, like search, csv 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 kernal module(SRP). Hence, which means you probably need to install above addons when you need specific features. 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.
## Core Table Migration ## Core Table Migration
There is a big chagne is that there's no `TableHeaderColumn` in the `react-bootstrap-table2`, instead you are supposed to be define the `columns` prop on `BootstrapTable`: There is a big change is that there's no `TableHeaderColumn` in the `react-bootstrap-table2`, instead you are supposed to be define the `columns` prop on `BootstrapTable`:
```js ```js
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
@@ -48,8 +50,8 @@ const columns = [{
The `text` property is just same as the children for the `TableHeaderColumn`, if you want to custom the header, there's a new property is: [`headerFormatter`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnheaderformatter-function). The `text` property is just same as the children for the `TableHeaderColumn`, if you want to custom the header, there's a new property is: [`headerFormatter`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnheaderformatter-function).
* [`BootstrapTable` Definitation](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/table-props.html) * [`BootstrapTable` Definition](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/table-props.html)
* [Column Definitation](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html) * [Column Definition](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html)
## Table Sort ## Table Sort
@@ -65,14 +67,14 @@ Please see [Work with table sort](https://react-bootstrap-table.github.io/react-
- [ ] Sort management - [ ] Sort management
- [ ] Multi sort - [ ] Multi sort
Due to no `TableHeaderColumn` so that no `dataSort` here, please add [`sort`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnsort-bool) property on column definitation. Due to no `TableHeaderColumn` so that no `dataSort` here, please add [`sort`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnsort-bool) property on column definition.
## Row Selection ## Row Selection
Please see [Work with selection](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/basic-row-select.html). Please see [Work with selection](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/basic-row-select.html).
Please see [available selectRow configurations](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/row-select-props.html). Please see [available selectRow configurations](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/row-select-props.html).
No huge change for row selection, but can not custom the selection column currently. Coming soon!!! No huge change for row selection.
## Column Filter ## Column Filter
@@ -82,18 +84,18 @@ Please see [available filter configuration](https://react-bootstrap-table.github
- [x] Text Filter - [x] Text Filter
- [x] Custom Text Filter - [x] Custom Text Filter
- [x] Remote Filter - [x] Remote Filter
- [ ] Custom Filter Component - [x] Custom Filter Component
- [ ] Regex Filter - [ ] Regex Filter
- [x] Select Filter - [x] Select Filter
- [x] Custom Select Filter - [x] Custom Select Filter
- [X] Number Filter - [X] Number Filter
- [ ] Date Filter - [X] Date Filter
- [ ] Array Filter - [X] Array Filter
- [ ] Programmatically Filter - [X] Programmatically Filter
Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly. Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly.
Due to no `TableHeaderColumn` so that no `filter` here, please add [`filter`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnfilter-object) property on column definitation and [`filter`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/table-props.html#filter-object) prop on `BootstrapTable`. Due to no `TableHeaderColumn` so that no `filter` here, please add [`filter`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnfilter-object) property on column definition and [`filter`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/table-props.html#filter-object) prop on `BootstrapTable`.
## Cell Edit ## Cell Edit
@@ -113,6 +115,34 @@ Remember to install [`react-bootstrap-table2-paginator`](https://www.npmjs.com/p
No big changes for pagination, but still can't custom the pagination list, button and sizePerPage dropdown. 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
- [ ] Multiple search
- [ ] Strict search
## Row Expand
- [x] Expand Row Events
- [x] Expand Row Indicator
- [x] Expand Row Management
- [x] Custom Expand Row Indicators
- [ ] Compatiable with Row Selection
- [ ] Expand Column position
- [ ] Expand Column Style/Class
## Export CSV
Export CSV functionality is like search, which is one of functionality in the `react-bootstrap-table2-toolkit`. All of the legacy functions we already implemented.
## Remote ## Remote
> It's totally different in `react-bootstrap-table2`. Please [see](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/basic-remote.html). > It's totally different in `react-bootstrap-table2`. Please [see](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/basic-remote.html).
## Row insert/Delete
Not support yet
## Keyboard Navigation
Not support yet

129
docs/row-expand.md Normal file
View File

@@ -0,0 +1,129 @@
# 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!
## Required
* [renderer (**required**)](#renderer)
## Optional
* [expanded](#expanded)
* [nonExpandable](#nonExpandable)
* [onExpand](#onExpand)
* [onExpandAll](#onExpandAll)
* [showExpandColumn](#showExpandColumn)
* [expandColumnRenderer](#expandColumnRenderer)
* [expandHeaderColumnRenderer](#expandHeaderColumnRenderer)
### <a name="renderer">expandRow.renderer - [Function]</a>
Specify the content of expand row, `react-bootstrap-table2` will pass a row object as argument and expect return a react element.
#### values
* **row**
#### examples
```js
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>
)
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
```
### <a name='expanded'>expandRow.expanded - [Array]</a>
`expandRow.expanded` allow you have default row expandations on table.
```js
const expandRow = {
renderer: (row) => ...
expanded: [1, 3] // should be a row keys array
};
```
### <a name='nonExpandable'>expandRow.nonExpandable - [Array]</a>
This prop allow you to restrict some rows which can not be expanded by user. `expandRow.nonExpandable` accept an rowkeys array.
```js
const expandRow = {
renderer: (row) => ...
nonExpandable: [1, 3 ,5]
};
```
### <a name='onExpand'>expandRow.onExpand - [Function]</a>
This callback function will be called when a row is expand/collapse and pass following four arguments:
`row`, `isExpand`, `rowIndex` and `e`.
```js
const expandRow = {
renderer: (row) => ...
onExpand: (row, isExpand, rowIndex, e) => {
// ...
}
};
```
### <a name='onExpandAll'>expandRow.onExpandAll - [Function]</a>
This callback function will be called when expand/collapse all. It only work when you configure [`expandRow.showExpandColumn`](#showExpandColumn) as `true`.
```js
const expandRow = {
renderer: (row) => ...
onExpandAll: (isExpandAll, results, e) => {
// ...
}
};
```
### <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
```js
const expandRow = {
renderer: (row) => ...
expandColumnRenderer: ({ expanded }) => (
// ....
)
};
```
> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer.
### <a name='expandHeaderColumnRenderer'>expandRow.expandHeaderColumnRenderer - [Function]</a>
Provide a callback function which allow you to custom the expand indicator in the expand header column. This callback only have one argument which is an object and contain one property `isAnyExpands` which indicate if there's any rows are expanded:
```js
const expandRow = {
renderer: (row) => ...
expandHeaderColumnRenderer: ({ isAnyExpands }) => (
// ....
)
};
```
> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer.
### <a name='showExpandColumn'>expandRow.showExpandColumn - [Bool]</a>
Default is `false`, if you want to have a expand indicator, give this prop as `true`
```js
const expandRow = {
renderer: (row) => ...
showExpandColumn: true
};
```

View File

@@ -6,6 +6,7 @@
* [mode (**required**)](#mode) * [mode (**required**)](#mode)
## Optional ## Optional
* [selected](#selected)
* [style](#style) * [style](#style)
* [classes)](#classes) * [classes)](#classes)
* [bgColor](#bgColor) * [bgColor](#bgColor)
@@ -15,6 +16,8 @@
* [onSelect](#onSelect) * [onSelect](#onSelect)
* [onSelectAll](#onSelectAll) * [onSelectAll](#onSelectAll)
* [hideSelectColumn](#hideSelectColumn) * [hideSelectColumn](#hideSelectColumn)
* [selectionRenderer](#selectionRenderer)
* [selectionHeaderRenderer](#selectionHeaderRenderer)
### <a name="mode">selectRow.mode - [String]</a> ### <a name="mode">selectRow.mode - [String]</a>
@@ -52,6 +55,16 @@ const selectRow = {
/> />
``` ```
### <a name='selected'>selectRow.selected - [Array]</a>
`selectRow.selected` allow you have default selections on table.
```js
const selectRow = {
mode: 'checkbox',
selected: [1, 3] // should be a row keys array
};
```
### <a name='style'>selectRow.style - [Object | Function]</a> ### <a name='style'>selectRow.style - [Object | Function]</a>
`selectRow.style` allow you to have custom style on selected rows: `selectRow.style` allow you to have custom style on selected rows:
@@ -91,7 +104,7 @@ const selectRow = {
``` ```
### <a name='bgColor'>selectRow.bgColor - [String | Function]</a> ### <a name='bgColor'>selectRow.bgColor - [String | Function]</a>
The backgroud color when row is selected The background color when row is selected
```js ```js
const selectRow = { const selectRow = {
@@ -145,14 +158,42 @@ const selectRow = {
}; };
``` ```
### <a name='onSelect'>selectRow.onSelect - [Function]</a> ### <a name='selectionRenderer'>selectRow.selectionRenderer - [Function]</a>
This callback function will be called when a row is select/unselect and pass following three arguments: Provide a callback function which allow you to custom the checkbox/radio box. This callback only have one argument which is an object and contain following properties:
`row`, `isSelect` and `rowIndex`.
```js ```js
const selectRow = { const selectRow = {
mode: 'checkbox', mode: 'checkbox',
onSelect: (row, isSelect, rowIndex) => { selectionRenderer: ({ mode, checked, disabled }) => (
// ....
)
};
```
> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer.
### <a name='selectionHeaderRenderer'>selectRow.selectionHeaderRenderer - [Function]</a>
Provide a callback function which allow you to custom the checkbox/radio box in the selection header column. This callback only have one argument which is an object and contain following properties:
```js
const selectRow = {
mode: 'checkbox',
selectionHeaderRenderer: ({ mode, checked, indeterminate }) => (
// ....
)
};
```
> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer.
### <a name='onSelect'>selectRow.onSelect - [Function]</a>
This callback function will be called when a row is select/unselect and pass following three arguments:
`row`, `isSelect`, `rowIndex` and `e`.
```js
const selectRow = {
mode: 'checkbox',
onSelect: (row, isSelect, rowIndex, e) => {
// ... // ...
} }
}; };
@@ -164,7 +205,7 @@ This callback function will be called when select/unselect all and it only work
```js ```js
const selectRow = { const selectRow = {
mode: 'checkbox', mode: 'checkbox',
onSelectAll: (isSelect, results) => { onSelectAll: (isSelect, results, e) => {
// ... // ...
} }
}; };

View File

@@ -17,7 +17,8 @@ const JS_PKGS = [
'react-bootstrap-table2-editor', 'react-bootstrap-table2-editor',
'react-bootstrap-table2-filter', 'react-bootstrap-table2-filter',
'react-bootstrap-table2-overlay', 'react-bootstrap-table2-overlay',
'react-bootstrap-table2-paginator' 'react-bootstrap-table2-paginator',
'react-bootstrap-table2-toolkit'
].reduce((pkg, curr) => `${curr}|${pkg}`, ''); ].reduce((pkg, curr) => `${curr}|${pkg}`, '');
const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`; const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`;
@@ -25,7 +26,8 @@ const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`;
const STYLE_PKGS = [ const STYLE_PKGS = [
'react-bootstrap-table2', 'react-bootstrap-table2',
'react-bootstrap-table2-filter', 'react-bootstrap-table2-filter',
'react-bootstrap-table2-paginator' 'react-bootstrap-table2-paginator',
'react-bootstrap-table2-toolkit',
].reduce((pkg, curr) => `${curr}|${pkg}`, ''); ].reduce((pkg, curr) => `${curr}|${pkg}`, '');
const STYLE_SKIPS = `+(${NODE_MODULES})`; const STYLE_SKIPS = `+(${NODE_MODULES})`;
@@ -72,9 +74,16 @@ function styles() {
.pipe(gulp.dest(PKG_PATH)); .pipe(gulp.dest(PKG_PATH));
} }
function umd() { function umd(done) {
return gulp.src('./webpack.prod.config.babel.js') gulp.parallel(
.pipe(shell(['webpack --config <%= file.path %>'])); () => gulp.src('./webpack/next.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>'])),
() => gulp.src('./webpack/editor.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>'])),
() => gulp.src('./webpack/filter.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>'])),
() => gulp.src('./webpack/overlay.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>'])),
() => gulp.src('./webpack/paginator.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>'])),
() => gulp.src('./webpack/toolkit.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>']))
)();
done();
} }
const buildJS = gulp.parallel(umd, scripts); const buildJS = gulp.parallel(umd, scripts);

View File

@@ -11,10 +11,11 @@
"pretest": "yarn lint --cache", "pretest": "yarn lint --cache",
"test": "jest", "test": "jest",
"test:coverage": "jest --coverage", "test:coverage": "jest --coverage",
"test:watch": "jest --watch", "test:watch": "jest --coverage --watch",
"storybook": "cd ./packages/react-bootstrap-table2-example && yarn storybook", "storybook": "cd ./packages/react-bootstrap-table2-example && yarn storybook",
"gh-pages:clean": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:clean", "gh-pages:clean": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:clean",
"gh-pages:build": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:build" "gh-pages:build": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:build",
"release": "yarn install && yarn build && lerna publish"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -49,8 +50,9 @@
"babel-preset-stage-0": "6.24.1", "babel-preset-stage-0": "6.24.1",
"babel-register": "6.24.1", "babel-register": "6.24.1",
"css-loader": "0.28.1", "css-loader": "0.28.1",
"enzyme": "3.1.1", "enzyme": "3.3.0",
"enzyme-adapter-react-16": "1.0.4", "enzyme-adapter-react-16": "1.1.1",
"enzyme-to-json": "3.3.4",
"eslint": "4.5.0", "eslint": "4.5.0",
"eslint-config-airbnb": "15.1.0", "eslint-config-airbnb": "15.1.0",
"eslint-loader": "1.9.0", "eslint-loader": "1.9.0",
@@ -80,12 +82,14 @@
"dependencies": { "dependencies": {
"classnames": "2.2.5", "classnames": "2.2.5",
"prop-types": "15.5.10", "prop-types": "15.5.10",
"react": "16.0.0", "react": "16.3.2",
"react-dom": "16.0.0" "react-dom": "16.3.2",
"underscore": "1.9.1"
}, },
"jest": { "jest": {
"collectCoverageFrom": [ "collectCoverageFrom": [
"packages/**/*.js" "packages/*/src/**/*.js",
"packages/*/index.js"
], ],
"roots": [ "roots": [
"<rootDir>/packages" "<rootDir>/packages"

View File

@@ -48,14 +48,182 @@ How user save their new editings? We offer two ways:
* Column Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as bool value) * Column Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as bool value)
* Cell Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as a callback function) * Cell Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as a callback function)
## Customize Style/Class ## Validation
Currently, we only support the editing cell style/class customization, in the future, we will offer more customizations.
[column.validator](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnvalidator-function) will help you to work on it!
## Customize Style/Class
### Editing Cell ### Editing Cell
* Customize the editing cell style via [column.editCellStyle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditcellstyle-object-function) * Customize the editing cell style via [column.editCellStyle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditcellstyle-object-function)
* Customize the editing cell classname via [column.editCellClasses](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditcellclasses-string-function) * Customize the editing cell classname via [column.editCellClasses](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditcellclasses-string-function)
## Validation ### Editor
* Customize the editor style via [column.editorStyle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorstyle-object-function)
* Customize the editor classname via [column.editoClasses](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorclasses-string-function)
[`column.validator`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnvalidator-function) will help you to work on it! ## Rich Editors
`react-bootstrap-table2` have following predefined editor:
* Text(Default)
* Dropdown
* Date
* Textarea
* Checkbox
In a nutshell, you just only give a [column.editor](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditor-object) and define the `type`:
```js
import { Type } from 'react-bootstrap-table2-editor';
const columns = [
..., {
dataField: 'done',
text: 'Done',
editor: {
type: Type.SELECT | Type.TEXTAREA | Type.CHECKBOX | Type.DATE,
... // The rest properties will be rendered into the editor's DOM element
}
}
]
```
In the following, we go though all the predefined editors:
### Dropdown Editor
Dropdown editor give a select menu to choose a data from a list, the `editor.options` is required property for dropdown editor.
```js
import { Type } from 'react-bootstrap-table2-editor';
const columns = [
..., {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.SELECT,
options: [{
value: 'A',
label: 'A'
}, {
value: 'B',
label: 'B'
}, {
value: 'C',
label: 'C'
}, {
value: 'D',
label: 'D'
}, {
value: 'E',
label: 'E'
}]
}
}];
```
### Date Editor
Date editor is use `<input type="date">`, the configuration is very simple:
```js
const columns = [
..., {
dataField: 'inStockDate',
text: 'Stock Date',
formatter: (cell) => {
let dateObj = cell;
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`;
},
editor: {
type: Type.DATE
}
}];
```
### Textarea Editor
Textarea editor is use `<input type="textarea">`, user can press `ENTER` to change line and in the `react-bootstrap-table2`, user allow to save result via press `SHIFT` + `ENTER`.
```js
const columns = [
..., {
dataField: 'comment',
text: 'Product Comments',
editor: {
type: Type.TEXTAREA
}
}];
```
### Checkbox Editor
Checkbox editor allow you to have a pair value choice, the `editor.value` is required value to represent the actual value for check and uncheck.
```js
const columns = [
..., {
dataField: 'comment',
text: 'Product Comments',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}];
```
## Customize Editor
If you feel above predefined editors are not satisfied to your requirement, you can certainly custom the editor via [column.editorRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorrenderer-function). It accept a function and pass following arguments when function called:
* `editorProps`: Some useful attributes you can use on DOM editor, like class, style etc.
* `value`: Current cell value
* `row`: Current row data
* `column`: Current column definition
* `rowIndex`: Current row index
* `columnIndex`: Current column index
> Note when implement a custom React editor component, this component should have a **getValue** function which return current value on editor
> Note when you want to save value, you can call **editorProps.onUpdate** function
Following is a short example:
```js
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [
..., {
dataField: 'quality',
text: 'Product Quality',
editorRenderer: (editorProps, value, row, column, rowIndex, columnIndex) => (
<QualityRanger { ...editorProps } value={ value } />
)
}];
```

View File

@@ -1,16 +1,19 @@
import wrapperFactory from './src/wrapper'; import createContext from './src/context';
import editingCellFactory from './src/editing-cell'; import editingCellFactory from './src/editing-cell';
import { import {
EDITTYPE,
CLICK_TO_CELL_EDIT, CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK DELAY_FOR_DBCLICK
} from './src/const'; } from './src/const';
export default (options = {}) => ({ export default (options = {}) => ({
wrapperFactory, createContext,
editingCellFactory, editingCellFactory,
CLICK_TO_CELL_EDIT, CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK, DELAY_FOR_DBCLICK,
options options
}); });
export const Type = EDITTYPE;

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-editor", "name": "react-bootstrap-table2-editor",
"version": "0.1.1", "version": "1.0.0",
"description": "it's the editor addon for react-bootstrap-table2", "description": "it's the editor addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"scripts": { "scripts": {
@@ -41,7 +41,7 @@
], ],
"peerDependencies": { "peerDependencies": {
"prop-types": "^15.0.0", "prop-types": "^15.0.0",
"react": "^16.0.0", "react": "^16.3.0",
"react-dom": "^16.0.0" "react-dom": "^16.3.0"
} }
} }

View File

@@ -0,0 +1,61 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class CheckBoxEditor extends Component {
constructor(props) {
super(props);
this.state = {
checked: props.defaultValue.toString() === props.value.split(':')[0]
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.checkbox.focus();
}
getValue() {
const [positive, negative] = this.props.value.split(':');
return this.checkbox.checked ? positive : negative;
}
handleChange(e) {
if (this.props.onChange) this.props.onChange(e);
const { target } = e;
this.setState(() => ({ checked: target.checked }));
}
render() {
const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('editor edit-chseckbox checkbox', className);
return (
<input
ref={ node => this.checkbox = node }
type="checkbox"
className={ editorClass }
{ ...rest }
checked={ this.state.checked }
onChange={ this.handleChange }
/>
);
}
}
CheckBoxEditor.propTypes = {
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
]),
value: PropTypes.string,
defaultValue: PropTypes.any,
onChange: PropTypes.func
};
CheckBoxEditor.defaultProps = {
className: '',
value: 'on:off',
defaultValue: false,
onChange: undefined
};
export default CheckBoxEditor;

View File

@@ -2,3 +2,11 @@ export const TIME_TO_CLOSE_MESSAGE = 3000;
export const DELAY_FOR_DBCLICK = 200; export const DELAY_FOR_DBCLICK = 200;
export const CLICK_TO_CELL_EDIT = 'click'; export const CLICK_TO_CELL_EDIT = 'click';
export const DBCLICK_TO_CELL_EDIT = 'dbclick'; export const DBCLICK_TO_CELL_EDIT = 'dbclick';
export const EDITTYPE = {
TEXT: 'text',
SELECT: 'select',
TEXTAREA: 'textarea',
CHECKBOX: 'checkbox',
DATE: 'date'
};

View File

@@ -1,16 +1,22 @@
/* eslint react/prop-types: 0 */ /* eslint react/prop-types: 0 */
import React, { Component } from 'react'; /* eslint react/require-default-props: 0 */
import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const'; import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
export default ( export default (
Base, _,
{ _, remoteResolver } dataOperator,
isRemoteCellEdit,
handleCellChange
) => { ) => {
let EditingCell; let EditingCell;
return class CellEditWrapper extends remoteResolver(Component) { const CellEditContext = React.createContext();
class CellEditProvider extends React.Component {
static propTypes = { static propTypes = {
data: PropTypes.array.isRequired,
selectRow: PropTypes.object,
options: PropTypes.shape({ options: PropTypes.shape({
mode: PropTypes.oneOf([CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT]).isRequired, mode: PropTypes.oneOf([CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT]).isRequired,
onErrorMessageDisappear: PropTypes.func, onErrorMessageDisappear: PropTypes.func,
@@ -19,7 +25,7 @@ export default (
afterSaveCell: PropTypes.func, afterSaveCell: PropTypes.func,
nonEditableRows: PropTypes.func, nonEditableRows: PropTypes.func,
timeToCloseMessage: PropTypes.number, timeToCloseMessage: PropTypes.number,
errorMessage: PropTypes.string errorMessage: PropTypes.any
}) })
} }
@@ -33,41 +39,32 @@ export default (
this.state = { this.state = {
ridx: null, ridx: null,
cidx: null, cidx: null,
message: null, message: null
isDataChanged: false
}; };
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.cellEdit && this.isRemoteCellEdit()) { if (nextProps.cellEdit && isRemoteCellEdit()) {
if (nextProps.cellEdit.options.errorMessage) { if (nextProps.cellEdit.options.errorMessage) {
this.setState(() => ({ this.setState(() => ({
isDataChanged: false,
message: nextProps.cellEdit.options.errorMessage message: nextProps.cellEdit.options.errorMessage
})); }));
} else { } else {
this.setState(() => ({
isDataChanged: true
}));
this.escapeEditing(); this.escapeEditing();
} }
} else {
this.setState(() => ({
isDataChanged: false
}));
} }
} }
handleCellUpdate(row, column, newValue) { handleCellUpdate(row, column, newValue) {
const { keyField, cellEdit, store } = this.props; const { keyField, cellEdit, data } = this.props;
const { beforeSaveCell, afterSaveCell } = cellEdit.options; const { beforeSaveCell, afterSaveCell } = cellEdit.options;
const oldValue = _.get(row, column.dataField); const oldValue = _.get(row, column.dataField);
const rowId = _.get(row, keyField); const rowId = _.get(row, keyField);
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column); if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
if (this.isRemoteCellEdit()) { if (isRemoteCellEdit()) {
this.handleCellChange(rowId, column.dataField, newValue); handleCellChange(rowId, column.dataField, newValue);
} else { } else {
store.edit(rowId, column.dataField, newValue); dataOperator.editCell(data, keyField, rowId, column.dataField, newValue);
if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column); if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column);
this.completeEditing(); this.completeEditing();
} }
@@ -77,8 +74,7 @@ export default (
this.setState(() => ({ this.setState(() => ({
ridx: null, ridx: null,
cidx: null, cidx: null,
message: null, message: null
isDataChanged: true
})); }));
} }
@@ -86,8 +82,7 @@ export default (
const editing = () => { const editing = () => {
this.setState(() => ({ this.setState(() => ({
ridx, ridx,
cidx, cidx
isDataChanged: false
})); }));
}; };
@@ -103,18 +98,19 @@ export default (
} }
render() { render() {
const { isDataChanged, ...stateRest } = this.state;
const { const {
cellEdit: { cellEdit: {
options: { nonEditableRows, errorMessage, ...optionsRest }, options: { nonEditableRows, errorMessage, ...optionsRest },
editingCellFactory, editingCellFactory,
createContext,
...cellEditRest ...cellEditRest
} }
} = this.props; } = this.props;
const newCellEdit = { const newCellEdit = {
...optionsRest, ...optionsRest,
...cellEditRest, ...cellEditRest,
...stateRest, ...this.state,
EditingCell, EditingCell,
nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [], nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [],
onStart: this.startEditing, onStart: this.startEditing,
@@ -123,13 +119,16 @@ export default (
}; };
return ( return (
<Base <CellEditContext.Provider
{ ...this.props } value={ { cellEdit: newCellEdit } }
data={ this.props.store.data } >
isDataChanged={ isDataChanged } { this.props.children }
cellEdit={ newCellEdit } </CellEditContext.Provider>
/>
); );
} }
}
return {
Provider: CellEditProvider,
Consumer: CellEditContext.Consumer
}; };
}; };

View File

@@ -0,0 +1,42 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class DateEditor extends Component {
componentDidMount() {
const { defaultValue } = this.props;
this.date.valueAsDate = new Date(defaultValue);
this.date.focus();
}
getValue() {
return this.date.value;
}
render() {
const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('form-control editor edit-date', className);
return (
<input
ref={ node => this.date = node }
type="date"
className={ editorClass }
{ ...rest }
/>
);
}
}
DateEditor.propTypes = {
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
]),
defaultValue: PropTypes.string
};
DateEditor.defaultProps = {
className: '',
defaultValue: ''
};
export default DateEditor;

View File

@@ -0,0 +1,61 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class DropDownEditor extends Component {
componentDidMount() {
const { defaultValue } = this.props;
this.select.value = defaultValue;
this.select.focus();
}
getValue() {
return this.select.value;
}
render() {
const { defaultValue, className, options, ...rest } = this.props;
const editorClass = cs('form-control editor edit-select', className);
const attr = {
...rest,
className: editorClass
};
return (
<select
{ ...attr }
ref={ node => this.select = node }
defaultValue={ defaultValue }
>
{
options.map(({ label, value }) => (
<option key={ value } value={ value }>{ label }</option>
))
}
</select>
);
}
}
DropDownEditor.propTypes = {
defaultValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
className: PropTypes.string,
style: PropTypes.object,
options: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
value: PropTypes.any
}))
]).isRequired
};
DropDownEditor.defaultProps = {
className: '',
defaultValue: '',
style: {}
};
export default DropDownEditor;

View File

@@ -6,15 +6,21 @@ import React, { Component } from 'react';
import cs from 'classnames'; import cs from 'classnames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import DropdownEditor from './dropdown-editor';
import TextAreaEditor from './textarea-editor';
import CheckBoxEditor from './checkbox-editor';
import DateEditor from './date-editor';
import TextEditor from './text-editor'; import TextEditor from './text-editor';
import EditorIndicator from './editor-indicator'; import EditorIndicator from './editor-indicator';
import { TIME_TO_CLOSE_MESSAGE } from './const'; import { TIME_TO_CLOSE_MESSAGE, EDITTYPE } from './const';
export default _ => export default _ =>
class EditingCell extends Component { class EditingCell extends Component {
static propTypes = { static propTypes = {
row: PropTypes.object.isRequired, row: PropTypes.object.isRequired,
rowIndex: PropTypes.number.isRequired,
column: PropTypes.object.isRequired, column: PropTypes.object.isRequired,
columnIndex: PropTypes.number.isRequired,
onUpdate: PropTypes.func.isRequired, onUpdate: PropTypes.func.isRequired,
onEscape: PropTypes.func.isRequired, onEscape: PropTypes.func.isRequired,
timeToCloseMessage: PropTypes.number, timeToCloseMessage: PropTypes.number,
@@ -71,8 +77,8 @@ export default _ =>
}, timeToCloseMessage); }, timeToCloseMessage);
} }
beforeComplete(row, column, newValue) { beforeComplete(newValue) {
const { onUpdate } = this.props; const { onUpdate, row, column } = this.props;
if (_.isFunction(column.validator)) { if (_.isFunction(column.validator)) {
const validateForm = column.validator(newValue, row, column); const validateForm = column.validator(newValue, row, column);
if (_.isObject(validateForm) && !validateForm.valid) { if (_.isObject(validateForm) && !validateForm.valid) {
@@ -87,28 +93,20 @@ export default _ =>
} }
handleBlur() { handleBlur() {
const { onEscape, blurToSave, row, column } = this.props; const { onEscape, blurToSave } = this.props;
if (blurToSave) { if (blurToSave) {
const value = this.editor.text.value; this.beforeComplete(this.editor.getValue());
if (!_.isDefined(value)) {
// TODO: for other custom or embed editor
}
this.beforeComplete(row, column, value);
} else { } else {
onEscape(); onEscape();
} }
} }
handleKeyDown(e) { handleKeyDown(e) {
const { onEscape, row, column } = this.props; const { onEscape } = this.props;
if (e.keyCode === 27) { // ESC if (e.keyCode === 27) { // ESC
onEscape(); onEscape();
} else if (e.keyCode === 13) { // ENTER } else if (e.keyCode === 13) { // ENTER
const value = e.currentTarget.value; this.beforeComplete(this.editor.getValue());
if (!_.isDefined(value)) {
// TODO: for other custom or embed editor
}
this.beforeComplete(row, column, value);
} }
} }
@@ -122,31 +120,73 @@ export default _ =>
} }
render() { render() {
const { invalidMessage } = this.state; let editor;
const { row, column, className, style } = this.props; const { row, column, className, style, rowIndex, columnIndex } = this.props;
const { dataField } = column; const { dataField } = column;
const value = _.get(row, dataField); const value = _.get(row, dataField);
const editorAttrs = { const hasError = _.isDefined(this.state.invalidMessage);
let customEditorClass = column.editorClasses || '';
if (_.isFunction(column.editorClasses)) {
customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex);
}
let editorStyle = column.editorStyle || {};
if (_.isFunction(column.editorStyle)) {
editorStyle = column.editorStyle(value, row, rowIndex, columnIndex);
}
const editorClass = cs({
animated: hasError,
shake: hasError
}, customEditorClass);
let editorProps = {
ref: node => this.editor = node,
defaultValue: value,
style: editorStyle,
className: editorClass,
onKeyDown: this.handleKeyDown, onKeyDown: this.handleKeyDown,
onBlur: this.handleBlur onBlur: this.handleBlur
}; };
const hasError = _.isDefined(invalidMessage); const isDefaultEditorDefined = _.isObject(column.editor);
const editorClass = hasError ? cs('animated', 'shake') : null;
if (isDefaultEditorDefined) {
editorProps = {
...editorProps,
...column.editor
};
} else if (_.isFunction(column.editorRenderer)) {
editorProps = {
...editorProps,
onUpdate: this.beforeComplete
};
}
if (_.isFunction(column.editorRenderer)) {
editor = column.editorRenderer(editorProps, value, row, column, rowIndex, columnIndex);
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.SELECT) {
editor = <DropdownEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.TEXTAREA) {
editor = <TextAreaEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.CHECKBOX) {
editor = <CheckBoxEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.DATE) {
editor = <DateEditor { ...editorProps } />;
} else {
editor = <TextEditor { ...editorProps } />;
}
return ( return (
<td <td
className={ cs('react-bootstrap-table-editing-cell', className) } className={ cs('react-bootstrap-table-editing-cell', className) }
style={ style } style={ style }
onClick={ this.handleClick } onClick={ this.handleClick }
> >
<TextEditor { editor }
ref={ node => this.editor = node } { hasError ? <EditorIndicator invalidMessage={ this.state.invalidMessage } /> : null }
defaultValue={ value }
className={ editorClass }
{ ...editorAttrs }
/>
{ hasError ? <EditorIndicator invalidMessage={ invalidMessage } /> : null }
</td> </td>
); );
} }

View File

@@ -10,6 +10,10 @@ class TextEditor extends Component {
this.text.focus(); this.text.focus();
} }
getValue() {
return this.text.value;
}
render() { render() {
const { defaultValue, className, ...rest } = this.props; const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('form-control editor edit-text', className); const editorClass = cs('form-control editor edit-text', className);
@@ -32,9 +36,10 @@ TextEditor.propTypes = {
defaultValue: PropTypes.oneOfType([ defaultValue: PropTypes.oneOfType([
PropTypes.string, PropTypes.string,
PropTypes.number PropTypes.number
]).isRequired ])
}; };
TextEditor.defaultProps = { TextEditor.defaultProps = {
className: null className: null,
defaultValue: ''
}; };
export default TextEditor; export default TextEditor;

View File

@@ -0,0 +1,60 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class TextAreaEditor extends Component {
constructor(props) {
super(props);
this.handleKeyDown = this.handleKeyDown.bind(this);
}
componentDidMount() {
const { defaultValue } = this.props;
this.text.value = defaultValue;
this.text.focus();
}
getValue() {
return this.text.value;
}
handleKeyDown(e) {
if (e.keyCode === 13 && !e.shiftKey) return;
if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}
}
render() {
const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('form-control editor edit-textarea', className);
return (
<textarea
ref={ node => this.text = node }
type="textarea"
className={ editorClass }
{ ...rest }
onKeyDown={ this.handleKeyDown }
/>
);
}
}
TextAreaEditor.propTypes = {
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
]),
defaultValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
onKeyDown: PropTypes.func
};
TextAreaEditor.defaultProps = {
className: '',
defaultValue: '',
onKeyDown: undefined
};
export default TextAreaEditor;

View File

@@ -0,0 +1,430 @@
import 'jsdom-global/register';
import React from 'react';
import { shallow } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils';
import dataOperator from 'react-bootstrap-table-next/src/store/operators';
import BootstrapTable from 'react-bootstrap-table-next/src/bootstrap-table';
import {
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK
} from '../src/const';
import createCellEditContext from '../src/context';
import cellEditFactory from '../index';
describe('CellEditContext', () => {
let wrapper;
let cellEdit;
let CellEditContext;
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}];
const keyField = 'id';
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}];
const defaultCellEdit = {
mode: CLICK_TO_CELL_EDIT
};
const defaultSelectRow = undefined;
const mockBase = jest.fn((props => (
<BootstrapTable
data={ data }
columns={ columns }
keyField={ keyField }
{ ...props }
/>
)));
const handleCellChange = jest.fn();
function shallowContext(
customCellEdit = defaultCellEdit,
enableRemote = false,
selectRow = defaultSelectRow
) {
mockBase.mockReset();
handleCellChange.mockReset();
CellEditContext = createCellEditContext(
_,
dataOperator,
jest.fn().mockReturnValue(enableRemote),
handleCellChange
);
cellEdit = cellEditFactory(customCellEdit);
return (
<CellEditContext.Provider
cellEdit={ cellEdit }
keyField={ keyField }
columns={ columns }
selectRow={ selectRow }
data={ data }
>
<CellEditContext.Consumer>
{
cellEditProps => mockBase(cellEditProps)
}
</CellEditContext.Consumer>
</CellEditContext.Provider>
);
}
describe('default render', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.render();
});
it('should have correct Provider property after calling createCellEditContext', () => {
expect(CellEditContext.Provider).toBeDefined();
});
it('should have correct Consumer property after calling createCellEditContext', () => {
expect(CellEditContext.Consumer).toBeDefined();
});
it('should have correct state.ridx', () => {
expect(wrapper.state().ridx).toBeNull();
});
it('should have correct state.cidx', () => {
expect(wrapper.state().cidx).toBeNull();
});
it('should have correct state.message', () => {
expect(wrapper.state().message).toBeNull();
});
it('should pass correct cell editing props to children element', () => {
expect(wrapper.length).toBe(1);
expect(JSON.stringify(mockBase.mock.calls[0])).toEqual(JSON.stringify([{
cellEdit: {
...defaultCellEdit,
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK,
...wrapper.state(),
nonEditableRows: []
}
}]));
});
});
describe('componentWillReceiveProps', () => {
const initialState = { ridx: 1, cidx: 1, message: 'test' };
describe('if nextProps.cellEdit is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.setState(initialState);
wrapper.render();
wrapper.instance().componentWillReceiveProps({});
});
it('should not set state.message', () => {
expect(wrapper.state().message).toBe(initialState.message);
});
it('should not set state.ridx', () => {
expect(wrapper.state().ridx).toBe(initialState.ridx);
});
it('should not set state.cidx', () => {
expect(wrapper.state().cidx).toBe(initialState.cidx);
});
});
describe('if nextProps.cellEdit is existing but remote cell editing is disable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.setState(initialState);
wrapper.render();
wrapper.instance().componentWillReceiveProps({
cellEdit: cellEditFactory(defaultCellEdit)
});
});
it('should not set state.message', () => {
expect(wrapper.state().message).toBe(initialState.message);
});
it('should not set state.ridx', () => {
expect(wrapper.state().ridx).toBe(initialState.ridx);
});
it('should not set state.cidx', () => {
expect(wrapper.state().cidx).toBe(initialState.cidx);
});
});
describe('if nextProps.cellEdit is existing and remote cell editing is enable', () => {
describe('if nextProps.cellEdit.options.errorMessage is defined', () => {
let message;
beforeEach(() => {
message = 'validation fail';
wrapper = shallow(shallowContext(defaultCellEdit, true));
wrapper.setState(initialState);
wrapper.render();
wrapper.instance().componentWillReceiveProps({
cellEdit: cellEditFactory({
...defaultCellEdit,
errorMessage: message
})
});
wrapper.update();
});
it('should set state.message', () => {
expect(wrapper.state('message')).toBe(message);
});
it('should not set state.ridx', () => {
expect(wrapper.state().ridx).toBe(initialState.ridx);
});
it('should not set state.cidx', () => {
expect(wrapper.state().cidx).toBe(initialState.cidx);
});
});
describe('if nextProps.cellEdit.options.errorMessage is not defined', () => {
beforeEach(() => {
wrapper = shallow(shallowContext(defaultCellEdit, true));
wrapper.setState(initialState);
wrapper.instance().componentWillReceiveProps({
cellEdit: cellEditFactory({ ...defaultCellEdit })
});
wrapper.update();
});
it('should not set state.message', () => {
expect(wrapper.state('message')).toBe(initialState.message);
});
it('should set correct state.ridx', () => {
expect(wrapper.state().ridx).toBeNull();
});
it('should set correct state.cidx', () => {
expect(wrapper.state().cidx).toBeNull();
});
});
});
});
describe('handleCellUpdate', () => {
const row = data[1];
const column = columns[1];
const newValue = 'This is new value';
const oldValue = row[column.dataField];
describe('if cellEdit.beforeSaveCell prop is defined', () => {
const beforeSaveCell = jest.fn();
beforeEach(() => {
beforeSaveCell.mockReset();
wrapper = shallow(shallowContext({
...defaultCellEdit,
beforeSaveCell
}));
wrapper.instance().handleCellUpdate(
row,
column,
newValue
);
});
it('should call cellEdit.beforeSaveCell correctly', () => {
expect(beforeSaveCell).toHaveBeenCalledTimes(1);
expect(beforeSaveCell).toHaveBeenCalledWith(oldValue, newValue, row, column);
});
});
describe('when remote cell editing is enable', () => {
const afterSaveCell = jest.fn();
beforeEach(() => {
afterSaveCell.mockReset();
wrapper = shallow(shallowContext({
...defaultCellEdit,
afterSaveCell
}, true));
wrapper.instance().handleCellUpdate(
row,
column,
newValue
);
});
it('should call handleCellChange correctly', () => {
expect(handleCellChange).toHaveBeenCalledTimes(1);
expect(handleCellChange).toHaveBeenCalledWith(row[keyField], column.dataField, newValue);
});
it('should not call cellEdit.afterSaveCell even if it is defined', () => {
expect(afterSaveCell).toHaveBeenCalledTimes(0);
});
});
describe('when remote cell editing is disable', () => {
const afterSaveCell = jest.fn();
beforeEach(() => {
afterSaveCell.mockReset();
wrapper = shallow(shallowContext({
...defaultCellEdit,
afterSaveCell
}));
wrapper.setState({
ridx: 1,
cidx: 1
});
wrapper.instance().handleCellUpdate(
row,
column,
newValue
);
});
it('should not call handleCellChange correctly', () => {
expect(handleCellChange).toHaveBeenCalledTimes(0);
});
it('should set state correctly', () => {
expect(wrapper.state('ridx')).toBeNull();
expect(wrapper.state('cidx')).toBeNull();
expect(wrapper.state('message')).toBeNull();
});
it('should call cellEdit.afterSaveCell if it is defined', () => {
expect(afterSaveCell).toHaveBeenCalledTimes(1);
});
});
});
describe('completeEditing', () => {
const initialState = { ridx: 1, cidx: 1, message: 'test' };
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.setState(initialState);
wrapper.render();
wrapper.instance().completeEditing();
});
it('should set state correctly', () => {
expect(wrapper.state().ridx).toBeNull();
expect(wrapper.state().cidx).toBeNull();
expect(wrapper.state().message).toBeNull();
});
});
describe('startEditing', () => {
const ridx = 0;
const cidx = 1;
describe('if selectRow prop is not defined', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.render();
wrapper.instance().startEditing(ridx, cidx);
});
it('should set state correctly', () => {
expect(wrapper.state().ridx).toEqual(ridx);
expect(wrapper.state().cidx).toEqual(cidx);
});
});
describe('if selectRow prop is defined', () => {
describe('and selectRow.clickToEdit is enable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext(
defaultCellEdit,
false,
{
...defaultSelectRow,
clickToEdit: true
}
));
wrapper.render();
wrapper.instance().startEditing(ridx, cidx);
});
it('should set state correctly', () => {
expect(wrapper.state().ridx).toEqual(ridx);
expect(wrapper.state().cidx).toEqual(cidx);
});
});
describe('and selectRow.clickToSelect is disable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext(
defaultCellEdit,
false,
{
...defaultSelectRow,
clickToSelect: false
}
));
wrapper.render();
wrapper.instance().startEditing(ridx, cidx);
});
it('should set state correctly', () => {
expect(wrapper.state().ridx).toEqual(ridx);
expect(wrapper.state().cidx).toEqual(cidx);
});
});
describe('and selectRow.clickToEdit & selectRow.clickToSelect is enable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext(
defaultCellEdit,
false,
{
...defaultSelectRow,
clickToEdit: false,
clickToSelect: true
}
));
wrapper.render();
wrapper.instance().startEditing(ridx, cidx);
});
it('should not set state', () => {
expect(wrapper.state().ridx).toBeNull();
expect(wrapper.state().cidx).toBeNull();
});
});
});
});
describe('escapeEditing', () => {
const initialState = { ridx: 1, cidx: 1 };
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.setState(initialState);
wrapper.instance().escapeEditing();
});
it('should set state correctly', () => {
expect(wrapper.state().ridx).toBeNull();
expect(wrapper.state().cidx).toBeNull();
});
});
});

View File

@@ -6,7 +6,12 @@ import { shallow, mount } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils'; import _ from 'react-bootstrap-table-next/src/utils';
import editingCellFactory from '../src/editing-cell'; import editingCellFactory from '../src/editing-cell';
import * as constants from '../src/const';
import TextEditor from '../src/text-editor'; import TextEditor from '../src/text-editor';
import DateEditor from '../src/date-editor';
import DropDownEditor from '../src/dropdown-editor';
import TextAreaEditor from '../src/textarea-editor';
import CheckBoxEditor from '../src/checkbox-editor';
import EditorIndicator from '../src/editor-indicator'; import EditorIndicator from '../src/editor-indicator';
const EditingCell = editingCellFactory(_); const EditingCell = editingCellFactory(_);
@@ -28,6 +33,9 @@ describe('EditingCell', () => {
name: 'A' name: 'A'
}; };
const rowIndex = 1;
const columnIndex = 1;
let column = { let column = {
dataField: 'id', dataField: 'id',
text: 'ID' text: 'ID'
@@ -36,9 +44,11 @@ describe('EditingCell', () => {
beforeEach(() => { beforeEach(() => {
onEscape = sinon.stub(); onEscape = sinon.stub();
onUpdate = sinon.stub(); onUpdate = sinon.stub();
wrapper = shallow( wrapper = mount(
<EditingCell <EditingCell
row={ row } row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column } column={ column }
onUpdate={ onUpdate } onUpdate={ onUpdate }
onEscape={ onEscape } onEscape={ onEscape }
@@ -58,7 +68,7 @@ describe('EditingCell', () => {
expect(textEditor.props().defaultValue).toEqual(row[column.dataField]); expect(textEditor.props().defaultValue).toEqual(row[column.dataField]);
expect(textEditor.props().onKeyDown).toBeDefined(); expect(textEditor.props().onKeyDown).toBeDefined();
expect(textEditor.props().onBlur).toBeDefined(); expect(textEditor.props().onBlur).toBeDefined();
expect(textEditor.props().className).toBeNull(); expect(textEditor.props().className).toEqual('');
}); });
it('should not render EditorIndicator due to state.invalidMessage is null', () => { it('should not render EditorIndicator due to state.invalidMessage is null', () => {
@@ -69,7 +79,8 @@ describe('EditingCell', () => {
it('when press ENTER on TextEditor should call onUpdate correctly', () => { it('when press ENTER on TextEditor should call onUpdate correctly', () => {
const newValue = 'test'; const newValue = 'test';
const textEditor = wrapper.find(TextEditor); const textEditor = wrapper.find(TextEditor);
textEditor.simulate('keyDown', { keyCode: 13, currentTarget: { value: newValue } }); sinon.stub(textEditor.instance(), 'getValue').returns(newValue);
textEditor.simulate('keyDown', { keyCode: 13 });
expect(onUpdate.callCount).toBe(1); expect(onUpdate.callCount).toBe(1);
expect(onUpdate.calledWith(row, column, newValue)).toBe(true); expect(onUpdate.calledWith(row, column, newValue)).toBe(true);
}); });
@@ -92,6 +103,8 @@ describe('EditingCell', () => {
wrapper = shallow( wrapper = shallow(
<EditingCell <EditingCell
row={ row } row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column } column={ column }
onUpdate={ onUpdate } onUpdate={ onUpdate }
onEscape={ onEscape } onEscape={ onEscape }
@@ -112,6 +125,8 @@ describe('EditingCell', () => {
wrapper = shallow( wrapper = shallow(
<EditingCell <EditingCell
row={ row } row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column } column={ column }
onUpdate={ onUpdate } onUpdate={ onUpdate }
onEscape={ onEscape } onEscape={ onEscape }
@@ -126,12 +141,140 @@ describe('EditingCell', () => {
}); });
}); });
describe('if column.editorClasses is defined', () => {
let columnWithEditorClasses;
const classes = 'test test1';
describe('and it is a function', () => {
beforeEach(() => {
columnWithEditorClasses = {
...column,
editorClasses: jest.fn(() => classes)
};
wrapper = shallow(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ columnWithEditorClasses }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render TextEditor with correct props', () => {
const textEditor = wrapper.find(TextEditor);
expect(textEditor.props().className).toEqual(classes);
});
it('should call column.editorClasses correctly', () => {
expect(columnWithEditorClasses.editorClasses).toHaveBeenCalledTimes(1);
expect(columnWithEditorClasses.editorClasses).toHaveBeenCalledWith(
_.get(row, column.dataField),
row,
rowIndex,
columnIndex
);
});
});
describe('and it is a string', () => {
beforeEach(() => {
columnWithEditorClasses = {
...column,
editorClasses: classes
};
wrapper = shallow(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ columnWithEditorClasses }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render TextEditor with correct props', () => {
const textEditor = wrapper.find(TextEditor);
expect(textEditor.props().className).toEqual(classes);
});
});
});
describe('if column.editorStyle is defined', () => {
let columnWithEditorStyle;
const style = { color: 'red' };
describe('and it is a function', () => {
beforeEach(() => {
columnWithEditorStyle = {
...column,
editorStyle: jest.fn(() => style)
};
wrapper = shallow(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ columnWithEditorStyle }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render TextEditor with correct props', () => {
const textEditor = wrapper.find(TextEditor);
expect(textEditor.props().style).toEqual(style);
});
it('should call column.editorStyle correctly', () => {
expect(columnWithEditorStyle.editorStyle).toHaveBeenCalledTimes(1);
expect(columnWithEditorStyle.editorStyle).toHaveBeenCalledWith(
_.get(row, column.dataField),
row,
rowIndex,
columnIndex
);
});
});
describe('and it is an object', () => {
beforeEach(() => {
columnWithEditorStyle = {
...column,
editorStyle: style
};
wrapper = shallow(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ columnWithEditorStyle }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render TextEditor with correct props', () => {
const textEditor = wrapper.find(TextEditor);
expect(textEditor.props().style).toEqual(style);
});
});
});
describe('if blurToSave prop is true', () => { describe('if blurToSave prop is true', () => {
beforeEach(() => { beforeEach(() => {
wrapper = mount( wrapper = mount(
<TableRowWrapper> <TableRowWrapper>
<EditingCell <EditingCell
row={ row } row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column } column={ column }
onUpdate={ onUpdate } onUpdate={ onUpdate }
onEscape={ onEscape } onEscape={ onEscape }
@@ -167,12 +310,14 @@ describe('EditingCell', () => {
wrapper = mount( wrapper = mount(
<EditingCell <EditingCell
row={ row } row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column } column={ column }
onUpdate={ onUpdate } onUpdate={ onUpdate }
onEscape={ onEscape } onEscape={ onEscape }
/> />
); );
wrapper.instance().beforeComplete(row, column, newValue); wrapper.instance().beforeComplete(newValue);
}); });
it('should call column.validator successfully', () => { it('should call column.validator successfully', () => {
@@ -218,7 +363,17 @@ describe('EditingCell', () => {
text: 'ID', text: 'ID',
validator: validatorCallBack validator: validatorCallBack
}; };
wrapper.instance().beforeComplete(row, column, newValue); wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
wrapper.instance().beforeComplete(newValue);
}); });
it('should call column.validator successfully', () => { it('should call column.validator successfully', () => {
@@ -231,4 +386,156 @@ describe('EditingCell', () => {
}); });
}); });
}); });
describe('if column.editorRenderer is defined', () => {
const TestEditor = () => <input type="text" />;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editorRenderer: sinon.stub().returns(<TestEditor />)
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should call column.editorRenderer correctly', () => {
expect(column.editorRenderer.callCount).toBe(1);
});
it('should render correctly', () => {
expect(wrapper.find(TestEditor)).toHaveLength(1);
});
});
describe('if column.editor is select', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.SELECT,
options: [{
value: 1,
label: 'A'
}]
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render dropdown editor successfully', () => {
const editor = wrapper.find(DropDownEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
expect(editor.props().options).toEqual(column.editor.options);
});
});
describe('if column.editor is textarea', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.TEXTAREA
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render textarea editor successfully', () => {
const editor = wrapper.find(TextAreaEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
});
});
describe('if column.editor is checkbox', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.CHECKBOX
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render checkbox editor successfully', () => {
const editor = wrapper.find(CheckBoxEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
});
});
describe('if column.editor is date', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.DATE
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render date editor successfully', () => {
const editor = wrapper.find(DateEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
});
});
}); });

View File

@@ -1,330 +0,0 @@
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils';
import remoteResolver from 'react-bootstrap-table-next/src/props-resolver/remote-resolver';
import Store from 'react-bootstrap-table-next/src/store';
import BootstrapTable from 'react-bootstrap-table-next/src/bootstrap-table';
import cellEditFactory from '..';
import * as Const from '../src/const';
import wrapperFactory from '../src/wrapper';
describe('CellEditWrapper', () => {
let wrapper;
let instance;
const onTableChangeCB = sinon.stub();
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}];
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}];
const createTableProps = (props = {}) => {
const { cellEdit, ...rest } = props;
const tableProps = {
keyField: 'id',
columns,
data,
_,
store: new Store('id'),
cellEdit: cellEditFactory(cellEdit),
onTableChange: onTableChangeCB,
...rest
};
tableProps.store.data = data;
return tableProps;
};
const CellEditWrapper = wrapperFactory(BootstrapTable, {
_,
remoteResolver
});
const createCellEditWrapper = (props, renderFragment = true) => {
wrapper = shallow(<CellEditWrapper { ...props } />);
instance = wrapper.instance();
if (renderFragment) {
const fragment = instance.render();
wrapper = shallow(<div>{ fragment }</div>);
}
};
afterEach(() => {
onTableChangeCB.reset();
});
beforeEach(() => {
const props = createTableProps({
cellEdit: { mode: Const.CLICK_TO_CELL_EDIT }
});
createCellEditWrapper(props);
});
it('should render CellEditWrapper correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(BootstrapTable)).toBeDefined();
});
it('should have correct state', () => {
expect(instance.state.ridx).toBeNull();
expect(instance.state.cidx).toBeNull();
expect(instance.state.message).toBeNull();
expect(instance.state.isDataChanged).toBeFalsy();
});
it('should inject correct props to base component', () => {
const base = wrapper.find(BootstrapTable);
expect(base.props().cellEdit).toBeDefined();
expect(base.props().cellEdit.onStart).toBeDefined();
expect(base.props().cellEdit.onEscape).toBeDefined();
expect(base.props().cellEdit.onUpdate).toBeDefined();
expect(base.props().cellEdit.EditingCell).toBeDefined();
expect(base.props().cellEdit.ridx).toBeNull();
expect(base.props().cellEdit.cidx).toBeNull();
expect(base.props().cellEdit.message).toBeNull();
expect(base.props().isDataChanged).toBe(instance.state.isDataChanged);
});
describe('when receive new cellEdit prop', () => {
const spy = jest.spyOn(CellEditWrapper.prototype, 'escapeEditing');
describe('and cellEdit is not work on remote', () => {
beforeEach(() => {
const props = createTableProps({
cellEdit: { mode: Const.CLICK_TO_CELL_EDIT }
});
createCellEditWrapper(props);
wrapper.setProps({ cellEdit: props.cellEdit });
});
it('should always setting state.isDataChanged as false', () => {
expect(instance.state.isDataChanged).toBeFalsy();
});
});
describe('and cellEdit is work on remote', () => {
let errorMessage;
let props;
beforeEach(() => {
props = createTableProps({
cellEdit: { mode: Const.CLICK_TO_CELL_EDIT },
remote: true
});
});
describe('and cellEdit.errorMessage is defined', () => {
beforeEach(() => {
createCellEditWrapper(props, false);
errorMessage = 'test';
const newCellEdit = {
...props.cellEdit,
options: { ...props.cellEdit.options, errorMessage }
};
wrapper.setProps({ cellEdit: newCellEdit });
});
it('should setting correct state', () => {
expect(instance.state.isDataChanged).toBeFalsy();
expect(instance.state.message).toEqual(errorMessage);
});
});
describe('and cellEdit.errorMessage is undefined', () => {
beforeEach(() => {
errorMessage = null;
createCellEditWrapper(props, false);
const newCellEdit = {
...props.cellEdit,
options: { ...props.cellEdit.options, errorMessage }
};
wrapper.setProps({ cellEdit: newCellEdit });
});
it('should setting correct state', () => {
expect(wrapper.state().isDataChanged).toBeTruthy();
});
it('should escape current editing', () => {
expect(spy).toHaveBeenCalled();
});
});
});
});
describe('call escapeEditing function', () => {
it('should set state correctly', () => {
instance.escapeEditing();
expect(instance.state.ridx).toBeNull();
expect(instance.state.cidx).toBeNull();
});
});
describe('call startEditing function', () => {
const ridx = 1;
const cidx = 3;
it('should set state correctly', () => {
instance.startEditing(ridx, cidx);
expect(instance.state.ridx).toEqual(ridx);
expect(instance.state.cidx).toEqual(cidx);
expect(instance.state.isDataChanged).toBeFalsy();
});
describe('if selectRow.clickToSelect is defined', () => {
beforeEach(() => {
const selectRow = { mode: 'checkbox', clickToSelect: true };
const props = createTableProps({
cellEdit: { mode: Const.CLICK_TO_CELL_EDIT },
selectRow
});
createCellEditWrapper(props);
});
it('should not set state', () => {
instance.startEditing(ridx, cidx);
expect(instance.state.ridx).toBeNull();
expect(instance.state.cidx).toBeDefined();
});
});
describe('if selectRow.clickToSelect and selectRow.clickToEdit is defined', () => {
beforeEach(() => {
const selectRow = { mode: 'checkbox', clickToSelect: true, clickToEdit: true };
const props = createTableProps({
cellEdit: { mode: Const.CLICK_TO_CELL_EDIT },
selectRow
});
createCellEditWrapper(props);
});
it('should set state correctly', () => {
instance.startEditing(ridx, cidx);
expect(instance.state.ridx).toEqual(ridx);
expect(instance.state.cidx).toEqual(cidx);
});
});
});
describe('call completeEditing function', () => {
it('should set state correctly', () => {
instance.completeEditing();
expect(instance.state.ridx).toBeNull();
expect(instance.state.cidx).toBeNull();
expect(instance.state.message).toBeNull();
expect(instance.state.isDataChanged).toBeTruthy();
});
});
describe('call handleCellUpdate function', () => {
let props;
const row = data[0];
const column = columns[1];
const newValue = 'new name';
describe('when cell edit is work on remote', () => {
const spy = jest.spyOn(CellEditWrapper.prototype, 'handleCellChange');
beforeEach(() => {
props = createTableProps({
cellEdit: { mode: Const.CLICK_TO_CELL_EDIT },
remote: true
});
createCellEditWrapper(props);
instance.handleCellUpdate(row, column, newValue);
});
it('should calling handleCellChange correctly', () => {
expect(spy).toHaveBeenCalled();
expect(spy.mock.calls).toHaveLength(1);
expect(spy.mock.calls[0]).toHaveLength(3);
expect(spy.mock.calls[0][0]).toEqual(row.id);
expect(spy.mock.calls[0][1]).toEqual(column.dataField);
expect(spy.mock.calls[0][2]).toEqual(newValue);
});
});
describe('when cell edit is not work on remote', () => {
const spyOnCompleteEditing = jest.spyOn(CellEditWrapper.prototype, 'completeEditing');
const spyOnStoreEdit = jest.spyOn(Store.prototype, 'edit');
beforeEach(() => {
props = createTableProps({
cellEdit: { mode: Const.CLICK_TO_CELL_EDIT }
});
createCellEditWrapper(props);
instance.handleCellUpdate(row, column, newValue);
});
afterEach(() => {
spyOnStoreEdit.mockReset();
spyOnCompleteEditing.mockReset();
});
it('should calling props.store.edit', () => {
expect(spyOnStoreEdit).toHaveBeenCalled();
expect(spyOnStoreEdit.mock.calls).toHaveLength(1);
expect(spyOnStoreEdit.mock.calls[0]).toHaveLength(3);
expect(spyOnStoreEdit.mock.calls[0][0]).toEqual(row.id);
expect(spyOnStoreEdit.mock.calls[0][1]).toEqual(column.dataField);
expect(spyOnStoreEdit.mock.calls[0][2]).toEqual(newValue);
});
it('should calling completeEditing function', () => {
expect(spyOnCompleteEditing).toHaveBeenCalled();
});
describe('if cellEdit.afterSaveCell prop defined', () => {
const aftereSaveCellCallBack = sinon.stub();
beforeEach(() => {
props = createTableProps({
cellEdit: {
mode: Const.CLICK_TO_CELL_EDIT,
afterSaveCell: aftereSaveCellCallBack
}
});
createCellEditWrapper(props);
instance.handleCellUpdate(row, column, newValue);
});
it('should calling cellEdit.afterSaveCell correctly', () => {
expect(aftereSaveCellCallBack.callCount).toBe(1);
expect(aftereSaveCellCallBack.calledWith(
row[column.dataField], newValue, row, column)
).toBe(true);
});
});
});
describe('if cellEdit.beforeSaveCell prop defined', () => {
const beforeSaveCellCallBack = sinon.stub();
beforeEach(() => {
props = createTableProps({
cellEdit: {
mode: Const.CLICK_TO_CELL_EDIT,
beforeSaveCell: beforeSaveCellCallBack
}
});
createCellEditWrapper(props);
instance.handleCellUpdate(row, column, newValue);
});
it('should calling cellEdit.beforeSaveCell correctly', () => {
expect(beforeSaveCellCallBack.callCount).toBe(1);
expect(beforeSaveCellCallBack.calledWith(
row[column.dataField], newValue, row, column)
).toBe(true);
});
});
});
});

View File

@@ -1,2 +1,2 @@
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script> <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
<!-- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css" integrity="sha384-Smlep5jCw/wG7hdkwQ/Z5nLIefveQRIY9nfy6xoR1uRYBtpZgI6339F5dgvm/e9B" crossorigin="anonymous"> -->

View File

@@ -8,6 +8,7 @@ const editorSourcePath = path.join(__dirname, '../../react-bootstrap-table2-edit
const sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style'); const sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style');
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style'); const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style'); const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style');
const toolkitSourcePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/index.js');
const storyPath = path.join(__dirname, '../stories'); const storyPath = path.join(__dirname, '../stories');
const examplesPath = path.join(__dirname, '../examples'); const examplesPath = path.join(__dirname, '../examples');
const srcPath = path.join(__dirname, '../src'); const srcPath = path.join(__dirname, '../src');
@@ -23,6 +24,7 @@ const aliasPath = {
'react-bootstrap-table2-filter': filterSourcePath, 'react-bootstrap-table2-filter': filterSourcePath,
'react-bootstrap-table2-overlay': overlaySourcePath, 'react-bootstrap-table2-overlay': overlaySourcePath,
'react-bootstrap-table2-paginator': paginationSourcePath, 'react-bootstrap-table2-paginator': paginationSourcePath,
'react-bootstrap-table2-toolkit': toolkitSourcePath
}; };
const loaders = [{ const loaders = [{

View File

@@ -0,0 +1,52 @@
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 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'
}];
<BootstrapTable id="bar" keyField='id' data={ products } columns={ columns } />
<BootstrapTable classes="foo" keyField='id' data={ products } columns={ columns } />
<BootstrapTable wrapperClasses="boo" keyField="id" data={ products } columns={ columns } />
`;
export default () => (
<div>
<h4> Customized table ID </h4>
<BootstrapTable id="bar" keyField="id" data={ products } columns={ columns } />
<h4> Customized table className </h4>
<BootstrapTable classes="foo" keyField="id" data={ products } columns={ columns } />
<h4> Customized wrapper className </h4>
<BootstrapTable wrapperClasses="boo" keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,32 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(5000);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
selectRow={ { mode: 'checkbox' } }
cellEdit={ cellEditFactory({
mode: 'click'
}) }
/>
</div>
);

View File

@@ -0,0 +1,64 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { todosGenerator } from 'utils/common';
const todos = todosGenerator();
const columns = [{
dataField: 'id',
text: 'Todo ID'
}, {
dataField: 'todo',
text: 'Todo Name'
}, {
dataField: 'done',
text: 'Done',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Todo ID'
}, {
dataField: 'todo',
text: 'Todo Name'
}, {
dataField: 'done',
text: 'Done',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}];
<BootstrapTable
keyField="id"
data={ todos }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ todos }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,130 @@
/* eslint react/prefer-stateless-function: 0 */
/* eslint no-return-assign: 0 */
/* eslint no-unused-vars: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator();
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
editorRenderer: (editorProps, value, row, column, rowIndex, columnIndex) => (
<QualityRanger { ...editorProps } value={ value } />
)
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
editorRenderer: (editorProps, value, row, rowIndex, columnIndex) => (
<QualityRanger { ...editorProps } value={ value } />
)
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,77 @@
/* eslint prefer-template: 0 */
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { stockGenerator } from 'utils/common';
const stocks = stockGenerator();
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'inStockDate',
text: 'Stock Date',
formatter: (cell) => {
let dateObj = cell;
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`;
},
editor: {
type: Type.DATE
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'inStockDate',
text: 'Stock Date',
formatter: (cell) => {
let dateObj = cell;
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return \`$\{('0' + dateObj.getDate()).slice(-2)}/$\{('0' + (dateObj.getMonth() + 1)).slice(-2)}/$\{dateObj.getFullYear()}\`;
},
editor: {
type: Type.DATE
}
}];
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,100 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { jobsGenerator } from 'utils/common';
const jobs = jobsGenerator();
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.SELECT,
options: [{
value: 'A',
label: 'A'
}, {
value: 'B',
label: 'B'
}, {
value: 'C',
label: 'C'
}, {
value: 'D',
label: 'D'
}, {
value: 'E',
label: 'E'
}]
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.SELECT,
options: [{
value: 'A',
label: 'A'
}, {
value: 'B',
label: 'B'
}, {
value: 'C',
label: 'C'
}, {
value: 'D',
label: 'D'
}, {
value: 'E',
label: 'E'
}]
}
}];
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,61 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
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',
editorClasses: 'editing-name'
}, {
dataField: 'price',
text: 'Product Price',
editorClasses: (cell, row, rowIndex, colIndex) =>
(cell > 2101 ? 'editing-price-bigger-than-2101' : 'editing-price-small-than-2101')
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
editorClasses: 'editing-name'
}, {
dataField: 'price',
text: 'Product Price',
editorClasses: (cell, row, rowIndex, colIndex) =>
(cell > 2101 ? 'editing-price-bigger-than-2101' : 'editing-price-small-than-2101')
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click' }) }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click' }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,69 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
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',
editorStyle: {
backgroundColor: '#20B2AA'
}
}, {
dataField: 'price',
text: 'Product Price',
editorStyle: (cell, row, rowIndex, colIndex) => {
const backgroundColor = cell > 2101 ? '#00BFFF' : '#00FFFF';
return { backgroundColor };
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
editorStyle: {
backgroundColor: '#20B2AA'
}
}, {
dataField: 'price',
text: 'Product Price',
editorStyle: (cell, row, rowIndex, colIndex) => {
const backgroundColor = cell > 2101 ? '#00BFFF' : '#00FFFF';
return { backgroundColor };
}
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click' }) }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click' }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,68 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { jobsGenerator } from 'utils/common';
const jobs = jobsGenerator();
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.TEXTAREA
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.TEXTAREA
}
}];
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,184 @@
/* eslint no-return-assign: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter, Comparator, FILTER_TYPES } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
this.onChange = this.onChange.bind(this);
this.state = { value: 2100 };
}
onChange(e) {
this.setState({ value: e.target.value });
}
getValue() {
return parseInt(this.range.value, 10);
}
filter() {
this.props.onFilter({
number: this.getValue(),
comparator: this.select.value
});
}
render() {
return [
<input
key="range"
ref={ node => this.range = node }
type="range"
min="2100"
max="2110"
onChange={ this.onChange }
/>,
<p
key="show"
ref={ node => this.showValue = node }
style={ { textAlign: 'center' } }
>
{ this.state.value }
</p>,
<select
key="select"
ref={ node => this.select = node }
className="form-control"
>
<option value={ Comparator.GT }>&gt;</option>
<option value={ Comparator.EQ }>=</option>
<option value={ Comparator.LT }>&lt;</option>
</select>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ `Filter ${this.props.column.text}` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter({
type: FILTER_TYPES.NUMBER // ask react-bootstrap-table to filter data as number
}),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter, Comparator, FILTER_TYPES } from 'react-bootstrap-table2-filter';
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
this.onChange = this.onChange.bind(this);
this.state = { value: 2100 };
}
onChange(e) {
this.setState({ value: e.target.value });
}
getValue() {
return parseInt(this.range.value, 10);
}
filter() {
this.props.onFilter({
number: this.getValue(),
comparator: this.select.value
});
}
render() {
return [
<input
key="range"
ref={ node => this.range = node }
type="range"
min="2100"
max="2110"
onChange={ this.onChange }
/>,
<p
key="show"
ref={ node => this.showValue = node }
style={ { textAlign: 'center' } }
>
{ this.state.value }
</p>,
<select
key="select"
ref={ node => this.select = node }
className="form-control"
>
<option value={ Comparator.GT }>&gt;</option>
<option value={ Comparator.EQ }>=</option>
<option value={ Comparator.LT }>&lt;</option>
</select>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ \`Filter $\{this.props.column.text}\` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter({
type: FILTER_TYPES.NUMBER // ask react-bootstrap-table to filter data as number
}),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
<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,86 @@
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);
let nameFilter;
let priceFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter({
getFilter: (filter) => {
nameFilter = filter;
}
})
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter({
getFilter: (filter) => {
priceFilter = filter;
}
})
}];
const handleClick = () => {
nameFilter('');
priceFilter('');
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
let nameFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter({
getFilter: (filter) => {
// nameFilter was assigned once the component has been mounted.
nameFilter = filter;
}
})
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}];
const handleClick = () => {
nameFilter(0);
};
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter columns by 0 </button>
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
</div>
);
`;
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> Clear all filters </button>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,78 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { stockGenerator } from 'utils/common';
const stocks = stockGenerator(8);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
formatter: cell => cell.toString(),
filter: dateFilter({
delay: 400,
placeholder: 'custom placeholder',
withoutEmptyComparatorOption: true,
comparators: [Comparator.EQ, Comparator.GT, Comparator.LT],
style: { display: 'inline-grid' },
className: 'custom-datefilter-class',
comparatorStyle: { backgroundColor: 'antiquewhite' },
comparatorClassName: 'custom-comparator-class',
dateStyle: { backgroundColor: 'cadetblue', margin: '0px' },
dateClassName: 'custom-date-class'
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
filter: dateFilter({
delay: 400,
placeholder: 'custom placeholder',
withoutEmptyComparatorOption: true,
comparators: [Comparator.EQ, Comparator.GT, Comparator.LT],
style: { display: 'inline-grid' },
className: 'custom-datefilter-class',
comparatorStyle: { backgroundColor: 'antiquewhite' },
comparatorClassName: 'custom-comparator-class',
dateStyle: { backgroundColor: 'cadetblue', margin: '0px' },
dateClassName: 'custom-date-class'
})
}];
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
filter={ filterFactory() }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -3,9 +3,9 @@ import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block'; import Code from 'components/common/code-block';
import { jobsGenerator } from 'utils/common'; import { jobsGenerator1 } from 'utils/common';
const jobs = jobsGenerator(5); const jobs = jobsGenerator1(5);
const owners = ['Allen', 'Bob', 'Cat']; const owners = ['Allen', 'Bob', 'Cat'];
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money']; const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];

View File

@@ -0,0 +1,128 @@
/* eslint no-return-assign: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
}
getValue() {
return this.input.value;
}
filter() {
this.props.onFilter(this.getValue());
}
render() {
return [
<input
key="input"
ref={ node => this.input = node }
type="text"
placeholder="Input price"
/>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ `Find ${this.props.column.text}` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter(),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter } from 'react-bootstrap-table2-filter';
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
}
getValue() {
return this.input.value;
}
filter() {
this.props.onFilter(this.getValue());
}
render() {
return [
<input
key="input"
ref={ node => this.input = node }
type="text"
placeholder="Input price"
/>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ \`Filter $\{this.props.column.text}\` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter(),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
<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,80 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
withoutEmptyOption: true,
style: {
backgroundColor: 'pink'
},
className: 'test-classname',
datamycustomattr: 'datamycustomattr'
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } 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'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
withoutEmptyOption: true,
style: {
backgroundColor: 'pink'
},
className: 'test-classname',
datamycustomattr: 'datamycustomattr'
})
}];
<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,60 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { stockGenerator } from 'utils/common';
const stocks = stockGenerator(8);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
formatter: cell => cell.toString(),
filter: dateFilter({
defaultValue: { date: new Date(2018, 0, 1), comparator: Comparator.GT }
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
filter: dateFilter({
defaultValue: { date: new Date(2018, 0, 1), comparator: Comparator.GT }
})
}];
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
filter={ filterFactory() }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,56 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { stockGenerator } from 'utils/common';
const stocks = stockGenerator(8);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
formatter: cell => cell.toString(),
filter: dateFilter()
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
filter: dateFilter()
}];
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
filter={ filterFactory() }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,69 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
defaultValue: [0, 2]
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } 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'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
defaultValue: [0, 2]
})
}];
<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,67 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } 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'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions
})
}];
<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,86 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { stockGenerator } from 'utils/common';
const stocks = stockGenerator(8);
let inStockDateFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
formatter: cell => cell.toString(),
filter: dateFilter({
getFilter: (filter) => {
// inStockDateFilter was assigned once the component has been mounted.
inStockDateFilter = filter;
}
})
}];
const handleClick = () => {
inStockDateFilter({
date: new Date(2018, 0, 1),
comparator: Comparator.GT
});
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter';
let inStockDateFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'inStockDate',
text: 'InStock Date',
filter: dateFilter({
getFilter: (filter) => {
// inStockDateFilter was assigned once the component has been mounted.
inStockDateFilter = filter;
}
})
}];
const handleClick = () => {
inStockDateFilter({
date: new Date(2018, 0, 1),
comparator: Comparator.GT
});
};
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter InStock Date columns which is greater than 2018.01.01 </button>
<BootstrapTable keyField='id' data={ stocks } columns={ columns } filter={ filterFactory() } />
</div>
);
`;
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter InStock Date columns which is greater than 2018.01.01 </button>
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,95 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
let qualityFilter;
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
getFilter: (filter) => {
// qualityFilter was assigned once the component has been mounted.
qualityFilter = filter;
}
})
}];
const handleClick = () => {
qualityFilter([0, 2]);
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
let qualityFilter;
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
getFilter: (filter) => {
// qualityFilter was assigned once the component has been mounted.
qualityFilter = filter;
}
})
}];
const handleClick = () => {
qualityFilter([0, 2]);
};
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }>{' filter columns by option "good" and "unknow" '}</button>
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
</div>
);
`;
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }>{' filter columns by option "good" and "unknow" '}</button>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,85 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { numberFilter, Comparator } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
let priceFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price',
filter: numberFilter({
getFilter: (filter) => {
// pricerFilter was assigned once the component has been mounted.
priceFilter = filter;
}
})
}];
const handleClick = () => {
priceFilter({
number: 2103,
comparator: Comparator.GT
});
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { numberFilter } from 'react-bootstrap-table2-filter';
let priceFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price',
filter: numberFilter({
getFilter: (filter) => {
// pricerFilter was assigned once the component has been mounted.
priceFilter = filter;
}
})
}];
const handleClick = () => {
priceFilter({
number: 2103,
comparator: Comparator.GT
});
};
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter all columns which is greater than 2103 </button>
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
</div>
);
`;
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter all columns which is greater than 2103 </button>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,96 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
let qualityFilter;
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
formatter: cell => selectOptions[cell],
filter: selectFilter({
options: selectOptions,
getFilter: (filter) => {
// qualityFilter was assigned once the component has been mounted.
qualityFilter = filter;
}
})
}];
const handleClick = () => {
qualityFilter(0);
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter';
let qualityFilter;
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
formatter: cell => selectOptions[cell],
filter: selectFilter({
options: selectOptions,
getFilter: (filter) => {
// qualityFilter was assigned once the component has been mounted.
qualityFilter = filter;
}
})
}];
const handleClick = () => {
qualityFilter(0);
};
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }>{' filter columns by option "good" '}</button>
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
</div>
);
`;
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }>{' filter columns by option "good" '}</button>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,81 @@
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);
let nameFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter({
getFilter: (filter) => {
// nameFilter was assigned once the component has been mounted.
nameFilter = filter;
}
})
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}];
const handleClick = () => {
nameFilter(0);
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
let nameFilter;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter({
getFilter: (filter) => {
// nameFilter was assigned once the component has been mounted.
nameFilter = filter;
}
})
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}];
const handleClick = () => {
nameFilter(0);
};
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter columns by 0 </button>
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
</div>
);
`;
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter columns by 0 </button>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -41,7 +41,7 @@ const columns = [{
export default () => ( export default () => (
<div> <div>
<h3>Try to hover on Product Name header column</h3> <h3>Try to hover on Product ID Cell</h3>
<BootstrapTable keyField="id" data={ products } columns={ columns } /> <BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code> <Code>{ sourceCode }</Code>
</div> </div>

View File

@@ -0,0 +1,80 @@
/* eslint react/prop-types: 0 */
import React from 'react';
/* eslint no-unused-vars: 0 */
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { ExportCSVButton } = CSVExport;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price',
csvFormatter: (cell, row, rowIndex) => `$ ${cell}NTD`
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport } from 'react-bootstrap-table2-toolkit';
const { ExportCSVButton } = CSVExport;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price',
csvFormatter: (cell, row, rowIndex) => \`$ \${cell}NTD\`
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

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

View File

@@ -0,0 +1,97 @@
/* 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 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 from 'react-bootstrap-table2-toolkit';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const MyExportCSV = (props) => {
const handleClick = () => {
props.onExport();
};
return (
<div>
<button className="btn btn-success" onClick={ handleClick }>Export to CSV</button>
</div>
);
};
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV
>
{
props => (
<div>
<BootstrapTable { ...props.baseProps } />
<hr />
<MyExportCSV { ...props.csvProps } />
</div>
)
}
</ToolkitProvider>
`;
const MyExportCSV = (props) => {
const handleClick = () => {
props.onExport();
};
return (
<div>
<button className="btn btn-success" onClick={ handleClick }>Export to CSV</button>
</div>
);
};
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV
>
{
props => (
<div>
<BootstrapTable { ...props.baseProps } />
<hr />
<MyExportCSV { ...props.csvProps } />
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

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

View File

@@ -0,0 +1,82 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
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, { CSVExport } from 'react-bootstrap-table2-toolkit';
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 }
exportCSV
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV={ {
fileName: 'custom.csv',
separator: '|',
ignoreHeader: true,
noAutoBOM: false
} }
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

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

View File

@@ -0,0 +1,77 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { CSVExport } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
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, { CSVExport } from 'react-bootstrap-table2-toolkit';
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 }
exportCSV
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
exportCSV
>
{
props => (
<div>
<ExportCSVButton { ...props.csvProps }>Export CSV!!</ExportCSVButton>
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -28,7 +28,7 @@ import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{ const columns = [{
dataField: 'id', dataField: 'id',
text: 'Product ID', text: 'Product ID',
events: { headerEvents: {
onClick: () => alert('Click on Product ID header column') onClick: () => alert('Click on Product ID header column')
} }
}, { }, {

View File

@@ -0,0 +1,52 @@
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 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'
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
headerClasses="header-class"
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
headerClasses="header-class"
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

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

View File

@@ -24,6 +24,12 @@ import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator'; import paginationFactory from 'react-bootstrap-table2-paginator';
// ... // ...
const customTotal = (from, to, size) => (
<span className="react-bootstrap-table-pagination-total">
Showing { from } to { to } of { size } Results
</span>
);
const options = { const options = {
paginationSize: 4, paginationSize: 4,
pageStartIndex: 0, pageStartIndex: 0,
@@ -39,6 +45,8 @@ const options = {
prePageTitle: 'Pre page', prePageTitle: 'Pre page',
firstPageTitle: 'Next page', firstPageTitle: 'Next page',
lastPageTitle: 'Last page', lastPageTitle: 'Last page',
showTotal: true,
paginationTotalRenderer: customTotal,
sizePerPageList: [{ sizePerPageList: [{
text: '5', value: 5 text: '5', value: 5
}, { }, {
@@ -50,11 +58,18 @@ const options = {
<BootstrapTable keyField='id' data={ products } columns={ columns } pagination={ paginationFactory(options) } /> <BootstrapTable keyField='id' data={ products } columns={ columns } pagination={ paginationFactory(options) } />
`; `;
const customTotal = (from, to, size) => (
<span className="react-bootstrap-table-pagination-total">
Showing { from } to { to } of { size } Results
</span>
);
const options = { const options = {
paginationSize: 4, paginationSize: 4,
pageStartIndex: 0, pageStartIndex: 0,
// alwaysShowAllBtns: true // Always show next and previous button // alwaysShowAllBtns: true, // Always show next and previous button
// withFirstAndLast: false // Hide the going to First and Last page button // withFirstAndLast: false, // Hide the going to First and Last page button
// hideSizePerPage: true, // Hide the sizePerPage dropdown always // hideSizePerPage: true, // Hide the sizePerPage dropdown always
// hidePageListOnlyOnePage: true, // Hide the pagination list when only one page // hidePageListOnlyOnePage: true, // Hide the pagination list when only one page
firstPageText: 'First', firstPageText: 'First',
@@ -65,6 +80,8 @@ const options = {
prePageTitle: 'Pre page', prePageTitle: 'Pre page',
firstPageTitle: 'Next page', firstPageTitle: 'Next page',
lastPageTitle: 'Last page', lastPageTitle: 'Last page',
showTotal: true,
paginationTotalRenderer: customTotal,
sizePerPageList: [{ sizePerPageList: [{
text: '5', value: 5 text: '5', value: 5
}, { }, {

View File

@@ -4,53 +4,76 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator'; import paginationFactory from 'react-bootstrap-table2-paginator';
import cellEditFactory from 'react-bootstrap-table2-editor';
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter'; import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block'; import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common'; import { productsGenerator } from 'utils/common';
const products = productsGenerator(87); let products = productsGenerator(87);
const columns = [{ const columns = [{
dataField: 'id', dataField: 'id',
text: 'Product ID' text: 'Product ID',
sort: true
}, { }, {
dataField: 'name', dataField: 'name',
text: 'Product Name', text: 'Product Name',
filter: textFilter() filter: textFilter({
defaultValue: '8'
}),
sort: true
}, { }, {
dataField: 'price', dataField: 'price',
text: 'Product Price', text: 'Product Price',
filter: textFilter() filter: textFilter(),
sort: true
}]; }];
const sourceCode = `\ const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator'; import paginationFactory from 'react-bootstrap-table2-paginator';
import cellEditFactory from 'react-bootstrap-table2-editor';
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter'; import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
// ... // ...
const columns = [{ const columns = [{
dataField: 'id', dataField: 'id',
text: 'Product ID' text: 'Product ID',
sort: true
}, { }, {
dataField: 'name', dataField: 'name',
text: 'Product Name', text: 'Product Name',
filter: textFilter() filter: textFilter({
defaultValue: '8'
}),
sort: true
}, { }, {
dataField: 'price', dataField: 'price',
text: 'Product Price', text: 'Product Price',
filter: textFilter() filter: textFilter(),
sort: true
}]; }];
const defaultSorted = [{
dataField: 'name',
order: 'desc'
}];
const cellEditProps = {
mode: 'click'
};
const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => ( const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div> <div>
<BootstrapTable <BootstrapTable
remote={ { pagination: true } } remote
keyField="id" keyField="id"
data={ data } data={ data }
columns={ columns } columns={ columns }
defaultSorted={ defaultSorted }
filter={ filterFactory() } filter={ filterFactory() }
pagination={ paginationFactory({ page, sizePerPage, totalSize }) } pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
cellEdit={ cellEditFactory(cellEditProps) }
onTableChange={ onTableChange } onTableChange={ onTableChange }
/> />
<Code>{ sourceCode }</Code> <Code>{ sourceCode }</Code>
@@ -77,10 +100,25 @@ class Container extends React.Component {
this.handleTableChange = this.handleTableChange.bind(this); this.handleTableChange = this.handleTableChange.bind(this);
} }
handleTableChange = (type, { page, sizePerPage, filters }) => { handleTableChange = (type, { page, sizePerPage, filters, sortField, sortOrder, cellEdit }) => {
const currentIndex = (page - 1) * sizePerPage; const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => { setTimeout(() => {
const result = products.filter((row) => { // Handle cell editing
if (type === 'cellEdit') {
const { rowId, dataField, newValue } = cellEdit;
products = products.map((row) => {
if (row.id === rowId) {
const newRow = { ...row };
newRow[dataField] = newValue;
return newRow;
}
return row;
});
}
let result = products;
// Handle column filters
result = result.filter((row) => {
let valid = true; let valid = true;
for (const dataField in filters) { for (const dataField in filters) {
const { filterVal, filterType, comparator } = filters[dataField]; const { filterVal, filterType, comparator } = filters[dataField];
@@ -96,6 +134,26 @@ class Container extends React.Component {
} }
return valid; return valid;
}); });
// Handle column sort
if (sortOrder === 'asc') {
result = result.sort((a, b) => {
if (a[sortField] > b[sortField]) {
return 1;
} else if (b[sortField] > a[sortField]) {
return -1;
}
return 0;
});
} else {
result = result.sort((a, b) => {
if (a[sortField] > b[sortField]) {
return -1;
} else if (b[sortField] > a[sortField]) {
return 1;
}
return 0;
});
}
this.setState(() => ({ this.setState(() => ({
page, page,
data: result.slice(currentIndex, currentIndex + sizePerPage), data: result.slice(currentIndex, currentIndex + sizePerPage),
@@ -120,18 +178,29 @@ class Container extends React.Component {
} }
`; `;
const defaultSorted = [{
dataField: 'name',
order: 'desc'
}];
const cellEditProps = {
mode: 'click'
};
const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => ( const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div> <div>
<h3>When <code>remote.pagination</code> is enabled, the filtering, <h3>When <code>remote.pagination</code> is enabled, the filtering,
sorting and searching will also change to remote mode automatically</h3> sorting and searching will also change to remote mode automatically</h3>
<BootstrapTable <BootstrapTable
remote={ { pagination: true } } remote
keyField="id" keyField="id"
data={ data } data={ data }
columns={ columns } columns={ columns }
defaultSorted={ defaultSorted }
filter={ filterFactory() } filter={ filterFactory() }
pagination={ paginationFactory({ page, sizePerPage, totalSize }) } pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
onTableChange={ onTableChange } onTableChange={ onTableChange }
cellEdit={ cellEditFactory(cellEditProps) }
/> />
<Code>{ sourceCode }</Code> <Code>{ sourceCode }</Code>
</div> </div>
@@ -157,10 +226,24 @@ class Container extends React.Component {
this.handleTableChange = this.handleTableChange.bind(this); this.handleTableChange = this.handleTableChange.bind(this);
} }
handleTableChange = (type, { page, sizePerPage, filters }) => { handleTableChange = (type, { page, sizePerPage, filters, sortField, sortOrder, cellEdit }) => {
const currentIndex = (page - 1) * sizePerPage; const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => { setTimeout(() => {
const result = products.filter((row) => { // Handle cell editing
if (type === 'cellEdit') {
const { rowId, dataField, newValue } = cellEdit;
products = products.map((row) => {
if (row.id === rowId) {
const newRow = { ...row };
newRow[dataField] = newValue;
return newRow;
}
return row;
});
}
let result = products;
// Handle column filters
result = result.filter((row) => {
let valid = true; let valid = true;
for (const dataField in filters) { for (const dataField in filters) {
const { filterVal, filterType, comparator } = filters[dataField]; const { filterVal, filterType, comparator } = filters[dataField];
@@ -176,6 +259,26 @@ class Container extends React.Component {
} }
return valid; return valid;
}); });
// Handle column sort
if (sortOrder === 'asc') {
result = result.sort((a, b) => {
if (a[sortField] > b[sortField]) {
return 1;
} else if (b[sortField] > a[sortField]) {
return -1;
}
return 0;
});
} else {
result = result.sort((a, b) => {
if (a[sortField] > b[sortField]) {
return -1;
} else if (b[sortField] > a[sortField]) {
return 1;
}
return 0;
});
}
this.setState(() => ({ this.setState(() => ({
page, page,
data: result.slice(currentIndex, currentIndex + sizePerPage), data: result.slice(currentIndex, currentIndex + sizePerPage),

View File

@@ -0,0 +1,175 @@
/* eslint guard-for-in: 0 */
/* eslint no-restricted-syntax: 0 */
import React from 'react';
import PropTypes from 'prop-types';
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 } = Search;
const products = productsGenerator(17);
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';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
const columns = [{
dataField: 'id',
text: 'Product ID',
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter()
}];
const RemoteFilter = props => (
<div>
<ToolkitProvider
keyField="id"
data={ props.data }
columns={ columns }
search
>
{
toolkitprops => [
<SearchBar { ...toolkitprops.searchProps } />,
<BootstrapTable
{ ...toolkitprops.baseProps }
remote={ { search: true } }
onTableChange={ props.onTableChange }
/>
]
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
data: products
};
}
handleTableChange = (type, { filters }) => {
setTimeout(() => {
const result = products.filter((row) => {
let valid = true;
for (const dataField in filters) {
const { filterVal, filterType, comparator } = filters[dataField];
if (filterType === 'TEXT') {
if (comparator === Comparator.LIKE) {
valid = row[dataField].toString().indexOf(filterVal) > -1;
} else {
valid = row[dataField] === filterVal;
}
}
if (!valid) break;
}
return valid;
});
this.setState(() => ({
data: result
}));
}, 2000);
}
render() {
return (
<RemoteFilter
data={ this.state.data }
onTableChange={ this.handleTableChange }
/>
);
}
}
`;
const RemoteFilter = props => (
<div>
<ToolkitProvider
keyField="id"
data={ props.data }
columns={ columns }
search
>
{
toolkitprops => [
<SearchBar { ...toolkitprops.searchProps } />,
<BootstrapTable
{ ...toolkitprops.baseProps }
remote={ { search: true } }
onTableChange={ props.onTableChange }
/>
]
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);
RemoteFilter.propTypes = {
data: PropTypes.array.isRequired,
onTableChange: PropTypes.func.isRequired
};
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
data: products
};
}
handleTableChange = (type, { searchText }) => {
setTimeout(() => {
const result = products.filter((row) => {
for (let cidx = 0; cidx < columns.length; cidx += 1) {
const column = columns[cidx];
let targetValue = row[column.dataField];
if (targetValue !== null && typeof targetValue !== 'undefined') {
targetValue = targetValue.toString().toLowerCase();
if (targetValue.indexOf(searchText) > -1) {
return true;
}
}
}
return false;
});
this.setState(() => ({
data: result
}));
}, 2000);
}
render() {
return (
<RemoteFilter
data={ this.state.data }
onTableChange={ this.handleTableChange }
/>
);
}
}
export default Container;

View File

@@ -0,0 +1,107 @@
/* eslint react/prop-types: 0 */
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,
expandHeaderColumnRenderer: ({ isAnyExpands }) => {
if (isAnyExpands) {
return <b>-</b>;
}
return <b>+</b>;
},
expandColumnRenderer: ({ expanded }) => {
if (expanded) {
return (
<b>-</b>
);
}
return (
<b>...</b>
);
}
};
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,
expandHeaderColumnRenderer: ({ isAnyExpands }) => {
if (isAnyExpands) {
return <b>-</b>;
}
return <b>+</b>;
},
expandColumnRenderer: ({ expanded }) => {
if (expanded) {
return (
<b>-</b>
);
}
return (
<b>...</b>
);
}
};
<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

@@ -0,0 +1,74 @@
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
};
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
};
<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

@@ -0,0 +1,97 @@
/* eslint no-console: 0 */
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,
onExpand: (row, isExpand, rowIndex, e) => {
console.log(row.id);
console.log(isExpand);
console.log(rowIndex);
console.log(e);
},
onExpandAll: (isExpandAll, rows, e) => {
console.log(isExpandAll);
console.log(rows);
console.log(e);
}
};
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,
onExpand: (row, isExpand, rowIndex, e) => {
console.log(row.id);
console.log(isExpand);
console.log(rowIndex);
console.log(e);
},
onExpandAll: (isExpandAll, rows, e) => {
console.log(isExpandAll);
console.log(rows);
console.log(e);
}
};
<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

@@ -0,0 +1,138 @@
/* eslint no-unused-vars: 0 */
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 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'
}];
class RowExpandManagment extends React.Component {
constructor(props) {
super(props);
this.state = { expanded: [0, 1] };
}
handleBtnClick = () => {
if (!this.state.expanded.includes(2)) {
this.setState(() => ({
expanded: [...this.state.expanded, 2]
}));
} else {
this.setState(() => ({
expanded: this.state.expanded.filter(x => x !== 2)
}));
}
}
handleOnExpand = (row, isExpand, rowIndex, e) => {
if (isExpand) {
this.setState(() => ({
expanded: [...this.state.expanded, row.id]
}));
} else {
this.setState(() => ({
expanded: this.state.expanded.filter(x => x !== row.id)
}));
}
}
render() {
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>
),
expanded: this.state.expanded,
onExpand: this.handleOnExpand
};
return (
<div>
<button className="btn btn-success" onClick={ this.handleBtnClick }>Expand/Collapse 3rd row</button>
<BootstrapTable keyField="id" data={ products } columns={ columns } expandRow={ expandRow } />
<Code>{ sourceCode }</Code>
</div>
);
}
}
`;
export default class RowExpandManagment extends React.Component {
constructor(props) {
super(props);
this.state = { expanded: [0, 1] };
}
handleBtnClick = () => {
if (!this.state.expanded.includes(2)) {
this.setState(() => ({
expanded: [...this.state.expanded, 2]
}));
} else {
this.setState(() => ({
expanded: this.state.expanded.filter(x => x !== 2)
}));
}
}
handleOnExpand = (row, isExpand, rowIndex, e) => {
if (isExpand) {
this.setState(() => ({
expanded: [...this.state.expanded, row.id]
}));
} else {
this.setState(() => ({
expanded: this.state.expanded.filter(x => x !== row.id)
}));
}
}
render() {
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>
),
expanded: this.state.expanded,
onExpand: this.handleOnExpand
};
return (
<div>
<button className="btn btn-success" onClick={ this.handleBtnClick }>Expand/Collapse 3rd row</button>
<BootstrapTable keyField="id" data={ products } columns={ columns } expandRow={ expandRow } />
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -0,0 +1,72 @@
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>
)
};
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>
)
};
<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

@@ -0,0 +1,75 @@
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>
),
nonExpandable: [1, 3]
};
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>
),
nonExpandable: [1, 3]
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
`;
export default () => (
<div>
<h3>The second and fourth row is not expandable</h3>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,107 @@
/* eslint react/prop-types: 0 */
/* eslint no-param-reassign: 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 selectRow1 = {
mode: 'radio',
clickToSelect: true,
selectionHeaderRenderer: () => 'X',
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
const selectRow2 = {
mode: 'checkbox',
clickToSelect: true,
selectionHeaderRenderer: ({ indeterminate, ...rest }) => (
<input
type="checkbox"
ref={ (input) => {
if (input) input.indeterminate = indeterminate;
} }
{ ...rest }
/>
),
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
const sourceCode1 = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = ....;
const selectRow = {
mode: 'radio',
clickToSelect: true,
selectionHeaderRenderer: () => 'X',
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
const sourceCode2 = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = ....;
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
selectionHeaderRenderer: ({ indeterminate, ...rest }) => (
<input
type="checkbox"
ref={ (input) => {
if (input) input.indeterminate = indeterminate;
} }
{ ...rest }
/>
),
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow1 } />
<Code>{ sourceCode1 }</Code>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow2 } />
<Code>{ sourceCode2 }</Code>
</div>
);

View File

@@ -0,0 +1,59 @@
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 selectRow = {
mode: 'checkbox',
clickToSelect: true,
selected: [1, 3]
};
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 selectRow = {
mode: 'checkbox',
clickToSelect: true,
selected: [1, 3]
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -22,14 +22,16 @@ const columns = [{
const selectRow = { const selectRow = {
mode: 'checkbox', mode: 'checkbox',
clickToSelect: true, clickToSelect: true,
onSelect: (row, isSelect, rowIndex) => { onSelect: (row, isSelect, rowIndex, e) => {
console.log(row.id); console.log(row.id);
console.log(isSelect); console.log(isSelect);
console.log(rowIndex); console.log(rowIndex);
console.log(e);
}, },
onSelectAll: (isSelect, rows) => { onSelectAll: (isSelect, rows, e) => {
console.log(isSelect); console.log(isSelect);
console.log(rows); console.log(rows);
console.log(e);
} }
}; };
@@ -49,7 +51,18 @@ const columns = [{
const selectRow = { const selectRow = {
mode: 'checkbox', mode: 'checkbox',
clickToSelect: true clickToSelect: true,
onSelect: (row, isSelect, rowIndex, e) => {
console.log(row.id);
console.log(isSelect);
console.log(rowIndex);
console.log(e);
},
onSelectAll: (isSelect, rows, e) => {
console.log(isSelect);
console.log(rows);
console.log(e);
}
}; };
<BootstrapTable <BootstrapTable

View File

@@ -0,0 +1,144 @@
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 sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
class SelectionManagment extends React.Component {
constructor(props) {
super(props);
this.state = { selected: [0, 1] };
}
handleBtnClick = () => {
if (!this.state.selected.includes(2)) {
this.setState(() => ({
selected: [...this.state.selected, 2]
}));
} else {
this.setState(() => ({
selected: this.state.selected.filter(x => x !== 2)
}));
}
}
handleOnSelect = (row, isSelect) => {
if (isSelect) {
this.setState(() => ({
selected: [...this.state.selected, row.id]
}));
} else {
this.setState(() => ({
selected: this.state.selected.filter(x => x !== row.id)
}));
}
}
handleOnSelectAll = (isSelect, rows) => {
const ids = rows.map(r => r.id);
if (isSelect) {
this.setState(() => ({
selected: ids
}));
} else {
this.setState(() => ({
selected: []
}));
}
}
render() {
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
selected: this.state.selected,
onSelect: this.handleOnSelect,
onSelectAll: this.handleOnSelectAll
};
return (
<div>
<button className="btn btn-success" onClick={ this.handleBtnClick }>Select/UnSelect 3rd row</button>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow } />
<Code>{ sourceCode }</Code>
</div>
);
}
}
`;
export default class SelectionManagment extends React.Component {
constructor(props) {
super(props);
this.state = { selected: [0, 1] };
}
handleBtnClick = () => {
if (!this.state.selected.includes(2)) {
this.setState(() => ({
selected: [...this.state.selected, 2]
}));
} else {
this.setState(() => ({
selected: this.state.selected.filter(x => x !== 2)
}));
}
}
handleOnSelect = (row, isSelect) => {
if (isSelect) {
this.setState(() => ({
selected: [...this.state.selected, row.id]
}));
} else {
this.setState(() => ({
selected: this.state.selected.filter(x => x !== row.id)
}));
}
}
handleOnSelectAll = (isSelect, rows) => {
const ids = rows.map(r => r.id);
if (isSelect) {
this.setState(() => ({
selected: ids
}));
} else {
this.setState(() => ({
selected: []
}));
}
}
render() {
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
selected: this.state.selected,
onSelect: this.handleOnSelect,
onSelectAll: this.handleOnSelectAll
};
return (
<div>
<button className="btn btn-success" onClick={ this.handleBtnClick }>Select/UnSelect 3rd row</button>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow } />
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -0,0 +1,62 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow1 = {
mode: 'checkbox',
clickToSelect: true
};
const sourceCode1 = `\
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 selectRow = {
mode: 'checkbox',
clickToSelect: true
};
<BootstrapTable
keyField='id'
data={ [] }
columns={ columns }
selectRow={ selectRow }
noDataIndication={ 'no results found' }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ [] }
columns={ columns }
selectRow={ selectRow1 }
noDataIndication={ 'no results found' }
/>
<Code>{ sourceCode1 }</Code>
</div>
);

View File

@@ -0,0 +1,102 @@
/* eslint react/prop-types: 0 */
/* eslint no-unused-vars: 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 { jobsGenerator1 } from 'utils/common';
const { SearchBar } = Search;
const products = jobsGenerator1(5);
const owners = ['Allen', 'Bob', 'Cat'];
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
const columns = [{
dataField: 'id',
text: 'Job ID',
searchable: false,
hidden: true
}, {
dataField: 'owner',
text: 'Job Owner',
formatter: (cell, row) => owners[cell],
filterValue: (cell, row) => owners[cell] // we will search the value after filterValue called
}, {
dataField: 'type',
text: 'Job Type',
formatter: (cell, row) => types[cell],
filterValue: (cell, row) => types[cell] // we will search the value after filterValue called
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
const { SearchBar } = Search;
const owners = ['Allen', 'Bob', 'Cat'];
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
const columns = [{
dataField: 'id',
text: 'Job ID',
searchable: false,
hidden: true
}, {
dataField: 'owner',
text: 'Job Owner',
formatter: (cell, row) => owners[cell],
filterValue: (cell, row) => owners[cell] // we will search the value after filterValue called
}, {
dataField: 'type',
text: 'Job Type',
formatter: (cell, row) => types[cell],
filterValue: (cell, row) => types[cell] // we will search the value after filterValue called
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<h3>Try to Search Bob, Cat or Allen instead of 0, 1 or 2</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<h3>Try to Search Bob, Cat or Allen instead of 0, 1 or 2</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,95 @@
/* 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 } = 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 } = 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 }
className="custome-search-field"
style={ { color: 'white' } }
delay={ 1000 }
placeholder="Search Something!!!"
/>
<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 }
className="custome-search-field"
style={ { color: 'white' } }
delay={ 1000 }
placeholder="Search Something!!!"
/>
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,116 @@
/* eslint react/prop-types: 0 */
/* eslint no-return-assign: 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 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 from 'react-bootstrap-table2-toolkit';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const MySearch = (props) => {
let input;
const handleClick = () => {
props.onSearch(input.value);
};
return (
<div>
<input
className="form-control"
style={ { backgroundColor: 'pink' } }
ref={ n => input = n }
type="text"
/>
<button className="btn btn-warning" onClick={ handleClick }>Click to Search!!</button>
</div>
);
};
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<BootstrapTable
{ ...props.baseProps }
/>
<MySearch { ...props.searchProps } />
<br />
</div>
)
}
</ToolkitProvider>
`;
const MySearch = (props) => {
let input;
const handleClick = () => {
props.onSearch(input.value);
};
return (
<div>
<input
className="form-control"
style={ { backgroundColor: 'pink' } }
ref={ n => input = n }
type="text"
/>
<button className="btn btn-warning" onClick={ handleClick }>Click to Search!!</button>
</div>
);
};
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search
>
{
props => (
<div>
<BootstrapTable
{ ...props.baseProps }
/>
<MySearch { ...props.searchProps } />
<br />
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,83 @@
/* 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 } = 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 } = 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 } />
<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 } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,85 @@
/* 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 } = Search;
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price',
formatter: cell => `USD ${cell}`
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
const { SearchBar } = Search;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price',
formatter: cell => \`USD \${cell}\` // we will search the data after formatted
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ { searchFormatted: true } }
>
{
props => (
<div>
<h3>Try to Search USD at below input field:</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ { searchFormatted: true } }
>
{
props => (
<div>
<h3>Try to Search USD at below input field:</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,67 @@
/* eslint react/prefer-stateless-function: 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',
sort: true
}, {
dataField: 'name',
text: 'Product Name',
sort: true
}, {
dataField: 'price',
text: 'Product Price',
sort: true
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
sort: true
}, {
dataField: 'name',
text: 'Product Name',
sort: true
}, {
dataField: 'price',
text: 'Product Price',
sort: true
}];
const defaultSorted = [{
dataField: 'name',
order: 'desc'
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
defaultSortDirection="asc"
/>
`;
class DefaultSortDirectionTable extends React.PureComponent {
render() {
return (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } defaultSortDirection="asc" />
<Code>{ sourceCode }</Code>
</div>
);
}
}
export default DefaultSortDirectionTable;

View File

@@ -38,9 +38,28 @@ const columns = [{
<BootstrapTable keyField='id' data={ products } columns={ columns } /> <BootstrapTable keyField='id' data={ products } columns={ columns } />
`; `;
export default () => ( export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = { data: products };
}
handleClick = () => {
this.setState(() => {
const newProducts = productsGenerator(21);
return {
data: newProducts
};
});
}
render() {
return (
<div> <div>
<BootstrapTable keyField="id" data={ products } columns={ columns } /> <button className="btn btn-default" onClick={ this.handleClick }>Change Data</button>
<BootstrapTable keyField="id" data={ this.state.data } columns={ columns } />
<Code>{ sourceCode }</Code> <Code>{ sourceCode }</Code>
</div> </div>
); );
}
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-example", "name": "react-bootstrap-table2-example",
"version": "0.1.2", "version": "1.0.2",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"private": true, "private": true,
@@ -14,8 +14,8 @@
"license": "ISC", "license": "ISC",
"peerDependencies": { "peerDependencies": {
"prop-types": "^15.0.0", "prop-types": "^15.0.0",
"react": "^15.0.0", "react": "^16.3.0",
"react-dom": "^15.0.0" "react-dom": "^116.3.0"
}, },
"dependencies": { "dependencies": {
"bootstrap": "^3.3.7" "bootstrap": "^3.3.7"

View File

@@ -1,3 +1,5 @@
/* eslint no-mixed-operators: 0 */
/** /**
* products generator for stories * products generator for stories
* *
@@ -27,12 +29,56 @@ export const productsQualityGenerator = (quantity = 5) =>
quality: index % 3 quality: index % 3
})); }));
const jobType = ['A', 'B', 'C', 'D', 'E'];
const jobOwner = ['Allen', 'Bob', 'Cindy'];
export const jobsGenerator = (quantity = 5) => export const jobsGenerator = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({ Array.from({ length: quantity }, (value, index) => ({
id: index, id: index,
name: `Job name ${index}`, name: `Job name ${index}`,
owner: Math.floor(Math.random() * 3), owner: jobOwner[Math.floor((Math.random() * 2) + 1)],
type: Math.floor(Math.random() * 5) type: jobType[Math.floor((Math.random() * 4) + 1)]
}));
export const jobsGenerator1 = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({
id: index,
name: `Job name ${index}`,
owner: Math.floor((Math.random() * 2) + 1),
type: Math.floor((Math.random() * 4) + 1)
}));
export const todosGenerator = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({
id: index,
todo: `Todo item ${index}`,
done: Math.random() > 0.4 ? 'Y' : 'N'
}));
const startDate = new Date(2017, 0, 1);
const endDate = new Date();
export const stockGenerator = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({
id: index,
name: `Todo item ${index}`,
inStockDate:
new Date(startDate.getTime() + Math.random() * (endDate.getTime() - startDate.getTime()))
})); }));
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
export const productsExpandRowsGenerator = (quantity = 5, callback) => {
if (callback) return Array.from({ length: quantity }, callback);
// if no given callback, retrun default product format.
return (
Array.from({ length: quantity }, (value, index) => ({
id: index,
name: `Item name ${index}`,
price: 2100 + index,
expand: productsQualityGenerator(index)
}))
);
};

View File

@@ -9,7 +9,9 @@ import BasicTable from 'examples/basic';
import BorderlessTable from 'examples/basic/borderless-table'; import BorderlessTable from 'examples/basic/borderless-table';
import StripHoverCondensedTable from 'examples/basic/striped-hover-condensed-table'; import StripHoverCondensedTable from 'examples/basic/striped-hover-condensed-table';
import NoDataTable from 'examples/basic/no-data-table'; import NoDataTable from 'examples/basic/no-data-table';
import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
import CaptionTable from 'examples/basic/caption-table'; import CaptionTable from 'examples/basic/caption-table';
import LargeTable from 'examples/basic/large-table';
// work on columns // work on columns
import NestedDataTable from 'examples/columns/nested-data-table'; import NestedDataTable from 'examples/columns/nested-data-table';
@@ -32,6 +34,7 @@ import HeaderColumnEventTable from 'examples/header-columns/column-event-table';
import HeaderColumnClassTable from 'examples/header-columns/column-class-table'; import HeaderColumnClassTable from 'examples/header-columns/column-class-table';
import HeaderColumnStyleTable from 'examples/header-columns/column-style-table'; import HeaderColumnStyleTable from 'examples/header-columns/column-style-table';
import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table'; import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
import HeaderClassTable from 'examples/header-columns/header-class-table';
// column filter // column filter
import TextFilter from 'examples/column-filter/text-filter'; import TextFilter from 'examples/column-filter/text-filter';
@@ -44,9 +47,23 @@ import SelectFilter from 'examples/column-filter/select-filter';
import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value'; import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value';
import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator'; import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator';
import CustomSelectFilter from 'examples/column-filter/custom-select-filter'; import CustomSelectFilter from 'examples/column-filter/custom-select-filter';
import MultiSelectFilter from 'examples/column-filter/multi-select-filter';
import MultiSelectFilterDefaultValue from 'examples/column-filter/multi-select-filter-default-value';
import CustomMultiSelectFilter from 'examples/column-filter/custom-multi-select-filter';
import NumberFilter from 'examples/column-filter/number-filter'; import NumberFilter from 'examples/column-filter/number-filter';
import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value'; import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value';
import CustomNumberFilter from 'examples/column-filter/custom-number-filter'; import CustomNumberFilter from 'examples/column-filter/custom-number-filter';
import DateFilter from 'examples/column-filter/date-filter';
import DateFilterWithDefaultValue from 'examples/column-filter/date-filter-default-value';
import CustomDateFilter from 'examples/column-filter/custom-date-filter';
import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-text-filter';
import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter';
import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter';
import ProgrammaticallyDateFilter from 'examples/column-filter/programmatically-date-filter';
import ProgrammaticallyMultiSelectFilter from 'examples/column-filter/programmatically-multi-select-filter';
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';
// work on rows // work on rows
import RowStyleTable from 'examples/rows/row-style'; import RowStyleTable from 'examples/rows/row-style';
@@ -56,6 +73,7 @@ import RowEventTable from 'examples/rows/row-event';
// table sort // table sort
import EnableSortTable from 'examples/sort/enable-sort-table'; import EnableSortTable from 'examples/sort/enable-sort-table';
import DefaultSortTable from 'examples/sort/default-sort-table'; import DefaultSortTable from 'examples/sort/default-sort-table';
import DefaultSortDirectionTable from 'examples/sort/default-sort-direction';
import SortEvents from 'examples/sort/sort-events'; import SortEvents from 'examples/sort/sort-events';
import CustomSortTable from 'examples/sort/custom-sort-table'; import CustomSortTable from 'examples/sort/custom-sort-table';
import HeaderSortingClassesTable from 'examples/sort/header-sorting-classes'; import HeaderSortingClassesTable from 'examples/sort/header-sorting-classes';
@@ -72,24 +90,59 @@ import CellEditHooks from 'examples/cell-edit/cell-edit-hooks-table';
import CellEditValidator from 'examples/cell-edit/cell-edit-validator-table'; import CellEditValidator from 'examples/cell-edit/cell-edit-validator-table';
import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table'; import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table';
import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table'; import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
import EditorStyleTable from 'examples/cell-edit/editor-style-table';
import EditorClassTable from 'examples/cell-edit/editor-class-table';
import DropdownEditorTable from 'examples/cell-edit/dropdown-editor-table';
import TextareaEditorTable from 'examples/cell-edit/textarea-editor-table';
import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table';
import DateEditorTable from 'examples/cell-edit/date-editor-table';
import CustomEditorTable from 'examples/cell-edit/custom-editor-table';
// work on row selection // work on row selection
import SingleSelectionTable from 'examples/row-selection/single-selection'; import SingleSelectionTable from 'examples/row-selection/single-selection';
import MultipleSelectionTable from 'examples/row-selection/multiple-selection'; import MultipleSelectionTable from 'examples/row-selection/multiple-selection';
import ClickToSelectTable from 'examples/row-selection/click-to-select'; import ClickToSelectTable from 'examples/row-selection/click-to-select';
import DefaultSelectTable from 'examples/row-selection/default-select';
import SelectionManagement from 'examples/row-selection/selection-management';
import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit'; import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit';
import SelectionNoDataTable from 'examples/row-selection/selection-no-data';
import SelectionStyleTable from 'examples/row-selection/selection-style'; import SelectionStyleTable from 'examples/row-selection/selection-style';
import SelectionClassTable from 'examples/row-selection/selection-class'; import SelectionClassTable from 'examples/row-selection/selection-class';
import CustomSelectionTable from 'examples/row-selection/custom-selection';
import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows'; import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows';
import SelectionBgColorTable from 'examples/row-selection/selection-bgcolor'; import SelectionBgColorTable from 'examples/row-selection/selection-bgcolor';
import SelectionHooks from 'examples/row-selection/selection-hooks'; import SelectionHooks from 'examples/row-selection/selection-hooks';
import HideSelectionColumnTable from 'examples/row-selection/hide-selection-column'; import HideSelectionColumnTable from 'examples/row-selection/hide-selection-column';
// work on row expand
import BasicRowExpand from 'examples/row-expand';
import RowExpandManagement from 'examples/row-expand/expand-management';
import NonExpandableRows from 'examples/row-expand/non-expandable-rows';
import ExpandColumn from 'examples/row-expand/expand-column';
import CustomExpandColumn from 'examples/row-expand/custom-expand-column';
import ExpandHooks from 'examples/row-expand/expand-hooks';
// pagination // pagination
import PaginationTable from 'examples/pagination'; import PaginationTable from 'examples/pagination';
import PaginationHooksTable from 'examples/pagination/pagination-hooks'; import PaginationHooksTable from 'examples/pagination/pagination-hooks';
import CustomPaginationTable from 'examples/pagination/custom-pagination'; import CustomPaginationTable from 'examples/pagination/custom-pagination';
// search
import SearchTable from 'examples/search';
import DefaultCustomSearch from 'examples/search/default-custom-search';
import FullyCustomSearch from 'examples/search/fully-custom-search';
import SearchFormattedData from 'examples/search/search-formatted';
import CustomSearchValue from 'examples/search/custom-search-value';
// CSV
import ExportCSV from 'examples/csv';
import CSVFormatter from 'examples/csv/csv-column-formatter';
import CustomCSVHeader from 'examples/csv/custom-csv-header';
import HideCSVColumn from 'examples/csv/hide-column';
import CSVColumnType from 'examples/csv/csv-column-type';
import CustomCSVButton from 'examples/csv/custom-csv-button';
import CustomCSV from 'examples/csv/custom-csv';
// loading overlay // loading overlay
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay'; import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
import TableOverlay from 'examples/loading-overlay/table-overlay'; import TableOverlay from 'examples/loading-overlay/table-overlay';
@@ -98,6 +151,7 @@ import TableOverlay from 'examples/loading-overlay/table-overlay';
import RemoteSort from 'examples/remote/remote-sort'; import RemoteSort from 'examples/remote/remote-sort';
import RemoteFilter from 'examples/remote/remote-filter'; import RemoteFilter from 'examples/remote/remote-filter';
import RemotePaginationTable from 'examples/remote/remote-pagination'; import RemotePaginationTable from 'examples/remote/remote-pagination';
import RemoteSearch from 'examples/remote/remote-search';
import RemoteCellEdit from 'examples/remote/remote-celledit'; import RemoteCellEdit from 'examples/remote/remote-celledit';
import RemoteAll from 'examples/remote/remote-all'; import RemoteAll from 'examples/remote/remote-all';
@@ -120,7 +174,9 @@ storiesOf('Basic Table', module)
.add('striped, hover, condensed table', () => <StripHoverCondensedTable />) .add('striped, hover, condensed table', () => <StripHoverCondensedTable />)
.add('borderless table', () => <BorderlessTable />) .add('borderless table', () => <BorderlessTable />)
.add('Indication For Empty Table', () => <NoDataTable />) .add('Indication For Empty Table', () => <NoDataTable />)
.add('Table with caption', () => <CaptionTable />); .add('Customized id and class table', () => <CustomizedIdClassesTable />)
.add('Table with caption', () => <CaptionTable />)
.add('Large Table', () => <LargeTable />);
storiesOf('Work on Columns', module) storiesOf('Work on Columns', module)
.add('Display Nested Data', () => <NestedDataTable />) .add('Display Nested Data', () => <NestedDataTable />)
@@ -142,7 +198,8 @@ storiesOf('Work on Header Columns', module)
.add('Column Event', () => <HeaderColumnEventTable />) .add('Column Event', () => <HeaderColumnEventTable />)
.add('Customize Column Class', () => <HeaderColumnClassTable />) .add('Customize Column Class', () => <HeaderColumnClassTable />)
.add('Customize Column Style', () => <HeaderColumnStyleTable />) .add('Customize Column Style', () => <HeaderColumnStyleTable />)
.add('Customize Column HTML attribute', () => <HeaderColumnAttrsTable />); .add('Customize Column HTML attribute', () => <HeaderColumnAttrsTable />)
.add('Header Class', () => <HeaderClassTable />);
storiesOf('Column Filter', module) storiesOf('Column Filter', module)
.add('Text Filter', () => <TextFilter />) .add('Text Filter', () => <TextFilter />)
@@ -153,12 +210,26 @@ storiesOf('Column Filter', module)
.add('Select Filter', () => <SelectFilter />) .add('Select Filter', () => <SelectFilter />)
.add('Select Filter with Default Value', () => <SelectFilterWithDefaultValue />) .add('Select Filter with Default Value', () => <SelectFilterWithDefaultValue />)
.add('Select Filter with Comparator', () => <SelectFilterComparator />) .add('Select Filter with Comparator', () => <SelectFilterComparator />)
.add('MultiSelect Filter', () => <MultiSelectFilter />)
.add('MultiSelect Filter with Default Value', () => <MultiSelectFilterDefaultValue />)
.add('Number Filter', () => <NumberFilter />) .add('Number Filter', () => <NumberFilter />)
.add('Number Filter with Default Value', () => <NumberFilterWithDefaultValue />) .add('Number Filter with Default Value', () => <NumberFilterWithDefaultValue />)
.add('Date Filter', () => <DateFilter />)
.add('Date Filter with Default Value', () => <DateFilterWithDefaultValue />)
.add('Custom Text Filter', () => <CustomTextFilter />) .add('Custom Text Filter', () => <CustomTextFilter />)
.add('Custom Select Filter', () => <CustomSelectFilter />) .add('Custom Select Filter', () => <CustomSelectFilter />)
.add('Custom Number Filter', () => <CustomNumberFilter />) .add('Custom Number Filter', () => <CustomNumberFilter />)
.add('Custom Filter Value', () => <CustomFilterValue />); .add('Custom Date Filter', () => <CustomDateFilter />)
.add('Custom MultiSelect Filter', () => <CustomMultiSelectFilter />)
.add('Custom Filter Value', () => <CustomFilterValue />)
.add('Programmatically Text Filter', () => <ProgrammaticallyTextFilter />)
.add('Programmatically Select Filter', () => <ProgrammaticallySelectFilter />)
.add('Programmatically Number Filter', () => <ProgrammaticallyNumberFilter />)
.add('Programmatically Date Filter', () => <ProgrammaticallyDateFilter />)
.add('Programmatically Multi Select Filter', () => <ProgrammaticallyMultiSelectFilter />)
.add('Custom Filter', () => <CustomFilter />)
.add('Advance Custom Filter', () => <AdvanceCustomFilter />)
.add('Clear All Filters', () => <ClearAllFilters />);
storiesOf('Work on Rows', module) storiesOf('Work on Rows', module)
.add('Customize Row Style', () => <RowStyleTable />) .add('Customize Row Style', () => <RowStyleTable />)
@@ -168,6 +239,7 @@ storiesOf('Work on Rows', module)
storiesOf('Sort Table', module) storiesOf('Sort Table', module)
.add('Enable Sort', () => <EnableSortTable />) .add('Enable Sort', () => <EnableSortTable />)
.add('Default Sort Table', () => <DefaultSortTable />) .add('Default Sort Table', () => <DefaultSortTable />)
.add('Default Sort Direction Table', () => <DefaultSortDirectionTable />)
.add('Sort Events', () => <SortEvents />) .add('Sort Events', () => <SortEvents />)
.add('Custom Sort Fuction', () => <CustomSortTable />) .add('Custom Sort Fuction', () => <CustomSortTable />)
.add('Custom Classes on Sorting Header Column', () => <HeaderSortingClassesTable />) .add('Custom Classes on Sorting Header Column', () => <HeaderSortingClassesTable />)
@@ -182,26 +254,61 @@ storiesOf('Cell Editing', module)
.add('Cell Level Editable', () => <CellLevelEditable />) .add('Cell Level Editable', () => <CellLevelEditable />)
.add('Rich Hook Functions', () => <CellEditHooks />) .add('Rich Hook Functions', () => <CellEditHooks />)
.add('Validation', () => <CellEditValidator />) .add('Validation', () => <CellEditValidator />)
.add('Custom Cell Style When Editing', () => <CellEditStyleTable />) .add('Custom Cell Style', () => <CellEditStyleTable />)
.add('Custom Cell Classes When Editing', () => <CellEditClassTable />); .add('Custom Cell Classes', () => <CellEditClassTable />)
.add('Custom Editor Classes', () => <EditorClassTable />)
.add('Custom Editor Style', () => <EditorStyleTable />)
.add('Dropdown Editor', () => <DropdownEditorTable />)
.add('Textarea Editor', () => <TextareaEditorTable />)
.add('Checkbox Editor', () => <CheckboxEditorTable />)
.add('Date Editor', () => <DateEditorTable />)
.add('Custom Editor', () => <CustomEditorTable />);
storiesOf('Row Selection', module) storiesOf('Row Selection', module)
.add('Single Selection', () => <SingleSelectionTable />) .add('Single Selection', () => <SingleSelectionTable />)
.add('Multiple Selection', () => <MultipleSelectionTable />) .add('Multiple Selection', () => <MultipleSelectionTable />)
.add('Click to Select', () => <ClickToSelectTable />) .add('Click to Select', () => <ClickToSelectTable />)
.add('Default Select', () => <DefaultSelectTable />)
.add('Selection Management', () => <SelectionManagement />)
.add('Click to Select and Edit Cell', () => <ClickToSelectWithCellEditTable />) .add('Click to Select and Edit Cell', () => <ClickToSelectWithCellEditTable />)
.add('Selection without Data', () => <SelectionNoDataTable />)
.add('Selection Style', () => <SelectionStyleTable />) .add('Selection Style', () => <SelectionStyleTable />)
.add('Selection Class', () => <SelectionClassTable />) .add('Selection Class', () => <SelectionClassTable />)
.add('Custom Selection', () => <CustomSelectionTable />)
.add('Selection Background Color', () => <SelectionBgColorTable />) .add('Selection Background Color', () => <SelectionBgColorTable />)
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />) .add('Not Selectabled Rows', () => <NonSelectableRowsTable />)
.add('Selection Hooks', () => <SelectionHooks />) .add('Selection Hooks', () => <SelectionHooks />)
.add('Hide Selection Column', () => <HideSelectionColumnTable />); .add('Hide Selection Column', () => <HideSelectionColumnTable />);
storiesOf('Row Expand', module)
.add('Basic Row Expand', () => <BasicRowExpand />)
.add('Expand Management', () => <RowExpandManagement />)
.add('Non Expandabled Rows', () => <NonExpandableRows />)
.add('Expand Indicator', () => <ExpandColumn />)
.add('Custom Expand Indicator', () => <CustomExpandColumn />)
.add('Expand Hooks', () => <ExpandHooks />);
storiesOf('Pagination', module) storiesOf('Pagination', module)
.add('Basic Pagination Table', () => <PaginationTable />) .add('Basic Pagination Table', () => <PaginationTable />)
.add('Pagination Hooks', () => <PaginationHooksTable />) .add('Pagination Hooks', () => <PaginationHooksTable />)
.add('Custom Pagination', () => <CustomPaginationTable />); .add('Custom Pagination', () => <CustomPaginationTable />);
storiesOf('Table Search', module)
.add('Basic Search Table', () => <SearchTable />)
.add('Default Custom Search', () => <DefaultCustomSearch />)
.add('Fully Custom Search', () => <FullyCustomSearch />)
.add('Search Fromatted Value', () => <SearchFormattedData />)
.add('Custom Search Value', () => <CustomSearchValue />);
storiesOf('Export CSV', module)
.add('Basic Export CSV', () => <ExportCSV />)
.add('Format CSV Column', () => <CSVFormatter />)
.add('Custom CSV Header', () => <CustomCSVHeader />)
.add('Hide CSV Column', () => <HideCSVColumn />)
.add('CSV Column Type', () => <CSVColumnType />)
.add('Custom CSV Button', () => <CustomCSVButton />)
.add('Custom CSV', () => <CustomCSV />);
storiesOf('EmptyTableOverlay', module) storiesOf('EmptyTableOverlay', module)
.add('Empty Table Overlay', () => <EmptyTableOverlay />) .add('Empty Table Overlay', () => <EmptyTableOverlay />)
.add('Table Overlay', () => <TableOverlay />); .add('Table Overlay', () => <TableOverlay />);
@@ -210,5 +317,6 @@ storiesOf('Remote', module)
.add('Remote Sort', () => <RemoteSort />) .add('Remote Sort', () => <RemoteSort />)
.add('Remote Filter', () => <RemoteFilter />) .add('Remote Filter', () => <RemoteFilter />)
.add('Remote Pagination', () => <RemotePaginationTable />) .add('Remote Pagination', () => <RemotePaginationTable />)
.add('Remote Search', () => <RemoteSearch />)
.add('Remote Cell Editing', () => <RemoteCellEdit />) .add('Remote Cell Editing', () => <RemoteCellEdit />)
.add('Remote All', () => <RemoteAll />); .add('Remote All', () => <RemoteAll />);

View File

@@ -0,0 +1,11 @@
table.foo {
background-color: $grey-500;
}
table#bar {
background-color: $light-blue;
}
div.boo {
border: 2px solid salmon;
}

View File

@@ -10,3 +10,7 @@
.demo-row-odd { .demo-row-odd {
background-color: $green-lighten-4; background-color: $green-lighten-4;
} }
.header-class {
background-color: $green-lighten-4;
}

View File

@@ -0,0 +1,3 @@
.custome-search-field {
background-color: #c8e6c9;
}

View File

@@ -3,10 +3,12 @@
@import "base/github-corner"; @import "base/github-corner";
@import "base/code-block"; @import "base/code-block";
@import "base-table/index";
@import "welcome/index"; @import "welcome/index";
@import "columns/index"; @import "columns/index";
@import "cell-edit/index"; @import "cell-edit/index";
@import "row-selection/index"; @import "row-selection/index";
@import "rows/index"; @import "rows/index";
@import "sort/index"; @import "sort/index";
@import "search/index";
@import "loading-overlay/index"; @import "loading-overlay/index";

View File

@@ -18,7 +18,10 @@ You can get all types of filters via import and these filters are a factory func
* TextFilter * TextFilter
* SelectFilter * SelectFilter
* MultiSelectFilter
* NumberFilter * NumberFilter
* DateFilter
* CustomFilter
* **Coming soon!** * **Coming soon!**
## Add CSS ## Add CSS
@@ -112,6 +115,52 @@ const qualityFilter = selectFilter({
// omit... // omit...
``` ```
## MultiSelect Filter
A quick example:
```js
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
// omit...
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [
..., {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions
})
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
```
Following is an example for custom select filter:
```js
import filterFactory, { multiSelectFilter, Comparator } from 'react-bootstrap-table2-filter';
// omit...
const qualityFilter = multiSelectFilter({
options: selectOptions,
placeholder: 'My Custom PlaceHolder', // custom the input placeholder
className: 'my-custom-text-filter', // custom classname on input
defaultValue: '2', // default filtering value
comparator: Comparator.LIKE, // default is Comparator.EQ
style: { ... }, // your custom styles on input
withoutEmptyOption: true // hide the default select option
});
// omit...
```
## Number Filter ## Number Filter
```js ```js
@@ -129,7 +178,7 @@ const columns = [..., {
Numner filter is same as other filter, you can custom the number filter via `numberFilter` factory function: Numner filter is same as other filter, you can custom the number filter via `numberFilter` factory function:
```js ```js
import filterFactory, { selectFilter, Comparator } from 'react-bootstrap-table2-filter'; import filterFactory, { selectFilter, Comparator, numberFilter } from 'react-bootstrap-table2-filter';
// omit... // omit...
const numberFilter = numberFilter({ const numberFilter = numberFilter({
@@ -150,3 +199,92 @@ const numberFilter = numberFilter({
// omit... // omit...
``` ```
## Date Filter
```js
import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter';
const columns = [..., {
dataField: 'date',
text: 'Product date',
filter: dateFilter()
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
```
> **Notes:** date filter accept a Javascript Date object in your raw data and you have to use `column.formatter` to make it as your prefer string result
Date filter is same as other filter, you can custom the date filter via `dateFilter` factory function:
```js
import filterFactory, { selectFilter, Comparator } from 'react-bootstrap-table2-filter';
// omit...
const dateFilter = dateFilter({
delay: 600, // how long will trigger filtering after user typing, default is 500 ms
placeholder: 'custom placeholder', // placeholder for date input
withoutEmptyComparatorOption: true, // dont render empty option for comparator
comparators: [Comparator.EQ, Comparator.GT, Comparator.LT], // Custom the comparators
style: { display: 'inline-grid' }, // custom the style on date filter
className: 'custom-dateFilter-class', // custom the class on date filter
comparatorStyle: { backgroundColor: 'antiquewhite' }, // custom the style on comparator select
comparatorClassName: 'custom-comparator-class', // custom the class on comparator select
dateStyle: { backgroundColor: 'cadetblue', margin: '0px' }, // custom the style on date input
dateClassName: 'custom-date-class', // custom the class on date input
defaultValue: { date: new Date(2018, 0, 1), comparator: Comparator.GT } // default value
})
// omit...
```
## Custom Filter
```js
import filterFactory, { customFilter } from 'react-bootstrap-table2-filter';
const columns = [..., {
dataField: 'date',
text: 'Product Name',
filter: customFilter(),
filterRenderer: (onFilter, column) => .....
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
```
In custom filter case, you are suppose to finish following two steps:
1. Call `customFilter` and pass to `column.filter`
2. Give `column.filterRenderer` as a callback function and return your custom filter element.
### column.filterRenderer
This function will pass two argument to you:
1. `onFilter`: call it to trigger filter when you need.
2. `column`: Just the column object!
In the end, please remember to return your custom filter element!
### customFilter
`customFilter` function just same as `textFilter`, `selectFilter` etc, it is for customization reason. However, in the custom filter case, there's only one props is valid: `type`
```js
import filterFactory, { FILTER_TYPES } from 'react-bootstrap-table2-filter';
const customFilter = customFilter({
type: FILTER_TYPES.NUMBER, // default is FILTER_TYPES.TEXT
})
```
`type` is a way to ask `react-bootstrap-table` to filter you data as number, select, date or normal text.
### FILTER_TYPES
Following properties is valid in `FILTER_TYPES`:
* TEXT
* SELECT
* NUMBER
* DATE

View File

@@ -1,14 +1,19 @@
import TextFilter from './src/components/text'; import TextFilter from './src/components/text';
import SelectFilter from './src/components/select'; import SelectFilter from './src/components/select';
import MultiSelectFilter from './src/components/multiselect';
import NumberFilter from './src/components/number'; import NumberFilter from './src/components/number';
import wrapperFactory from './src/wrapper'; import DateFilter from './src/components/date';
import createContext from './src/context';
import * as Comparison from './src/comparison'; import * as Comparison from './src/comparison';
import { FILTER_TYPE } from './src/const';
export default (options = {}) => ({ export default (options = {}) => ({
wrapperFactory, createContext,
options options
}); });
export const FILTER_TYPES = FILTER_TYPE;
export const Comparator = Comparison; export const Comparator = Comparison;
export const textFilter = (props = {}) => ({ export const textFilter = (props = {}) => ({
@@ -21,7 +26,21 @@ export const selectFilter = (props = {}) => ({
props props
}); });
export const multiSelectFilter = (props = {}) => ({
Filter: MultiSelectFilter,
props
});
export const numberFilter = (props = {}) => ({ export const numberFilter = (props = {}) => ({
Filter: NumberFilter, Filter: NumberFilter,
props props
}); });
export const dateFilter = (props = {}) => ({
Filter: DateFilter,
props
});
export const customFilter = (props = {}) => ({
props
});

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-filter", "name": "react-bootstrap-table2-filter",
"version": "0.1.3", "version": "1.0.0",
"description": "it's a column filter addon for react-bootstrap-table2", "description": "it's a column filter addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {
@@ -38,7 +38,7 @@
], ],
"peerDependencies": { "peerDependencies": {
"prop-types": "^15.0.0", "prop-types": "^15.0.0",
"react": "^16.0.0", "react": "^16.3.0",
"react-dom": "^16.0.0" "react-dom": "^16.3.0"
} }
} }

View File

@@ -0,0 +1,213 @@
/* eslint react/require-default-props: 0 */
/* eslint jsx-a11y/no-static-element-interactions: 0 */
/* eslint no-return-assign: 0 */
/* eslint prefer-template: 0 */
import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
import * as Comparator from '../comparison';
import { FILTER_TYPE } from '../const';
const legalComparators = [
Comparator.EQ,
Comparator.NE,
Comparator.GT,
Comparator.GE,
Comparator.LT,
Comparator.LE
];
function dateParser(d) {
return `${d.getFullYear()}-${('0' + (d.getMonth() + 1)).slice(-2)}-${('0' + d.getDate()).slice(-2)}`;
}
class DateFilter extends Component {
constructor(props) {
super(props);
this.timeout = null;
this.comparators = props.comparators || legalComparators;
this.applyFilter = this.applyFilter.bind(this);
this.onChangeDate = this.onChangeDate.bind(this);
this.onChangeComparator = this.onChangeComparator.bind(this);
}
componentDidMount() {
const { getFilter } = this.props;
const comparator = this.dateFilterComparator.value;
const date = this.inputDate.value;
if (comparator && date) {
this.applyFilter(date, comparator, true);
}
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
this.dateFilterComparator.value = filterVal.comparator;
this.inputDate.value = dateParser(filterVal.date);
this.applyFilter(filterVal.date, filterVal.comparator);
});
}
}
componentWillUnmount() {
if (this.timeout) clearTimeout(this.timeout);
}
onChangeDate(e) {
const comparator = this.dateFilterComparator.value;
const filterValue = e.target.value;
this.applyFilter(filterValue, comparator);
}
onChangeComparator(e) {
const value = this.inputDate.value;
const comparator = e.target.value;
this.applyFilter(value, comparator);
}
getComparatorOptions() {
const optionTags = [];
const { withoutEmptyComparatorOption } = this.props;
if (!withoutEmptyComparatorOption) {
optionTags.push(<option key="-1" />);
}
for (let i = 0; i < this.comparators.length; i += 1) {
optionTags.push(
<option key={ i } value={ this.comparators[i] }>
{ this.comparators[i] }
</option>
);
}
return optionTags;
}
getDefaultDate() {
let defaultDate = '';
const { defaultValue } = this.props;
if (defaultValue && defaultValue.date) {
// Set the appropriate format for the input type=date, i.e. "YYYY-MM-DD"
defaultDate = dateParser(new Date(defaultValue.date));
}
return defaultDate;
}
applyFilter(value, comparator, isInitial) {
// if (!comparator || !value) {
// return;
// }
const { column, onFilter, delay } = this.props;
const execute = () => {
// Incoming value should always be a string, and the defaultDate
// above is implemented as an empty string, so we can just check for that.
// instead of parsing an invalid Date. The filter function will interpret
// null as an empty date field
const date = value === '' ? null : new Date(value);
onFilter(column, FILTER_TYPE.DATE, isInitial)({ date, comparator });
};
if (delay) {
this.timeout = setTimeout(() => { execute(); }, delay);
} else {
execute();
}
}
render() {
const {
placeholder,
column: { text },
style,
comparatorStyle,
dateStyle,
className,
comparatorClassName,
dateClassName,
defaultValue
} = this.props;
return (
<div
onClick={ e => e.stopPropagation() }
className={ `filter date-filter ${className}` }
style={ style }
>
<select
ref={ n => this.dateFilterComparator = n }
style={ comparatorStyle }
className={ `date-filter-comparator form-control ${comparatorClassName}` }
onChange={ this.onChangeComparator }
defaultValue={ defaultValue ? defaultValue.comparator : '' }
>
{ this.getComparatorOptions() }
</select>
<input
ref={ n => this.inputDate = n }
className={ `filter date-filter-input form-control ${dateClassName}` }
style={ dateStyle }
type="date"
onChange={ this.onChangeDate }
placeholder={ placeholder || `Enter ${text}...` }
defaultValue={ this.getDefaultDate() }
/>
</div>
);
}
}
DateFilter.propTypes = {
onFilter: PropTypes.func.isRequired,
column: PropTypes.object.isRequired,
delay: PropTypes.number,
defaultValue: PropTypes.shape({
date: PropTypes.oneOfType([PropTypes.object]),
comparator: PropTypes.oneOf([...legalComparators, ''])
}),
/* eslint consistent-return: 0 */
comparators: (props, propName) => {
if (!props[propName]) {
return;
}
for (let i = 0; i < props[propName].length; i += 1) {
let comparatorIsValid = false;
for (let j = 0; j < legalComparators.length; j += 1) {
if (legalComparators[j] === props[propName][i] || props[propName][i] === '') {
comparatorIsValid = true;
break;
}
}
if (!comparatorIsValid) {
return new Error(`Date comparator provided is not supported.
Use only ${legalComparators}`);
}
}
},
placeholder: PropTypes.string,
withoutEmptyComparatorOption: PropTypes.bool,
style: PropTypes.object,
comparatorStyle: PropTypes.object,
dateStyle: PropTypes.object,
className: PropTypes.string,
comparatorClassName: PropTypes.string,
dateClassName: PropTypes.string,
getFilter: PropTypes.func
};
DateFilter.defaultProps = {
delay: 0,
defaultValue: {
date: undefined,
comparator: ''
},
withoutEmptyComparatorOption: false,
comparators: legalComparators,
placeholder: undefined,
style: undefined,
className: '',
comparatorStyle: undefined,
comparatorClassName: '',
dateStyle: undefined,
dateClassName: ''
};
export default DateFilter;

View File

@@ -0,0 +1,152 @@
/* eslint react/require-default-props: 0 */
/* eslint no-return-assign: 0 */
/* eslint no-param-reassign: 0 */
/* eslint react/no-unused-prop-types: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { LIKE, EQ } from '../comparison';
import { FILTER_TYPE } from '../const';
function optionsEquals(currOpts, prevOpts) {
const keys = Object.keys(currOpts);
for (let i = 0; i < keys.length; i += 1) {
if (currOpts[keys[i]] !== prevOpts[keys[i]]) {
return false;
}
}
return Object.keys(currOpts).length === Object.keys(prevOpts).length;
}
const getSelections = container =>
Array.from(container.selectedOptions).map(item => item.value);
class MultiSelectFilter extends Component {
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.applyFilter = this.applyFilter.bind(this);
const isSelected = props.defaultValue.map(item => props.options[item]).length > 0;
this.state = { isSelected };
}
componentDidMount() {
const { getFilter } = this.props;
const value = getSelections(this.selectInput);
if (value && value.length > 0) {
this.applyFilter(value);
}
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
this.selectInput.value = filterVal;
this.applyFilter(filterVal);
});
}
}
componentDidUpdate(prevProps) {
let needFilter = false;
if (this.props.defaultValue !== prevProps.defaultValue) {
needFilter = true;
} else if (!optionsEquals(this.props.options, prevProps.options)) {
needFilter = true;
}
if (needFilter) {
this.applyFilter(this.selectInput.value);
}
}
getOptions() {
const optionTags = [];
const { options, placeholder, column, withoutEmptyOption } = this.props;
if (!withoutEmptyOption) {
optionTags.push((
<option key="-1" value="">{ placeholder || `Select ${column.text}...` }</option>
));
}
Object.keys(options).forEach(key =>
optionTags.push(<option key={ key } value={ key }>{ options[key] }</option>)
);
return optionTags;
}
cleanFiltered() {
const value = (this.props.defaultValue !== undefined) ? this.props.defaultValue : [];
this.selectInput.value = value;
this.applyFilter(value);
}
applyFilter(value) {
if (value.length === 1 && value[0] === '') {
value = [];
}
this.setState(() => ({ isSelected: value.length > 0 }));
this.props.onFilter(this.props.column, FILTER_TYPE.MULTISELECT)(value);
}
filter(e) {
const value = getSelections(e.target);
this.applyFilter(value);
}
render() {
const {
style,
className,
defaultValue,
onFilter,
column,
options,
comparator,
withoutEmptyOption,
caseSensitive,
getFilter,
...rest
} = this.props;
const selectClass =
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return (
<select
{ ...rest }
ref={ n => this.selectInput = n }
style={ style }
multiple
className={ selectClass }
onChange={ this.filter }
onClick={ e => e.stopPropagation() }
defaultValue={ defaultValue !== undefined ? defaultValue : '' }
>
{ this.getOptions() }
</select>
);
}
}
MultiSelectFilter.propTypes = {
onFilter: PropTypes.func.isRequired,
column: PropTypes.object.isRequired,
options: PropTypes.object.isRequired,
comparator: PropTypes.oneOf([LIKE, EQ]),
placeholder: PropTypes.string,
style: PropTypes.object,
className: PropTypes.string,
withoutEmptyOption: PropTypes.bool,
defaultValue: PropTypes.array,
caseSensitive: PropTypes.bool,
getFilter: PropTypes.func
};
MultiSelectFilter.defaultProps = {
defaultValue: [],
className: '',
withoutEmptyOption: false,
comparator: EQ,
caseSensitive: true
};
export default MultiSelectFilter;

View File

@@ -1,3 +1,5 @@
/* eslint jsx-a11y/no-static-element-interactions: 0 */
/* eslint react/require-default-props: 0 */
/* eslint no-return-assign: 0 */ /* eslint no-return-assign: 0 */
import React, { Component } from 'react'; import React, { Component } from 'react';
@@ -30,11 +32,25 @@ class NumberFilter extends Component {
} }
componentDidMount() { componentDidMount() {
const { column, onFilter } = this.props; const { column, onFilter, getFilter } = this.props;
const comparator = this.numberFilterComparator.value; const comparator = this.numberFilterComparator.value;
const number = this.numberFilter.value; const number = this.numberFilter.value;
if (comparator && number) { if (comparator && number) {
onFilter(column, { number, comparator }, FILTER_TYPE.NUMBER); onFilter(column, FILTER_TYPE.NUMBER, true)({ number, comparator });
}
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
this.setState(() => ({ isSelected: (filterVal !== '') }));
this.numberFilterComparator.value = filterVal.comparator;
this.numberFilter.value = filterVal.number;
onFilter(column, FILTER_TYPE.NUMBER)({
number: filterVal.number,
comparator: filterVal.comparator
});
});
} }
} }
@@ -53,7 +69,7 @@ class NumberFilter extends Component {
} }
const filterValue = e.target.value; const filterValue = e.target.value;
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
onFilter(column, { number: filterValue, comparator }, FILTER_TYPE.NUMBER); onFilter(column, FILTER_TYPE.NUMBER)({ number: filterValue, comparator });
}, delay); }, delay);
} }
@@ -65,7 +81,7 @@ class NumberFilter extends Component {
// if (comparator === '') { // if (comparator === '') {
// return; // return;
// } // }
onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER); onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator });
} }
onChangeComparator(e) { onChangeComparator(e) {
@@ -75,7 +91,7 @@ class NumberFilter extends Component {
// if (value === '') { // if (value === '') {
// return; // return;
// } // }
onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER); onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator });
} }
getComparatorOptions() { getComparatorOptions() {
@@ -116,7 +132,7 @@ class NumberFilter extends Component {
this.setState(() => ({ isSelected: (number !== '') })); this.setState(() => ({ isSelected: (number !== '') }));
this.numberFilterComparator.value = comparator; this.numberFilterComparator.value = comparator;
this.numberFilter.value = number; this.numberFilter.value = number;
onFilter(column, { number, comparator }, FILTER_TYPE.NUMBER); onFilter(column, FILTER_TYPE.NUMBER)({ number, comparator });
} }
cleanFiltered() { cleanFiltered() {
@@ -126,7 +142,7 @@ class NumberFilter extends Component {
this.setState(() => ({ isSelected: (value !== '') })); this.setState(() => ({ isSelected: (value !== '') }));
this.numberFilterComparator.value = comparator; this.numberFilterComparator.value = comparator;
this.numberFilter.value = value; this.numberFilter.value = value;
onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER); onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator });
} }
render() { render() {
@@ -152,7 +168,11 @@ class NumberFilter extends Component {
`; `;
return ( return (
<div className={ `filter number-filter ${className}` } style={ style }> <div
onClick={ e => e.stopPropagation() }
className={ `filter number-filter ${className}` }
style={ style }
>
<select <select
ref={ n => this.numberFilterComparator = n } ref={ n => this.numberFilterComparator = n }
style={ comparatorStyle } style={ comparatorStyle }
@@ -224,7 +244,8 @@ NumberFilter.propTypes = {
comparatorStyle: PropTypes.object, comparatorStyle: PropTypes.object,
comparatorClassName: PropTypes.string, comparatorClassName: PropTypes.string,
numberStyle: PropTypes.object, numberStyle: PropTypes.object,
numberClassName: PropTypes.string numberClassName: PropTypes.string,
getFilter: PropTypes.func
}; };
NumberFilter.defaultProps = { NumberFilter.defaultProps = {

View File

@@ -25,9 +25,21 @@ class SelectFilter extends Component {
} }
componentDidMount() { componentDidMount() {
const { column, onFilter, getFilter } = this.props;
const value = this.selectInput.value; const value = this.selectInput.value;
if (value && value !== '') { if (value && value !== '') {
this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); onFilter(column, FILTER_TYPE.SELECT, true)(value);
}
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
this.setState(() => ({ isSelected: filterVal !== '' }));
this.selectInput.value = filterVal;
onFilter(column, FILTER_TYPE.SELECT)(filterVal);
});
} }
} }
@@ -41,7 +53,7 @@ class SelectFilter extends Component {
if (needFilter) { if (needFilter) {
const value = this.selectInput.value; const value = this.selectInput.value;
if (value) { if (value) {
this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value);
} }
} }
} }
@@ -64,19 +76,19 @@ class SelectFilter extends Component {
const value = (this.props.defaultValue !== undefined) ? this.props.defaultValue : ''; const value = (this.props.defaultValue !== undefined) ? this.props.defaultValue : '';
this.setState(() => ({ isSelected: value !== '' })); this.setState(() => ({ isSelected: value !== '' }));
this.selectInput.value = value; this.selectInput.value = value;
this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value);
} }
applyFilter(value) { applyFilter(value) {
this.selectInput.value = value; this.selectInput.value = value;
this.setState(() => ({ isSelected: value !== '' })); this.setState(() => ({ isSelected: value !== '' }));
this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value);
} }
filter(e) { filter(e) {
const { value } = e.target; const { value } = e.target;
this.setState(() => ({ isSelected: value !== '' })); this.setState(() => ({ isSelected: value !== '' }));
this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value);
} }
render() { render() {
@@ -90,6 +102,7 @@ class SelectFilter extends Component {
comparator, comparator,
withoutEmptyOption, withoutEmptyOption,
caseSensitive, caseSensitive,
getFilter,
...rest ...rest
} = this.props; } = this.props;
@@ -103,6 +116,7 @@ class SelectFilter extends Component {
style={ style } style={ style }
className={ selectClass } className={ selectClass }
onChange={ this.filter } onChange={ this.filter }
onClick={ e => e.stopPropagation() }
defaultValue={ defaultValue !== undefined ? defaultValue : '' } defaultValue={ defaultValue !== undefined ? defaultValue : '' }
> >
{ this.getOptions() } { this.getOptions() }
@@ -121,7 +135,8 @@ SelectFilter.propTypes = {
className: PropTypes.string, className: PropTypes.string,
withoutEmptyOption: PropTypes.bool, withoutEmptyOption: PropTypes.bool,
defaultValue: PropTypes.any, defaultValue: PropTypes.any,
caseSensitive: PropTypes.bool caseSensitive: PropTypes.bool,
getFilter: PropTypes.func
}; };
SelectFilter.defaultProps = { SelectFilter.defaultProps = {

View File

@@ -17,10 +17,21 @@ class TextFilter extends Component {
value: props.defaultValue value: props.defaultValue
}; };
} }
componentDidMount() { componentDidMount() {
const { onFilter, getFilter, column } = this.props;
const defaultValue = this.input.value; const defaultValue = this.input.value;
if (defaultValue) { if (defaultValue) {
this.props.onFilter(this.props.column, defaultValue, FILTER_TYPE.TEXT); onFilter(this.props.column, FILTER_TYPE.TEXT, true)(defaultValue);
}
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
this.setState(() => ({ value: filterVal }));
onFilter(column, FILTER_TYPE.TEXT)(filterVal);
});
} }
} }
@@ -40,7 +51,7 @@ class TextFilter extends Component {
const filterValue = e.target.value; const filterValue = e.target.value;
this.setState(() => ({ value: filterValue })); this.setState(() => ({ value: filterValue }));
this.timeout = setTimeout(() => { this.timeout = setTimeout(() => {
this.props.onFilter(this.props.column, filterValue, FILTER_TYPE.TEXT); this.props.onFilter(this.props.column, FILTER_TYPE.TEXT)(filterValue);
}, this.props.delay); }, this.props.delay);
} }
@@ -53,12 +64,12 @@ class TextFilter extends Component {
cleanFiltered() { cleanFiltered() {
const value = this.props.defaultValue; const value = this.props.defaultValue;
this.setState(() => ({ value })); this.setState(() => ({ value }));
this.props.onFilter(this.props.column, value, FILTER_TYPE.TEXT); this.props.onFilter(this.props.column, FILTER_TYPE.TEXT)(value);
} }
applyFilter(filterText) { applyFilter(filterText) {
this.setState(() => ({ value: filterText })); this.setState(() => ({ value: filterText }));
this.props.onFilter(this.props.column, filterText, FILTER_TYPE.TEXT); this.props.onFilter(this.props.column, FILTER_TYPE.TEXT)(filterText);
} }
handleClick(e) { handleClick(e) {
@@ -77,8 +88,10 @@ class TextFilter extends Component {
onFilter, onFilter,
caseSensitive, caseSensitive,
defaultValue, defaultValue,
getFilter,
...rest ...rest
} = this.props; } = this.props;
// stopPropagation for onClick event is try to prevent sort was triggered. // stopPropagation for onClick event is try to prevent sort was triggered.
return ( return (
<input <input
@@ -105,7 +118,8 @@ TextFilter.propTypes = {
placeholder: PropTypes.string, placeholder: PropTypes.string,
style: PropTypes.object, style: PropTypes.object,
className: PropTypes.string, className: PropTypes.string,
caseSensitive: PropTypes.bool caseSensitive: PropTypes.bool,
getFilter: PropTypes.func
}; };
TextFilter.defaultProps = { TextFilter.defaultProps = {

View File

@@ -1,7 +1,9 @@
export const FILTER_TYPE = { export const FILTER_TYPE = {
TEXT: 'TEXT', TEXT: 'TEXT',
SELECT: 'SELECT', SELECT: 'SELECT',
NUMBER: 'NUMBER' MULTISELECT: 'MULTISELECT',
NUMBER: 'NUMBER',
DATE: 'DATE'
}; };
export const FILTER_DELAY = 500; export const FILTER_DELAY = 500;

View File

@@ -0,0 +1,99 @@
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import { filters } from './filter';
import { LIKE, EQ } from './comparison';
import { FILTER_TYPE } from './const';
export default (
_,
isRemoteFiltering,
handleFilterChange
) => {
const FilterContext = React.createContext();
class FilterProvider extends React.Component {
static propTypes = {
data: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired
}
constructor(props) {
super(props);
this.currFilters = {};
this.onFilter = this.onFilter.bind(this);
this.onExternalFilter = this.onExternalFilter.bind(this);
}
componentDidMount() {
if (isRemoteFiltering() && Object.keys(this.currFilters).length > 0) {
handleFilterChange(this.currFilters);
}
}
onFilter(column, filterType, initialize = false) {
return (filterVal) => {
// watch out here if migration to context API, #334
const currFilters = Object.assign({}, this.currFilters);
const { dataField, filter } = column;
const needClearFilters =
!_.isDefined(filterVal) ||
filterVal === '' ||
filterVal.length === 0;
if (needClearFilters) {
delete currFilters[dataField];
} else {
// select default comparator is EQ, others are LIKE
const {
comparator = (filterType === FILTER_TYPE.SELECT ? EQ : LIKE),
caseSensitive = false
} = filter.props;
currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive };
}
this.currFilters = currFilters;
if (isRemoteFiltering()) {
if (!initialize) {
handleFilterChange(this.currFilters);
}
return;
}
this.forceUpdate();
};
}
onExternalFilter(column, filterType) {
return (value) => {
this.onFilter(column, filterType)(value);
};
}
render() {
let { data } = this.props;
if (!isRemoteFiltering()) {
data = filters(data, this.props.columns, _)(this.currFilters);
}
return (
<FilterContext.Provider value={ {
data,
onFilter: this.onFilter,
onExternalFilter: this.onExternalFilter
} }
>
{ this.props.children }
</FilterContext.Provider>
);
}
}
return {
Provider: FilterProvider,
Consumer: FilterContext.Consumer
};
};

View File

@@ -6,9 +6,13 @@ import { LIKE, EQ, NE, GT, GE, LT, LE } from './comparison';
export const filterByText = _ => ( export const filterByText = _ => (
data, data,
dataField, dataField,
{ filterVal = '', comparator = LIKE, caseSensitive }, { filterVal: userInput = '', comparator = LIKE, caseSensitive },
customFilterValue customFilterValue
) => ) => {
// make sure filter value to be a string
const filterVal = userInput.toString();
return (
data.filter((row) => { data.filter((row) => {
let cell = _.get(row, dataField); let cell = _.get(row, dataField);
if (customFilterValue) { if (customFilterValue) {
@@ -21,15 +25,18 @@ export const filterByText = _ => (
if (caseSensitive) { if (caseSensitive) {
return cellStr.includes(filterVal); return cellStr.includes(filterVal);
} }
return cellStr.toLocaleUpperCase().includes(filterVal.toLocaleUpperCase());
}); return cellStr.toLocaleUpperCase().indexOf(filterVal.toLocaleUpperCase()) !== -1;
})
);
};
export const filterByNumber = _ => ( export const filterByNumber = _ => (
data, data,
dataField, dataField,
{ filterVal: { comparator, number } }, { filterVal: { comparator, number } },
customFilterValue customFilterValue
) => ) => (
data.filter((row) => { data.filter((row) => {
if (number === '' || !comparator) return true; if (number === '' || !comparator) return true;
let valid = true; let valid = true;
@@ -81,7 +88,124 @@ export const filterByNumber = _ => (
} }
} }
return valid; return valid;
})
);
export const filterByDate = _ => (
data,
dataField,
{ filterVal: { comparator, date } },
customFilterValue
) => {
if (!date || !comparator) return data;
const filterDate = date.getDate();
const filterMonth = date.getMonth();
const filterYear = date.getFullYear();
return data.filter((row) => {
let valid = true;
let cell = _.get(row, dataField);
if (customFilterValue) {
cell = customFilterValue(cell, row);
}
if (typeof cell !== 'object') {
cell = new Date(cell);
}
const targetDate = cell.getDate();
const targetMonth = cell.getMonth();
const targetYear = cell.getFullYear();
switch (comparator) {
case EQ: {
if (
filterDate !== targetDate ||
filterMonth !== targetMonth ||
filterYear !== targetYear
) {
valid = false;
}
break;
}
case GT: {
if (cell <= date) {
valid = false;
}
break;
}
case GE: {
if (targetYear < filterYear) {
valid = false;
} else if (targetYear === filterYear &&
targetMonth < filterMonth) {
valid = false;
} else if (targetYear === filterYear &&
targetMonth === filterMonth &&
targetDate < filterDate) {
valid = false;
}
break;
}
case LT: {
if (cell >= date) {
valid = false;
}
break;
}
case LE: {
if (targetYear > filterYear) {
valid = false;
} else if (targetYear === filterYear &&
targetMonth > filterMonth) {
valid = false;
} else if (targetYear === filterYear &&
targetMonth === filterMonth &&
targetDate > filterDate) {
valid = false;
}
break;
}
case NE: {
if (
filterDate === targetDate &&
filterMonth === targetMonth &&
filterYear === targetYear
) {
valid = false;
}
break;
}
default: {
console.error('Date comparator provided is not supported');
break;
}
}
return valid;
}); });
};
export const filterByArray = _ => (
data,
dataField,
{ filterVal, comparator }
) => {
if (filterVal.length === 0) return data;
const refinedFilterVal = filterVal
.filter(x => _.isDefined(x))
.map(x => x.toString());
return data.filter((row) => {
const cell = _.get(row, dataField);
let cellStr = _.isDefined(cell) ? cell.toString() : '';
if (comparator === EQ) {
return refinedFilterVal.indexOf(cellStr) !== -1;
}
cellStr = cellStr.toLocaleUpperCase();
return refinedFilterVal.some(item => cellStr.indexOf(item.toLocaleUpperCase()) !== -1);
});
};
export const filterFactory = _ => (filterType) => { export const filterFactory = _ => (filterType) => {
let filterFn; let filterFn;
@@ -90,23 +214,35 @@ export const filterFactory = _ => (filterType) => {
case FILTER_TYPE.SELECT: case FILTER_TYPE.SELECT:
filterFn = filterByText(_); filterFn = filterByText(_);
break; break;
case FILTER_TYPE.MULTISELECT:
filterFn = filterByArray(_);
break;
case FILTER_TYPE.NUMBER: case FILTER_TYPE.NUMBER:
filterFn = filterByNumber(_); filterFn = filterByNumber(_);
break; break;
case FILTER_TYPE.DATE:
filterFn = filterByDate(_);
break;
default: default:
filterFn = filterByText(_); filterFn = filterByText(_);
} }
return filterFn; return filterFn;
}; };
export const filters = (store, columns, _) => (currFilters) => { export const filters = (data, columns, _) => (currFilters) => {
const factory = filterFactory(_); const factory = filterFactory(_);
let result = store.getAllData(); let result = data;
let filterFn; let filterFn;
Object.keys(currFilters).forEach((dataField) => { Object.keys(currFilters).forEach((dataField) => {
const filterObj = currFilters[dataField]; const filterObj = currFilters[dataField];
filterFn = factory(filterObj.filterType); filterFn = factory(filterObj.filterType);
const { filterValue } = columns.find(col => col.dataField === dataField); let filterValue;
for (let i = 0; i < columns.length; i += 1) {
if (columns[i].dataField === dataField) {
filterValue = columns[i].filterValue;
break;
}
}
result = filterFn(result, dataField, filterObj, filterValue); result = filterFn(result, dataField, filterObj, filterValue);
}); });
return result; return result;

View File

@@ -1,82 +0,0 @@
/* eslint no-param-reassign: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { filters } from './filter';
import { LIKE, EQ } from './comparison';
import { FILTER_TYPE } from './const';
export default (Base, {
_,
remoteResolver
}) =>
class FilterWrapper extends remoteResolver(Component) {
static propTypes = {
store: PropTypes.object.isRequired,
columns: PropTypes.array.isRequired
}
constructor(props) {
super(props);
this.state = { currFilters: {}, isDataChanged: props.isDataChanged || false };
this.onFilter = this.onFilter.bind(this);
}
componentWillReceiveProps({ isDataChanged, store, columns }) {
// consider to use lodash.isEqual
const isRemoteFilter = this.isRemoteFiltering() || this.isRemotePagination();
if (isRemoteFilter ||
JSON.stringify(this.state.currFilters) !== JSON.stringify(store.filters)) {
// I think this condition only isRemoteFilter is enough
store.filteredData = store.getAllData();
this.setState(() => ({ isDataChanged: true, currFilters: store.filters }));
} else if (isDataChanged) {
if (!isRemoteFilter && Object.keys(this.state.currFilters).length > 0) {
store.filteredData = filters(store, columns, _)(this.state.currFilters);
}
this.setState(() => ({ isDataChanged }));
} else {
this.setState(() => ({ isDataChanged: false }));
}
}
onFilter(column, filterVal, filterType) {
const { store, columns } = this.props;
const currFilters = Object.assign({}, this.state.currFilters);
const { dataField, filter } = column;
if (!_.isDefined(filterVal) || filterVal === '') {
delete currFilters[dataField];
} else {
// select default comparator is EQ, others are LIKE
const {
comparator = (filterType === FILTER_TYPE.SELECT ? EQ : LIKE),
caseSensitive = false
} = filter.props;
currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive };
}
store.filters = currFilters;
if (this.isRemoteFiltering() || this.isRemotePagination()) {
this.handleRemoteFilterChange();
// when remote filtering is enable, dont set currFilters state
// in the componentWillReceiveProps,
// it's the key point that we can know the filter is changed
return;
}
store.filteredData = filters(store, columns, _)(currFilters);
this.setState(() => ({ currFilters, isDataChanged: true }));
}
render() {
return (
<Base
{ ...this.props }
data={ this.props.store.data }
onFilter={ this.onFilter }
isDataChanged={ this.state.isDataChanged }
/>
);
}
};

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