mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2026-06-29 21:50:07 +00:00
Compare commits
185 Commits
react-boot
...
react-boot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
345cb83493 | ||
|
|
6e526f455b | ||
|
|
b05cf48f36 | ||
|
|
28249c9089 | ||
|
|
2b6081ab31 | ||
|
|
c935447266 | ||
|
|
ba1d6fa3ed | ||
|
|
1b3b68f8a7 | ||
|
|
4a3486cc3c | ||
|
|
cb49455a4e | ||
|
|
5e63d6ae59 | ||
|
|
e3ca6f2c24 | ||
|
|
96d33a60ba | ||
|
|
03389aece0 | ||
|
|
f86876ba51 | ||
|
|
44569d6df9 | ||
|
|
3663d1d4fe | ||
|
|
6225f0e5cb | ||
|
|
f0fd06a5f5 | ||
|
|
b181c98a38 | ||
|
|
1f51f1a08d | ||
|
|
586acaed68 | ||
|
|
528be5c058 | ||
|
|
76575bd9f1 | ||
|
|
54b98f41f4 | ||
|
|
ffac3a42c5 | ||
|
|
97b9e1097b | ||
|
|
a6de7fa84a | ||
|
|
3ec849bd94 | ||
|
|
208feb9849 | ||
|
|
a3ba464f40 | ||
|
|
cb970cded5 | ||
|
|
d5d8c54d98 | ||
|
|
2ec55f6de9 | ||
|
|
f7406bcafc | ||
|
|
925d3d7841 | ||
|
|
62c69490f2 | ||
|
|
3f957db56b | ||
|
|
495875792f | ||
|
|
c0416fc307 | ||
|
|
f7ba8e377d | ||
|
|
0d64443b26 | ||
|
|
7919a4001d | ||
|
|
fadbcdaa24 | ||
|
|
ec77a0539d | ||
|
|
b792803974 | ||
|
|
f0e37b130c | ||
|
|
03ece4b1fc | ||
|
|
0ec5b6cb9f | ||
|
|
e6d4a9641b | ||
|
|
5a442bf7ed | ||
|
|
a18932e9eb | ||
|
|
c36aa24c65 | ||
|
|
81ddd2c25b | ||
|
|
4af5b4f6ef | ||
|
|
dbd0f89a3d | ||
|
|
35b1e37940 | ||
|
|
6eaffe1993 | ||
|
|
46f0ce493b | ||
|
|
18b785d655 | ||
|
|
7b15bf45d9 | ||
|
|
0d4d32c6e4 | ||
|
|
760d459414 | ||
|
|
77301c2cf1 | ||
|
|
78ea63074e | ||
|
|
9c677fe174 | ||
|
|
c13b3fa197 | ||
|
|
167352f199 | ||
|
|
fc0b99e8a0 | ||
|
|
74bf885d47 | ||
|
|
400c307871 | ||
|
|
b1c086f424 | ||
|
|
d534c425d3 | ||
|
|
4ecf2433d0 | ||
|
|
6c086c3892 | ||
|
|
1e72c80566 | ||
|
|
8f4dc9907a | ||
|
|
2f7d0104a0 | ||
|
|
4f6809de84 | ||
|
|
216bc10142 | ||
|
|
5307e58813 | ||
|
|
143acde35e | ||
|
|
2525465a5a | ||
|
|
6d08a24a8f | ||
|
|
906180ad3f | ||
|
|
0ff0c33aa9 | ||
|
|
37e79a654b | ||
|
|
4e7cfdf5ea | ||
|
|
6f4e779a3e | ||
|
|
38d3e2df05 | ||
|
|
4e204f1ccd | ||
|
|
7e29999b40 | ||
|
|
01cf69392f | ||
|
|
7d7688582b | ||
|
|
e26065b116 | ||
|
|
485503c54d | ||
|
|
3c37716dd2 | ||
|
|
1a7f86a321 | ||
|
|
475f8c67b0 | ||
|
|
26314254be | ||
|
|
6522f6d964 | ||
|
|
ecaf439e66 | ||
|
|
90d03676ad | ||
|
|
f35d644608 | ||
|
|
2585a62697 | ||
|
|
6afe58a081 | ||
|
|
6f5bd1a13d | ||
|
|
85a9ab72af | ||
|
|
258ea43225 | ||
|
|
7a7b708029 | ||
|
|
0cf89861af | ||
|
|
eb74625835 | ||
|
|
cbaec4c655 | ||
|
|
04c21cb63d | ||
|
|
7d72002b6e | ||
|
|
279cc25da0 | ||
|
|
1152bb8440 | ||
|
|
88befb8136 | ||
|
|
42c6bc0337 | ||
|
|
6e753bb955 | ||
|
|
64df3e1fae | ||
|
|
5cdd1ad093 | ||
|
|
36e754b6bc | ||
|
|
6730dcf60d | ||
|
|
fb54809dc9 | ||
|
|
d43c622fdb | ||
|
|
7253d7a1d7 | ||
|
|
a6daa50417 | ||
|
|
b11019ce20 | ||
|
|
dda47f7b7d | ||
|
|
4da8ba7ecc | ||
|
|
2ff0b27747 | ||
|
|
c3f279fb0c | ||
|
|
06bcf1edca | ||
|
|
fc1f75cfac | ||
|
|
1cf12ab707 | ||
|
|
288ccc1049 | ||
|
|
f13c139f63 | ||
|
|
e72ad0586e | ||
|
|
c2044fe8b5 | ||
|
|
a7b3690a7c | ||
|
|
68afc348db | ||
|
|
5404124a78 | ||
|
|
d592871c0d | ||
|
|
6019e550fd | ||
|
|
765a49fb07 | ||
|
|
fe2fd93c20 | ||
|
|
19ba336e32 | ||
|
|
a50148fe85 | ||
|
|
c96156503f | ||
|
|
ed2ba2a5c5 | ||
|
|
f87fe3e544 | ||
|
|
43e73313e6 | ||
|
|
888aa1d08b | ||
|
|
028834da8b | ||
|
|
8f3b989b00 | ||
|
|
fe8761427d | ||
|
|
27a09de008 | ||
|
|
20ba8cc24e | ||
|
|
b8b52e7fc0 | ||
|
|
05a8c3be5f | ||
|
|
2f9bedbeeb | ||
|
|
01be6fc275 | ||
|
|
c20a4bb220 | ||
|
|
ed21b3cb65 | ||
|
|
f2a44c976d | ||
|
|
ca5189d8ad | ||
|
|
03f51c36ac | ||
|
|
607202b4e9 | ||
|
|
4db4f4fb2d | ||
|
|
1d7df6819e | ||
|
|
e4b6993692 | ||
|
|
b15d7a3412 | ||
|
|
b172c6801c | ||
|
|
dc1f4dcc38 | ||
|
|
a82e611358 | ||
|
|
c64951fd6f | ||
|
|
a35701fabf | ||
|
|
f54c1f77b4 | ||
|
|
377534512a | ||
|
|
09032349d0 | ||
|
|
4dd39aeed8 | ||
|
|
a1477e2ad3 | ||
|
|
f34cb4bf63 | ||
|
|
3dc9cd3941 |
@@ -11,6 +11,8 @@ branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
except:
|
||||
- gh-pages-src
|
||||
|
||||
before_install:
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash -s
|
||||
|
||||
@@ -11,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-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-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).
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#### Optional
|
||||
* [remote](#remote)
|
||||
* [bootstrap4](#bootstrap4)
|
||||
* [loading](#loading)
|
||||
* [caption](#caption)
|
||||
* [striped](#striped)
|
||||
@@ -17,8 +18,11 @@
|
||||
* [condensed](#condensed)
|
||||
* [id](#id)
|
||||
* [classes](#classes)
|
||||
* [wrapperClasses](#wrapperClasses)
|
||||
* [headerClasses](#headerClasses)
|
||||
* [cellEdit](#cellEdit)
|
||||
* [selectRow](#selectRow)
|
||||
* [expandRow](#expandRow)
|
||||
* [rowStyle](#rowStyle)
|
||||
* [rowClasses](#rowClasses)
|
||||
* [rowEvents](#rowEvents)
|
||||
@@ -64,6 +68,9 @@ remote={ { pagination: true, filter: false, sort: false } }
|
||||
|
||||
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>
|
||||
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.
|
||||
@@ -107,12 +114,22 @@ Same as bootstrap `.table-condensed` class for making a table more compact by cu
|
||||
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>
|
||||
Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail.
|
||||
|
||||
### <a name='selectRow'>selectRow - [Object]</a>
|
||||
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>
|
||||
Custom the style of table rows:
|
||||
|
||||
@@ -198,6 +215,7 @@ paginator({
|
||||
totalSize, // Total data size. It's necessary when remote is enabled
|
||||
pageStartIndex: 0, // first page will be 0, default is 1
|
||||
paginationSize: 3, // the pagination bar size, default is 5
|
||||
showTotal: true, // display pagination information
|
||||
sizePerPageList: [ {
|
||||
text: '5', value: 5
|
||||
}, {
|
||||
@@ -219,6 +237,7 @@ paginator({
|
||||
hidePageListOnlyOnePage: true, // hide pagination bar when only one page, default is false
|
||||
onPageChange: (page, sizePerPage) => {}, // callback function when page was changing
|
||||
onSizePerPageChange: (sizePerPage, page) => {}, // callback function when page size was changing
|
||||
paginationTotalRenderer: (from, to, size) => { ... } // custom the pagination total
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
108
docs/columns.md
108
docs/columns.md
@@ -34,8 +34,14 @@ Available properties in a column object:
|
||||
* [editCellClasses](#editCellClasses)
|
||||
* [editorStyle](#editorStyle)
|
||||
* [editorClasses](#editorClasses)
|
||||
* [editor](#editor)
|
||||
* [editorRenderer](#editorRenderer)
|
||||
* [filter](#filter)
|
||||
* [filterValue](#filterValue)
|
||||
* [csvType](#csvType)
|
||||
* [csvFormatter](#csvFormatter)
|
||||
* [csvText](#csvText)
|
||||
* [csvExport](#csvExport)
|
||||
|
||||
Following is a most simplest and basic usage:
|
||||
|
||||
@@ -89,6 +95,10 @@ dataField: 'address.city'
|
||||
* `rowIndex`
|
||||
* [`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>
|
||||
`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.
|
||||
|
||||
@@ -560,11 +570,94 @@ This is almost same as [`column.editCellStyle`](#editCellStyle), but `column.edi
|
||||
## <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>
|
||||
Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters:
|
||||
|
||||
* Text(`textFilter`)
|
||||
* Select(`selectFilter`)
|
||||
* Number(`numberFilter`)
|
||||
* Date(`dateFilter`)
|
||||
|
||||
We have a quick example to show you how to use `column.filter`:
|
||||
|
||||
@@ -600,4 +693,17 @@ A final `String` value you want to be filtered.
|
||||
filter: textFilter(),
|
||||
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.
|
||||
|
||||
@@ -22,6 +22,8 @@ Currently, **I still can't implement all the mainly features in legacy `react-bo
|
||||
* Pagination Addons
|
||||
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
|
||||
* Overlay/Loading Addons
|
||||
* [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit)
|
||||
* Table Toolkits, like search, csv etc.
|
||||
|
||||
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.
|
||||
|
||||
@@ -72,7 +74,7 @@ Due to no `TableHeaderColumn` so that no `dataSort` here, please add [`sort`](ht
|
||||
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).
|
||||
|
||||
No huge change for row selection, but can not custom the selection column currently. Coming soon!!!
|
||||
No huge change for row selection.
|
||||
|
||||
## Column Filter
|
||||
|
||||
@@ -82,14 +84,14 @@ Please see [available filter configuration](https://react-bootstrap-table.github
|
||||
- [x] Text Filter
|
||||
- [x] Custom Text Filter
|
||||
- [x] Remote Filter
|
||||
- [ ] Custom Filter Component
|
||||
- [x] Custom Filter Component
|
||||
- [ ] Regex Filter
|
||||
- [x] Select Filter
|
||||
- [x] Custom Select Filter
|
||||
- [X] Number Filter
|
||||
- [ ] Date Filter
|
||||
- [ ] Array Filter
|
||||
- [ ] Programmatically Filter
|
||||
- [X] Date Filter
|
||||
- [X] Array Filter
|
||||
- [X] Programmatically Filter
|
||||
|
||||
Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly.
|
||||
|
||||
@@ -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.
|
||||
|
||||
## Table Search
|
||||
The 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
|
||||
|
||||
> 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
129
docs/row-expand.md
Normal 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
|
||||
};
|
||||
```
|
||||
@@ -16,6 +16,8 @@
|
||||
* [onSelect](#onSelect)
|
||||
* [onSelectAll](#onSelectAll)
|
||||
* [hideSelectColumn](#hideSelectColumn)
|
||||
* [selectionRenderer](#selectionRenderer)
|
||||
* [selectionHeaderRenderer](#selectionHeaderRenderer)
|
||||
|
||||
### <a name="mode">selectRow.mode - [String]</a>
|
||||
|
||||
@@ -156,14 +158,42 @@ const selectRow = {
|
||||
};
|
||||
```
|
||||
|
||||
### <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` and `rowIndex`.
|
||||
### <a name='selectionRenderer'>selectRow.selectionRenderer - [Function]</a>
|
||||
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:
|
||||
|
||||
```js
|
||||
const selectRow = {
|
||||
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) => {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
@@ -175,7 +205,7 @@ This callback function will be called when select/unselect all and it only work
|
||||
```js
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
onSelectAll: (isSelect, results) => {
|
||||
onSelectAll: (isSelect, results, e) => {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,7 +17,8 @@ const JS_PKGS = [
|
||||
'react-bootstrap-table2-editor',
|
||||
'react-bootstrap-table2-filter',
|
||||
'react-bootstrap-table2-overlay',
|
||||
'react-bootstrap-table2-paginator'
|
||||
'react-bootstrap-table2-paginator',
|
||||
'react-bootstrap-table2-toolkit'
|
||||
].reduce((pkg, curr) => `${curr}|${pkg}`, '');
|
||||
|
||||
const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`;
|
||||
@@ -25,7 +26,8 @@ const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`;
|
||||
const STYLE_PKGS = [
|
||||
'react-bootstrap-table2',
|
||||
'react-bootstrap-table2-filter',
|
||||
'react-bootstrap-table2-paginator'
|
||||
'react-bootstrap-table2-paginator',
|
||||
'react-bootstrap-table2-toolkit',
|
||||
].reduce((pkg, curr) => `${curr}|${pkg}`, '');
|
||||
|
||||
const STYLE_SKIPS = `+(${NODE_MODULES})`;
|
||||
@@ -72,9 +74,16 @@ function styles() {
|
||||
.pipe(gulp.dest(PKG_PATH));
|
||||
}
|
||||
|
||||
function umd() {
|
||||
return gulp.src('./webpack.prod.config.babel.js')
|
||||
.pipe(shell(['webpack --config <%= file.path %>']));
|
||||
function umd(done) {
|
||||
gulp.parallel(
|
||||
() => 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);
|
||||
|
||||
18
package.json
18
package.json
@@ -11,10 +11,11 @@
|
||||
"pretest": "yarn lint --cache",
|
||||
"test": "jest",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:watch": "jest --watch",
|
||||
"test:watch": "jest --coverage --watch",
|
||||
"storybook": "cd ./packages/react-bootstrap-table2-example && yarn storybook",
|
||||
"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": {
|
||||
"type": "git",
|
||||
@@ -49,8 +50,9 @@
|
||||
"babel-preset-stage-0": "6.24.1",
|
||||
"babel-register": "6.24.1",
|
||||
"css-loader": "0.28.1",
|
||||
"enzyme": "3.1.1",
|
||||
"enzyme-adapter-react-16": "1.0.4",
|
||||
"enzyme": "3.3.0",
|
||||
"enzyme-adapter-react-16": "1.1.1",
|
||||
"enzyme-to-json": "3.3.4",
|
||||
"eslint": "4.5.0",
|
||||
"eslint-config-airbnb": "15.1.0",
|
||||
"eslint-loader": "1.9.0",
|
||||
@@ -80,12 +82,14 @@
|
||||
"dependencies": {
|
||||
"classnames": "2.2.5",
|
||||
"prop-types": "15.5.10",
|
||||
"react": "16.0.0",
|
||||
"react-dom": "16.0.0"
|
||||
"react": "16.3.2",
|
||||
"react-dom": "16.3.2",
|
||||
"underscore": "1.9.1"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"packages/**/*.js"
|
||||
"packages/*/src/**/*.js",
|
||||
"packages/*/index.js"
|
||||
],
|
||||
"roots": [
|
||||
"<rootDir>/packages"
|
||||
|
||||
@@ -48,6 +48,9 @@ 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)
|
||||
* 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)
|
||||
|
||||
## Validation
|
||||
|
||||
[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
|
||||
|
||||
@@ -58,6 +61,169 @@ How user save their new editings? We offer two ways:
|
||||
* 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)
|
||||
|
||||
## Validation
|
||||
## Rich Editors
|
||||
`react-bootstrap-table2` have following predefined editor:
|
||||
|
||||
[`column.validator`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnvalidator-function) will help you to work on it!
|
||||
* 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 } />
|
||||
)
|
||||
}];
|
||||
```
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import wrapperFactory from './src/wrapper';
|
||||
import createContext from './src/context';
|
||||
import editingCellFactory from './src/editing-cell';
|
||||
import {
|
||||
EDITTYPE,
|
||||
CLICK_TO_CELL_EDIT,
|
||||
DBCLICK_TO_CELL_EDIT,
|
||||
DELAY_FOR_DBCLICK
|
||||
} from './src/const';
|
||||
|
||||
export default (options = {}) => ({
|
||||
wrapperFactory,
|
||||
createContext,
|
||||
editingCellFactory,
|
||||
CLICK_TO_CELL_EDIT,
|
||||
DBCLICK_TO_CELL_EDIT,
|
||||
DELAY_FOR_DBCLICK,
|
||||
options
|
||||
});
|
||||
|
||||
export const Type = EDITTYPE;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-bootstrap-table2-editor",
|
||||
"version": "0.1.4",
|
||||
"version": "1.0.0",
|
||||
"description": "it's the editor addon for react-bootstrap-table2",
|
||||
"main": "./lib/index.js",
|
||||
"scripts": {
|
||||
@@ -41,7 +41,7 @@
|
||||
],
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.0.0",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0"
|
||||
"react": "^16.3.0",
|
||||
"react-dom": "^16.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
61
packages/react-bootstrap-table2-editor/src/checkbox-editor.js
vendored
Normal file
61
packages/react-bootstrap-table2-editor/src/checkbox-editor.js
vendored
Normal 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;
|
||||
@@ -2,3 +2,11 @@ export const TIME_TO_CLOSE_MESSAGE = 3000;
|
||||
export const DELAY_FOR_DBCLICK = 200;
|
||||
export const CLICK_TO_CELL_EDIT = 'click';
|
||||
export const DBCLICK_TO_CELL_EDIT = 'dbclick';
|
||||
|
||||
export const EDITTYPE = {
|
||||
TEXT: 'text',
|
||||
SELECT: 'select',
|
||||
TEXTAREA: 'textarea',
|
||||
CHECKBOX: 'checkbox',
|
||||
DATE: 'date'
|
||||
};
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
/* 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 { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
|
||||
|
||||
export default (
|
||||
Base,
|
||||
{ _, remoteResolver }
|
||||
_,
|
||||
dataOperator,
|
||||
isRemoteCellEdit,
|
||||
handleCellChange
|
||||
) => {
|
||||
let EditingCell;
|
||||
return class CellEditWrapper extends remoteResolver(Component) {
|
||||
const CellEditContext = React.createContext();
|
||||
|
||||
class CellEditProvider extends React.Component {
|
||||
static propTypes = {
|
||||
data: PropTypes.array.isRequired,
|
||||
selectRow: PropTypes.object,
|
||||
options: PropTypes.shape({
|
||||
mode: PropTypes.oneOf([CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT]).isRequired,
|
||||
onErrorMessageDisappear: PropTypes.func,
|
||||
@@ -19,7 +25,7 @@ export default (
|
||||
afterSaveCell: PropTypes.func,
|
||||
nonEditableRows: PropTypes.func,
|
||||
timeToCloseMessage: PropTypes.number,
|
||||
errorMessage: PropTypes.string
|
||||
errorMessage: PropTypes.any
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,41 +39,32 @@ export default (
|
||||
this.state = {
|
||||
ridx: null,
|
||||
cidx: null,
|
||||
message: null,
|
||||
isDataChanged: false
|
||||
message: null
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.cellEdit && this.isRemoteCellEdit()) {
|
||||
if (nextProps.cellEdit && isRemoteCellEdit()) {
|
||||
if (nextProps.cellEdit.options.errorMessage) {
|
||||
this.setState(() => ({
|
||||
isDataChanged: false,
|
||||
message: nextProps.cellEdit.options.errorMessage
|
||||
}));
|
||||
} else {
|
||||
this.setState(() => ({
|
||||
isDataChanged: true
|
||||
}));
|
||||
this.escapeEditing();
|
||||
}
|
||||
} else {
|
||||
this.setState(() => ({
|
||||
isDataChanged: false
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
handleCellUpdate(row, column, newValue) {
|
||||
const { keyField, cellEdit, store } = this.props;
|
||||
const { keyField, cellEdit, data } = this.props;
|
||||
const { beforeSaveCell, afterSaveCell } = cellEdit.options;
|
||||
const oldValue = _.get(row, column.dataField);
|
||||
const rowId = _.get(row, keyField);
|
||||
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
|
||||
if (this.isRemoteCellEdit()) {
|
||||
this.handleCellChange(rowId, column.dataField, newValue);
|
||||
if (isRemoteCellEdit()) {
|
||||
handleCellChange(rowId, column.dataField, newValue);
|
||||
} else {
|
||||
store.edit(rowId, column.dataField, newValue);
|
||||
dataOperator.editCell(data, keyField, rowId, column.dataField, newValue);
|
||||
if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column);
|
||||
this.completeEditing();
|
||||
}
|
||||
@@ -77,8 +74,7 @@ export default (
|
||||
this.setState(() => ({
|
||||
ridx: null,
|
||||
cidx: null,
|
||||
message: null,
|
||||
isDataChanged: true
|
||||
message: null
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -86,8 +82,7 @@ export default (
|
||||
const editing = () => {
|
||||
this.setState(() => ({
|
||||
ridx,
|
||||
cidx,
|
||||
isDataChanged: false
|
||||
cidx
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -103,18 +98,19 @@ export default (
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isDataChanged, ...stateRest } = this.state;
|
||||
const {
|
||||
cellEdit: {
|
||||
options: { nonEditableRows, errorMessage, ...optionsRest },
|
||||
editingCellFactory,
|
||||
createContext,
|
||||
...cellEditRest
|
||||
}
|
||||
} = this.props;
|
||||
|
||||
const newCellEdit = {
|
||||
...optionsRest,
|
||||
...cellEditRest,
|
||||
...stateRest,
|
||||
...this.state,
|
||||
EditingCell,
|
||||
nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [],
|
||||
onStart: this.startEditing,
|
||||
@@ -123,13 +119,16 @@ export default (
|
||||
};
|
||||
|
||||
return (
|
||||
<Base
|
||||
{ ...this.props }
|
||||
data={ this.props.store.data }
|
||||
isDataChanged={ isDataChanged }
|
||||
cellEdit={ newCellEdit }
|
||||
/>
|
||||
<CellEditContext.Provider
|
||||
value={ { cellEdit: newCellEdit } }
|
||||
>
|
||||
{ this.props.children }
|
||||
</CellEditContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
Provider: CellEditProvider,
|
||||
Consumer: CellEditContext.Consumer
|
||||
};
|
||||
};
|
||||
42
packages/react-bootstrap-table2-editor/src/date-editor.js
vendored
Normal file
42
packages/react-bootstrap-table2-editor/src/date-editor.js
vendored
Normal 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;
|
||||
61
packages/react-bootstrap-table2-editor/src/dropdown-editor.js
vendored
Normal file
61
packages/react-bootstrap-table2-editor/src/dropdown-editor.js
vendored
Normal 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;
|
||||
@@ -6,9 +6,13 @@ import React, { Component } from 'react';
|
||||
import cs from 'classnames';
|
||||
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 EditorIndicator from './editor-indicator';
|
||||
import { TIME_TO_CLOSE_MESSAGE } from './const';
|
||||
import { TIME_TO_CLOSE_MESSAGE, EDITTYPE } from './const';
|
||||
|
||||
export default _ =>
|
||||
class EditingCell extends Component {
|
||||
@@ -73,8 +77,8 @@ export default _ =>
|
||||
}, timeToCloseMessage);
|
||||
}
|
||||
|
||||
beforeComplete(row, column, newValue) {
|
||||
const { onUpdate } = this.props;
|
||||
beforeComplete(newValue) {
|
||||
const { onUpdate, row, column } = this.props;
|
||||
if (_.isFunction(column.validator)) {
|
||||
const validateForm = column.validator(newValue, row, column);
|
||||
if (_.isObject(validateForm) && !validateForm.valid) {
|
||||
@@ -89,28 +93,20 @@ export default _ =>
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
const { onEscape, blurToSave, row, column } = this.props;
|
||||
const { onEscape, blurToSave } = this.props;
|
||||
if (blurToSave) {
|
||||
const value = this.editor.text.value;
|
||||
if (!_.isDefined(value)) {
|
||||
// TODO: for other custom or embed editor
|
||||
}
|
||||
this.beforeComplete(row, column, value);
|
||||
this.beforeComplete(this.editor.getValue());
|
||||
} else {
|
||||
onEscape();
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
const { onEscape, row, column } = this.props;
|
||||
const { onEscape } = this.props;
|
||||
if (e.keyCode === 27) { // ESC
|
||||
onEscape();
|
||||
} else if (e.keyCode === 13) { // ENTER
|
||||
const value = e.currentTarget.value;
|
||||
if (!_.isDefined(value)) {
|
||||
// TODO: for other custom or embed editor
|
||||
}
|
||||
this.beforeComplete(row, column, value);
|
||||
this.beforeComplete(this.editor.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,17 +120,13 @@ export default _ =>
|
||||
}
|
||||
|
||||
render() {
|
||||
const { invalidMessage } = this.state;
|
||||
let editor;
|
||||
const { row, column, className, style, rowIndex, columnIndex } = this.props;
|
||||
const { dataField } = column;
|
||||
|
||||
const value = _.get(row, dataField);
|
||||
const editorAttrs = {
|
||||
onKeyDown: this.handleKeyDown,
|
||||
onBlur: this.handleBlur
|
||||
};
|
||||
const hasError = _.isDefined(this.state.invalidMessage);
|
||||
|
||||
const hasError = _.isDefined(invalidMessage);
|
||||
let customEditorClass = column.editorClasses || '';
|
||||
if (_.isFunction(column.editorClasses)) {
|
||||
customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex);
|
||||
@@ -150,20 +142,51 @@ export default _ =>
|
||||
shake: hasError
|
||||
}, customEditorClass);
|
||||
|
||||
let editorProps = {
|
||||
ref: node => this.editor = node,
|
||||
defaultValue: value,
|
||||
style: editorStyle,
|
||||
className: editorClass,
|
||||
onKeyDown: this.handleKeyDown,
|
||||
onBlur: this.handleBlur
|
||||
};
|
||||
|
||||
const isDefaultEditorDefined = _.isObject(column.editor);
|
||||
|
||||
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 (
|
||||
<td
|
||||
className={ cs('react-bootstrap-table-editing-cell', className) }
|
||||
style={ style }
|
||||
onClick={ this.handleClick }
|
||||
>
|
||||
<TextEditor
|
||||
ref={ node => this.editor = node }
|
||||
defaultValue={ value }
|
||||
style={ editorStyle }
|
||||
className={ editorClass }
|
||||
{ ...editorAttrs }
|
||||
/>
|
||||
{ hasError ? <EditorIndicator invalidMessage={ invalidMessage } /> : null }
|
||||
{ editor }
|
||||
{ hasError ? <EditorIndicator invalidMessage={ this.state.invalidMessage } /> : null }
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ class TextEditor extends Component {
|
||||
this.text.focus();
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.text.value;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { defaultValue, className, ...rest } = this.props;
|
||||
const editorClass = cs('form-control editor edit-text', className);
|
||||
|
||||
60
packages/react-bootstrap-table2-editor/src/textarea-editor.js
vendored
Normal file
60
packages/react-bootstrap-table2-editor/src/textarea-editor.js
vendored
Normal 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;
|
||||
430
packages/react-bootstrap-table2-editor/test/context.test.js
Normal file
430
packages/react-bootstrap-table2-editor/test/context.test.js
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,12 @@ import { shallow, mount } from 'enzyme';
|
||||
|
||||
import _ from 'react-bootstrap-table-next/src/utils';
|
||||
import editingCellFactory from '../src/editing-cell';
|
||||
import * as constants from '../src/const';
|
||||
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';
|
||||
|
||||
const EditingCell = editingCellFactory(_);
|
||||
@@ -39,7 +44,7 @@ describe('EditingCell', () => {
|
||||
beforeEach(() => {
|
||||
onEscape = sinon.stub();
|
||||
onUpdate = sinon.stub();
|
||||
wrapper = shallow(
|
||||
wrapper = mount(
|
||||
<EditingCell
|
||||
row={ row }
|
||||
rowIndex={ rowIndex }
|
||||
@@ -74,7 +79,8 @@ describe('EditingCell', () => {
|
||||
it('when press ENTER on TextEditor should call onUpdate correctly', () => {
|
||||
const newValue = 'test';
|
||||
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.calledWith(row, column, newValue)).toBe(true);
|
||||
});
|
||||
@@ -311,7 +317,7 @@ describe('EditingCell', () => {
|
||||
onEscape={ onEscape }
|
||||
/>
|
||||
);
|
||||
wrapper.instance().beforeComplete(row, column, newValue);
|
||||
wrapper.instance().beforeComplete(newValue);
|
||||
});
|
||||
|
||||
it('should call column.validator successfully', () => {
|
||||
@@ -357,7 +363,17 @@ describe('EditingCell', () => {
|
||||
text: 'ID',
|
||||
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', () => {
|
||||
@@ -370,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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,2 +1,2 @@
|
||||
<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"> -->
|
||||
@@ -8,6 +8,7 @@ const editorSourcePath = path.join(__dirname, '../../react-bootstrap-table2-edit
|
||||
const sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style');
|
||||
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
|
||||
const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style');
|
||||
const toolkitSourcePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/index.js');
|
||||
const storyPath = path.join(__dirname, '../stories');
|
||||
const examplesPath = path.join(__dirname, '../examples');
|
||||
const srcPath = path.join(__dirname, '../src');
|
||||
@@ -23,6 +24,7 @@ const aliasPath = {
|
||||
'react-bootstrap-table2-filter': filterSourcePath,
|
||||
'react-bootstrap-table2-overlay': overlaySourcePath,
|
||||
'react-bootstrap-table2-paginator': paginationSourcePath,
|
||||
'react-bootstrap-table2-toolkit': toolkitSourcePath
|
||||
};
|
||||
|
||||
const loaders = [{
|
||||
|
||||
@@ -33,6 +33,7 @@ const columns = [{
|
||||
|
||||
<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 () => (
|
||||
@@ -43,6 +44,9 @@ export default () => (
|
||||
<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>
|
||||
);
|
||||
|
||||
32
packages/react-bootstrap-table2-example/examples/basic/large-table.js
vendored
Normal file
32
packages/react-bootstrap-table2-example/examples/basic/large-table.js
vendored
Normal 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>
|
||||
);
|
||||
64
packages/react-bootstrap-table2-example/examples/cell-edit/checkbox-editor-table.js
vendored
Normal file
64
packages/react-bootstrap-table2-example/examples/cell-edit/checkbox-editor-table.js
vendored
Normal 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>
|
||||
);
|
||||
130
packages/react-bootstrap-table2-example/examples/cell-edit/custom-editor-table.js
vendored
Normal file
130
packages/react-bootstrap-table2-example/examples/cell-edit/custom-editor-table.js
vendored
Normal 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>
|
||||
);
|
||||
77
packages/react-bootstrap-table2-example/examples/cell-edit/date-editor-table.js
vendored
Normal file
77
packages/react-bootstrap-table2-example/examples/cell-edit/date-editor-table.js
vendored
Normal 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>
|
||||
);
|
||||
100
packages/react-bootstrap-table2-example/examples/cell-edit/dropdown-editor-table.js
vendored
Normal file
100
packages/react-bootstrap-table2-example/examples/cell-edit/dropdown-editor-table.js
vendored
Normal 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>
|
||||
);
|
||||
68
packages/react-bootstrap-table2-example/examples/cell-edit/textarea-editor-table.js
vendored
Normal file
68
packages/react-bootstrap-table2-example/examples/cell-edit/textarea-editor-table.js
vendored
Normal 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>
|
||||
);
|
||||
184
packages/react-bootstrap-table2-example/examples/column-filter/advance-custom-filter.js
vendored
Normal file
184
packages/react-bootstrap-table2-example/examples/column-filter/advance-custom-filter.js
vendored
Normal 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 }>></option>
|
||||
<option value={ Comparator.EQ }>=</option>
|
||||
<option value={ Comparator.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 }>></option>
|
||||
<option value={ Comparator.EQ }>=</option>
|
||||
<option value={ Comparator.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>
|
||||
);
|
||||
86
packages/react-bootstrap-table2-example/examples/column-filter/clear-all-filters.js
vendored
Normal file
86
packages/react-bootstrap-table2-example/examples/column-filter/clear-all-filters.js
vendored
Normal 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>
|
||||
);
|
||||
78
packages/react-bootstrap-table2-example/examples/column-filter/custom-date-filter.js
vendored
Normal file
78
packages/react-bootstrap-table2-example/examples/column-filter/custom-date-filter.js
vendored
Normal 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>
|
||||
);
|
||||
@@ -3,9 +3,9 @@ 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 { jobsGenerator } from 'utils/common';
|
||||
import { jobsGenerator1 } from 'utils/common';
|
||||
|
||||
const jobs = jobsGenerator(5);
|
||||
const jobs = jobsGenerator1(5);
|
||||
|
||||
const owners = ['Allen', 'Bob', 'Cat'];
|
||||
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
|
||||
|
||||
128
packages/react-bootstrap-table2-example/examples/column-filter/custom-filter.js
vendored
Normal file
128
packages/react-bootstrap-table2-example/examples/column-filter/custom-filter.js
vendored
Normal 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>
|
||||
);
|
||||
80
packages/react-bootstrap-table2-example/examples/column-filter/custom-multi-select-filter.js
vendored
Normal file
80
packages/react-bootstrap-table2-example/examples/column-filter/custom-multi-select-filter.js
vendored
Normal 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>
|
||||
);
|
||||
60
packages/react-bootstrap-table2-example/examples/column-filter/date-filter-default-value.js
vendored
Normal file
60
packages/react-bootstrap-table2-example/examples/column-filter/date-filter-default-value.js
vendored
Normal 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>
|
||||
);
|
||||
56
packages/react-bootstrap-table2-example/examples/column-filter/date-filter.js
vendored
Normal file
56
packages/react-bootstrap-table2-example/examples/column-filter/date-filter.js
vendored
Normal 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>
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
67
packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter.js
vendored
Normal file
67
packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter.js
vendored
Normal 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>
|
||||
);
|
||||
86
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-date-filter.js
vendored
Normal file
86
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-date-filter.js
vendored
Normal 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>
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
85
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-number-filter.js
vendored
Normal file
85
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-number-filter.js
vendored
Normal 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>
|
||||
);
|
||||
96
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-select-filter.js
vendored
Normal file
96
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-select-filter.js
vendored
Normal 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>
|
||||
);
|
||||
81
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-text-filter.js
vendored
Normal file
81
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-text-filter.js
vendored
Normal 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>
|
||||
);
|
||||
@@ -41,7 +41,7 @@ const columns = [{
|
||||
|
||||
export default () => (
|
||||
<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 } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
|
||||
80
packages/react-bootstrap-table2-example/examples/csv/csv-column-formatter.js
vendored
Normal file
80
packages/react-bootstrap-table2-example/examples/csv/csv-column-formatter.js
vendored
Normal 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>
|
||||
);
|
||||
79
packages/react-bootstrap-table2-example/examples/csv/csv-column-type.js
vendored
Normal file
79
packages/react-bootstrap-table2-example/examples/csv/csv-column-type.js
vendored
Normal 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>
|
||||
);
|
||||
97
packages/react-bootstrap-table2-example/examples/csv/custom-csv-button.js
vendored
Normal file
97
packages/react-bootstrap-table2-example/examples/csv/custom-csv-button.js
vendored
Normal 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>
|
||||
);
|
||||
83
packages/react-bootstrap-table2-example/examples/csv/custom-csv-header.js
vendored
Normal file
83
packages/react-bootstrap-table2-example/examples/csv/custom-csv-header.js
vendored
Normal 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>
|
||||
);
|
||||
82
packages/react-bootstrap-table2-example/examples/csv/custom-csv.js
vendored
Normal file
82
packages/react-bootstrap-table2-example/examples/csv/custom-csv.js
vendored
Normal 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>
|
||||
);
|
||||
79
packages/react-bootstrap-table2-example/examples/csv/hide-column.js
vendored
Normal file
79
packages/react-bootstrap-table2-example/examples/csv/hide-column.js
vendored
Normal 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>
|
||||
);
|
||||
77
packages/react-bootstrap-table2-example/examples/csv/index.js
vendored
Normal file
77
packages/react-bootstrap-table2-example/examples/csv/index.js
vendored
Normal 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>
|
||||
);
|
||||
52
packages/react-bootstrap-table2-example/examples/header-columns/header-class-table.js
vendored
Normal file
52
packages/react-bootstrap-table2-example/examples/header-columns/header-class-table.js
vendored
Normal 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>
|
||||
);
|
||||
@@ -22,8 +22,19 @@ const columns = [{
|
||||
const sourceCode = `\
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
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>
|
||||
<BootstrapTable
|
||||
remote
|
||||
@@ -32,12 +43,13 @@ const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize })
|
||||
columns={ columns }
|
||||
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
|
||||
onTableChange={ onTableChange }
|
||||
noDataIndication={ () => <NoDataIndication /> }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
|
||||
class Container extends React.Component {
|
||||
class EmptyTableOverlay extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -47,7 +59,7 @@ class Container extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
handleTableChange = ({ page, sizePerPage }) => {
|
||||
handleTableChange = (type, { page, sizePerPage }) => {
|
||||
const currentIndex = (page - 1) * sizePerPage;
|
||||
setTimeout(() => {
|
||||
this.setState(() => ({
|
||||
@@ -55,13 +67,14 @@ class Container extends React.Component {
|
||||
data: products.slice(currentIndex, currentIndex + sizePerPage),
|
||||
sizePerPage
|
||||
}));
|
||||
}, 2000);
|
||||
}, 3000);
|
||||
this.setState(() => ({ data: [] }));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { data, sizePerPage, page } = this.state;
|
||||
return (
|
||||
<RemotePagination
|
||||
<Table
|
||||
data={ data }
|
||||
page={ page }
|
||||
sizePerPage={ sizePerPage }
|
||||
|
||||
@@ -24,6 +24,12 @@ import BootstrapTable from 'react-bootstrap-table-next';
|
||||
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 = {
|
||||
paginationSize: 4,
|
||||
pageStartIndex: 0,
|
||||
@@ -39,6 +45,8 @@ const options = {
|
||||
prePageTitle: 'Pre page',
|
||||
firstPageTitle: 'Next page',
|
||||
lastPageTitle: 'Last page',
|
||||
showTotal: true,
|
||||
paginationTotalRenderer: customTotal,
|
||||
sizePerPageList: [{
|
||||
text: '5', value: 5
|
||||
}, {
|
||||
@@ -50,11 +58,18 @@ const 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 = {
|
||||
paginationSize: 4,
|
||||
pageStartIndex: 0,
|
||||
// alwaysShowAllBtns: true // Always show next and previous button
|
||||
// withFirstAndLast: false // Hide the going to First and Last page button
|
||||
// alwaysShowAllBtns: true, // Always show next and previous button
|
||||
// withFirstAndLast: false, // Hide the going to First and Last page button
|
||||
// hideSizePerPage: true, // Hide the sizePerPage dropdown always
|
||||
// hidePageListOnlyOnePage: true, // Hide the pagination list when only one page
|
||||
firstPageText: 'First',
|
||||
@@ -65,6 +80,8 @@ const options = {
|
||||
prePageTitle: 'Pre page',
|
||||
firstPageTitle: 'Next page',
|
||||
lastPageTitle: 'Last page',
|
||||
showTotal: true,
|
||||
paginationTotalRenderer: customTotal,
|
||||
sizePerPageList: [{
|
||||
text: '5', value: 5
|
||||
}, {
|
||||
|
||||
@@ -4,53 +4,76 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import paginationFactory from 'react-bootstrap-table2-paginator';
|
||||
import cellEditFactory from 'react-bootstrap-table2-editor';
|
||||
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
|
||||
import Code from 'components/common/code-block';
|
||||
import { productsGenerator } from 'utils/common';
|
||||
|
||||
const products = productsGenerator(87);
|
||||
let products = productsGenerator(87);
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
text: 'Product ID',
|
||||
sort: true
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
filter: textFilter()
|
||||
filter: textFilter({
|
||||
defaultValue: '8'
|
||||
}),
|
||||
sort: true
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price',
|
||||
filter: textFilter()
|
||||
filter: textFilter(),
|
||||
sort: true
|
||||
}];
|
||||
|
||||
const sourceCode = `\
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import paginationFactory from 'react-bootstrap-table2-paginator';
|
||||
import cellEditFactory from 'react-bootstrap-table2-editor';
|
||||
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
|
||||
// ...
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
text: 'Product ID',
|
||||
sort: true
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name',
|
||||
filter: textFilter()
|
||||
filter: textFilter({
|
||||
defaultValue: '8'
|
||||
}),
|
||||
sort: true
|
||||
}, {
|
||||
dataField: '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 }) => (
|
||||
<div>
|
||||
<BootstrapTable
|
||||
remote={ { pagination: true } }
|
||||
remote
|
||||
keyField="id"
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
defaultSorted={ defaultSorted }
|
||||
filter={ filterFactory() }
|
||||
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
|
||||
cellEdit={ cellEditFactory(cellEditProps) }
|
||||
onTableChange={ onTableChange }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
@@ -77,10 +100,25 @@ class Container extends React.Component {
|
||||
this.handleTableChange = this.handleTableChange.bind(this);
|
||||
}
|
||||
|
||||
handleTableChange = (type, { page, sizePerPage, filters }) => {
|
||||
handleTableChange = (type, { page, sizePerPage, filters, sortField, sortOrder, cellEdit }) => {
|
||||
const currentIndex = (page - 1) * sizePerPage;
|
||||
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;
|
||||
for (const dataField in filters) {
|
||||
const { filterVal, filterType, comparator } = filters[dataField];
|
||||
@@ -96,6 +134,26 @@ class Container extends React.Component {
|
||||
}
|
||||
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(() => ({
|
||||
page,
|
||||
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 }) => (
|
||||
<div>
|
||||
<h3>When <code>remote.pagination</code> is enabled, the filtering,
|
||||
sorting and searching will also change to remote mode automatically</h3>
|
||||
<BootstrapTable
|
||||
remote={ { pagination: true } }
|
||||
remote
|
||||
keyField="id"
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
defaultSorted={ defaultSorted }
|
||||
filter={ filterFactory() }
|
||||
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
|
||||
onTableChange={ onTableChange }
|
||||
cellEdit={ cellEditFactory(cellEditProps) }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
@@ -157,10 +226,24 @@ class Container extends React.Component {
|
||||
this.handleTableChange = this.handleTableChange.bind(this);
|
||||
}
|
||||
|
||||
handleTableChange = (type, { page, sizePerPage, filters }) => {
|
||||
handleTableChange = (type, { page, sizePerPage, filters, sortField, sortOrder, cellEdit }) => {
|
||||
const currentIndex = (page - 1) * sizePerPage;
|
||||
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;
|
||||
for (const dataField in filters) {
|
||||
const { filterVal, filterType, comparator } = filters[dataField];
|
||||
@@ -176,6 +259,26 @@ class Container extends React.Component {
|
||||
}
|
||||
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(() => ({
|
||||
page,
|
||||
data: result.slice(currentIndex, currentIndex + sizePerPage),
|
||||
|
||||
175
packages/react-bootstrap-table2-example/examples/remote/remote-search.js
vendored
Normal file
175
packages/react-bootstrap-table2-example/examples/remote/remote-search.js
vendored
Normal 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;
|
||||
107
packages/react-bootstrap-table2-example/examples/row-expand/custom-expand-column.js
vendored
Normal file
107
packages/react-bootstrap-table2-example/examples/row-expand/custom-expand-column.js
vendored
Normal 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>
|
||||
);
|
||||
74
packages/react-bootstrap-table2-example/examples/row-expand/expand-column.js
vendored
Normal file
74
packages/react-bootstrap-table2-example/examples/row-expand/expand-column.js
vendored
Normal 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>
|
||||
);
|
||||
97
packages/react-bootstrap-table2-example/examples/row-expand/expand-hooks.js
vendored
Normal file
97
packages/react-bootstrap-table2-example/examples/row-expand/expand-hooks.js
vendored
Normal 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>
|
||||
);
|
||||
138
packages/react-bootstrap-table2-example/examples/row-expand/expand-management.js
vendored
Normal file
138
packages/react-bootstrap-table2-example/examples/row-expand/expand-management.js
vendored
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
72
packages/react-bootstrap-table2-example/examples/row-expand/index.js
vendored
Normal file
72
packages/react-bootstrap-table2-example/examples/row-expand/index.js
vendored
Normal 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>
|
||||
);
|
||||
75
packages/react-bootstrap-table2-example/examples/row-expand/non-expandable-rows.js
vendored
Normal file
75
packages/react-bootstrap-table2-example/examples/row-expand/non-expandable-rows.js
vendored
Normal 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>
|
||||
);
|
||||
107
packages/react-bootstrap-table2-example/examples/row-selection/custom-selection.js
vendored
Normal file
107
packages/react-bootstrap-table2-example/examples/row-selection/custom-selection.js
vendored
Normal 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>
|
||||
);
|
||||
@@ -22,14 +22,16 @@ const columns = [{
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
clickToSelect: true,
|
||||
onSelect: (row, isSelect, rowIndex) => {
|
||||
onSelect: (row, isSelect, rowIndex, e) => {
|
||||
console.log(row.id);
|
||||
console.log(isSelect);
|
||||
console.log(rowIndex);
|
||||
console.log(e);
|
||||
},
|
||||
onSelectAll: (isSelect, rows) => {
|
||||
onSelectAll: (isSelect, rows, e) => {
|
||||
console.log(isSelect);
|
||||
console.log(rows);
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -49,7 +51,18 @@ const columns = [{
|
||||
|
||||
const selectRow = {
|
||||
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
|
||||
|
||||
102
packages/react-bootstrap-table2-example/examples/search/custom-search-value.js
vendored
Normal file
102
packages/react-bootstrap-table2-example/examples/search/custom-search-value.js
vendored
Normal 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>
|
||||
);
|
||||
95
packages/react-bootstrap-table2-example/examples/search/default-custom-search.js
vendored
Normal file
95
packages/react-bootstrap-table2-example/examples/search/default-custom-search.js
vendored
Normal 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>
|
||||
);
|
||||
116
packages/react-bootstrap-table2-example/examples/search/fully-custom-search.js
vendored
Normal file
116
packages/react-bootstrap-table2-example/examples/search/fully-custom-search.js
vendored
Normal 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>
|
||||
);
|
||||
83
packages/react-bootstrap-table2-example/examples/search/index.js
vendored
Normal file
83
packages/react-bootstrap-table2-example/examples/search/index.js
vendored
Normal 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>
|
||||
);
|
||||
85
packages/react-bootstrap-table2-example/examples/search/search-formatted.js
vendored
Normal file
85
packages/react-bootstrap-table2-example/examples/search/search-formatted.js
vendored
Normal 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>
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-bootstrap-table2-example",
|
||||
"version": "0.1.5",
|
||||
"version": "1.0.2",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
@@ -14,8 +14,8 @@
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.0.0",
|
||||
"react": "^15.0.0",
|
||||
"react-dom": "^15.0.0"
|
||||
"react": "^16.3.0",
|
||||
"react-dom": "^116.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.3.7"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint no-mixed-operators: 0 */
|
||||
|
||||
/**
|
||||
* products generator for stories
|
||||
*
|
||||
@@ -27,12 +29,56 @@ export const productsQualityGenerator = (quantity = 5) =>
|
||||
quality: index % 3
|
||||
}));
|
||||
|
||||
const jobType = ['A', 'B', 'C', 'D', 'E'];
|
||||
|
||||
const jobOwner = ['Allen', 'Bob', 'Cindy'];
|
||||
|
||||
export const jobsGenerator = (quantity = 5) =>
|
||||
Array.from({ length: quantity }, (value, index) => ({
|
||||
id: index,
|
||||
name: `Job name ${index}`,
|
||||
owner: Math.floor(Math.random() * 3),
|
||||
type: Math.floor(Math.random() * 5)
|
||||
owner: jobOwner[Math.floor((Math.random() * 2) + 1)],
|
||||
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 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)
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,6 +11,7 @@ import StripHoverCondensedTable from 'examples/basic/striped-hover-condensed-tab
|
||||
import NoDataTable from 'examples/basic/no-data-table';
|
||||
import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
|
||||
import CaptionTable from 'examples/basic/caption-table';
|
||||
import LargeTable from 'examples/basic/large-table';
|
||||
|
||||
// work on columns
|
||||
import NestedDataTable from 'examples/columns/nested-data-table';
|
||||
@@ -33,6 +34,7 @@ import HeaderColumnEventTable from 'examples/header-columns/column-event-table';
|
||||
import HeaderColumnClassTable from 'examples/header-columns/column-class-table';
|
||||
import HeaderColumnStyleTable from 'examples/header-columns/column-style-table';
|
||||
import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
|
||||
import HeaderClassTable from 'examples/header-columns/header-class-table';
|
||||
|
||||
// column filter
|
||||
import TextFilter from 'examples/column-filter/text-filter';
|
||||
@@ -45,9 +47,23 @@ import SelectFilter from 'examples/column-filter/select-filter';
|
||||
import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value';
|
||||
import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator';
|
||||
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 NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value';
|
||||
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
|
||||
import RowStyleTable from 'examples/rows/row-style';
|
||||
@@ -76,6 +92,11 @@ import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-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
|
||||
import SingleSelectionTable from 'examples/row-selection/single-selection';
|
||||
@@ -87,16 +108,41 @@ import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-sele
|
||||
import SelectionNoDataTable from 'examples/row-selection/selection-no-data';
|
||||
import SelectionStyleTable from 'examples/row-selection/selection-style';
|
||||
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 SelectionBgColorTable from 'examples/row-selection/selection-bgcolor';
|
||||
import SelectionHooks from 'examples/row-selection/selection-hooks';
|
||||
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
|
||||
import PaginationTable from 'examples/pagination';
|
||||
import PaginationHooksTable from 'examples/pagination/pagination-hooks';
|
||||
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
|
||||
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
|
||||
import TableOverlay from 'examples/loading-overlay/table-overlay';
|
||||
@@ -105,6 +151,7 @@ import TableOverlay from 'examples/loading-overlay/table-overlay';
|
||||
import RemoteSort from 'examples/remote/remote-sort';
|
||||
import RemoteFilter from 'examples/remote/remote-filter';
|
||||
import RemotePaginationTable from 'examples/remote/remote-pagination';
|
||||
import RemoteSearch from 'examples/remote/remote-search';
|
||||
import RemoteCellEdit from 'examples/remote/remote-celledit';
|
||||
import RemoteAll from 'examples/remote/remote-all';
|
||||
|
||||
@@ -128,7 +175,8 @@ storiesOf('Basic Table', module)
|
||||
.add('borderless table', () => <BorderlessTable />)
|
||||
.add('Indication For Empty Table', () => <NoDataTable />)
|
||||
.add('Customized id and class table', () => <CustomizedIdClassesTable />)
|
||||
.add('Table with caption', () => <CaptionTable />);
|
||||
.add('Table with caption', () => <CaptionTable />)
|
||||
.add('Large Table', () => <LargeTable />);
|
||||
|
||||
storiesOf('Work on Columns', module)
|
||||
.add('Display Nested Data', () => <NestedDataTable />)
|
||||
@@ -150,7 +198,8 @@ storiesOf('Work on Header Columns', module)
|
||||
.add('Column Event', () => <HeaderColumnEventTable />)
|
||||
.add('Customize Column Class', () => <HeaderColumnClassTable />)
|
||||
.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)
|
||||
.add('Text Filter', () => <TextFilter />)
|
||||
@@ -161,12 +210,26 @@ storiesOf('Column Filter', module)
|
||||
.add('Select Filter', () => <SelectFilter />)
|
||||
.add('Select Filter with Default Value', () => <SelectFilterWithDefaultValue />)
|
||||
.add('Select Filter with Comparator', () => <SelectFilterComparator />)
|
||||
.add('MultiSelect Filter', () => <MultiSelectFilter />)
|
||||
.add('MultiSelect Filter with Default Value', () => <MultiSelectFilterDefaultValue />)
|
||||
.add('Number Filter', () => <NumberFilter />)
|
||||
.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 Select Filter', () => <CustomSelectFilter />)
|
||||
.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)
|
||||
.add('Customize Row Style', () => <RowStyleTable />)
|
||||
@@ -194,7 +257,12 @@ storiesOf('Cell Editing', module)
|
||||
.add('Custom Cell Style', () => <CellEditStyleTable />)
|
||||
.add('Custom Cell Classes', () => <CellEditClassTable />)
|
||||
.add('Custom Editor Classes', () => <EditorClassTable />)
|
||||
.add('Custom Editor Style', () => <EditorStyleTable />);
|
||||
.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)
|
||||
.add('Single Selection', () => <SingleSelectionTable />)
|
||||
@@ -206,16 +274,41 @@ storiesOf('Row Selection', module)
|
||||
.add('Selection without Data', () => <SelectionNoDataTable />)
|
||||
.add('Selection Style', () => <SelectionStyleTable />)
|
||||
.add('Selection Class', () => <SelectionClassTable />)
|
||||
.add('Custom Selection', () => <CustomSelectionTable />)
|
||||
.add('Selection Background Color', () => <SelectionBgColorTable />)
|
||||
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />)
|
||||
.add('Selection Hooks', () => <SelectionHooks />)
|
||||
.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)
|
||||
.add('Basic Pagination Table', () => <PaginationTable />)
|
||||
.add('Pagination Hooks', () => <PaginationHooksTable />)
|
||||
.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)
|
||||
.add('Empty Table Overlay', () => <EmptyTableOverlay />)
|
||||
.add('Table Overlay', () => <TableOverlay />);
|
||||
@@ -224,5 +317,6 @@ storiesOf('Remote', module)
|
||||
.add('Remote Sort', () => <RemoteSort />)
|
||||
.add('Remote Filter', () => <RemoteFilter />)
|
||||
.add('Remote Pagination', () => <RemotePaginationTable />)
|
||||
.add('Remote Search', () => <RemoteSearch />)
|
||||
.add('Remote Cell Editing', () => <RemoteCellEdit />)
|
||||
.add('Remote All', () => <RemoteAll />);
|
||||
|
||||
@@ -5,3 +5,7 @@ table.foo {
|
||||
table#bar {
|
||||
background-color: $light-blue;
|
||||
}
|
||||
|
||||
div.boo {
|
||||
border: 2px solid salmon;
|
||||
}
|
||||
@@ -9,4 +9,8 @@
|
||||
|
||||
.demo-row-odd {
|
||||
background-color: $green-lighten-4;
|
||||
}
|
||||
|
||||
.header-class {
|
||||
background-color: $green-lighten-4;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.custome-search-field {
|
||||
background-color: #c8e6c9;
|
||||
}
|
||||
@@ -10,4 +10,5 @@
|
||||
@import "row-selection/index";
|
||||
@import "rows/index";
|
||||
@import "sort/index";
|
||||
@import "search/index";
|
||||
@import "loading-overlay/index";
|
||||
|
||||
@@ -18,7 +18,10 @@ You can get all types of filters via import and these filters are a factory func
|
||||
|
||||
* TextFilter
|
||||
* SelectFilter
|
||||
* MultiSelectFilter
|
||||
* NumberFilter
|
||||
* DateFilter
|
||||
* CustomFilter
|
||||
* **Coming soon!**
|
||||
|
||||
## Add CSS
|
||||
@@ -112,6 +115,52 @@ const qualityFilter = selectFilter({
|
||||
// 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
|
||||
|
||||
```js
|
||||
@@ -129,7 +178,7 @@ const columns = [..., {
|
||||
Numner filter is same as other filter, you can custom the number filter via `numberFilter` factory function:
|
||||
|
||||
```js
|
||||
import filterFactory, { selectFilter, Comparator } from 'react-bootstrap-table2-filter';
|
||||
import filterFactory, { selectFilter, Comparator, numberFilter } from 'react-bootstrap-table2-filter';
|
||||
// omit...
|
||||
|
||||
const numberFilter = numberFilter({
|
||||
@@ -149,4 +198,93 @@ const numberFilter = numberFilter({
|
||||
})
|
||||
|
||||
// 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
|
||||
|
||||
23
packages/react-bootstrap-table2-filter/index.js
vendored
23
packages/react-bootstrap-table2-filter/index.js
vendored
@@ -1,14 +1,19 @@
|
||||
import TextFilter from './src/components/text';
|
||||
import SelectFilter from './src/components/select';
|
||||
import MultiSelectFilter from './src/components/multiselect';
|
||||
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 { FILTER_TYPE } from './src/const';
|
||||
|
||||
export default (options = {}) => ({
|
||||
wrapperFactory,
|
||||
createContext,
|
||||
options
|
||||
});
|
||||
|
||||
export const FILTER_TYPES = FILTER_TYPE;
|
||||
|
||||
export const Comparator = Comparison;
|
||||
|
||||
export const textFilter = (props = {}) => ({
|
||||
@@ -21,7 +26,21 @@ export const selectFilter = (props = {}) => ({
|
||||
props
|
||||
});
|
||||
|
||||
export const multiSelectFilter = (props = {}) => ({
|
||||
Filter: MultiSelectFilter,
|
||||
props
|
||||
});
|
||||
|
||||
export const numberFilter = (props = {}) => ({
|
||||
Filter: NumberFilter,
|
||||
props
|
||||
});
|
||||
|
||||
export const dateFilter = (props = {}) => ({
|
||||
Filter: DateFilter,
|
||||
props
|
||||
});
|
||||
|
||||
export const customFilter = (props = {}) => ({
|
||||
props
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-bootstrap-table2-filter",
|
||||
"version": "0.1.4",
|
||||
"version": "1.0.0",
|
||||
"description": "it's a column filter addon for react-bootstrap-table2",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -38,7 +38,7 @@
|
||||
],
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.0.0",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0"
|
||||
"react": "^16.3.0",
|
||||
"react-dom": "^16.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
213
packages/react-bootstrap-table2-filter/src/components/date.js
vendored
Normal file
213
packages/react-bootstrap-table2-filter/src/components/date.js
vendored
Normal 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;
|
||||
152
packages/react-bootstrap-table2-filter/src/components/multiselect.js
vendored
Normal file
152
packages/react-bootstrap-table2-filter/src/components/multiselect.js
vendored
Normal 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;
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint jsx-a11y/no-static-element-interactions: 0 */
|
||||
/* eslint react/require-default-props: 0 */
|
||||
/* eslint no-return-assign: 0 */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
@@ -30,11 +32,25 @@ class NumberFilter extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { column, onFilter } = this.props;
|
||||
const { column, onFilter, getFilter } = this.props;
|
||||
const comparator = this.numberFilterComparator.value;
|
||||
const number = this.numberFilter.value;
|
||||
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;
|
||||
this.timeout = setTimeout(() => {
|
||||
onFilter(column, { number: filterValue, comparator }, FILTER_TYPE.NUMBER);
|
||||
onFilter(column, FILTER_TYPE.NUMBER)({ number: filterValue, comparator });
|
||||
}, delay);
|
||||
}
|
||||
|
||||
@@ -65,7 +81,7 @@ class NumberFilter extends Component {
|
||||
// if (comparator === '') {
|
||||
// return;
|
||||
// }
|
||||
onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER);
|
||||
onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator });
|
||||
}
|
||||
|
||||
onChangeComparator(e) {
|
||||
@@ -75,7 +91,7 @@ class NumberFilter extends Component {
|
||||
// if (value === '') {
|
||||
// return;
|
||||
// }
|
||||
onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER);
|
||||
onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator });
|
||||
}
|
||||
|
||||
getComparatorOptions() {
|
||||
@@ -116,7 +132,7 @@ class NumberFilter extends Component {
|
||||
this.setState(() => ({ isSelected: (number !== '') }));
|
||||
this.numberFilterComparator.value = comparator;
|
||||
this.numberFilter.value = number;
|
||||
onFilter(column, { number, comparator }, FILTER_TYPE.NUMBER);
|
||||
onFilter(column, FILTER_TYPE.NUMBER)({ number, comparator });
|
||||
}
|
||||
|
||||
cleanFiltered() {
|
||||
@@ -126,7 +142,7 @@ class NumberFilter extends Component {
|
||||
this.setState(() => ({ isSelected: (value !== '') }));
|
||||
this.numberFilterComparator.value = comparator;
|
||||
this.numberFilter.value = value;
|
||||
onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER);
|
||||
onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator });
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -152,7 +168,11 @@ class NumberFilter extends Component {
|
||||
`;
|
||||
|
||||
return (
|
||||
<div className={ `filter number-filter ${className}` } style={ style }>
|
||||
<div
|
||||
onClick={ e => e.stopPropagation() }
|
||||
className={ `filter number-filter ${className}` }
|
||||
style={ style }
|
||||
>
|
||||
<select
|
||||
ref={ n => this.numberFilterComparator = n }
|
||||
style={ comparatorStyle }
|
||||
@@ -224,7 +244,8 @@ NumberFilter.propTypes = {
|
||||
comparatorStyle: PropTypes.object,
|
||||
comparatorClassName: PropTypes.string,
|
||||
numberStyle: PropTypes.object,
|
||||
numberClassName: PropTypes.string
|
||||
numberClassName: PropTypes.string,
|
||||
getFilter: PropTypes.func
|
||||
};
|
||||
|
||||
NumberFilter.defaultProps = {
|
||||
|
||||
@@ -25,9 +25,21 @@ class SelectFilter extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { column, onFilter, getFilter } = this.props;
|
||||
|
||||
const value = this.selectInput.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) {
|
||||
const value = this.selectInput.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 : '';
|
||||
this.setState(() => ({ isSelected: 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) {
|
||||
this.selectInput.value = 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) {
|
||||
const { value } = e.target;
|
||||
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() {
|
||||
@@ -90,6 +102,7 @@ class SelectFilter extends Component {
|
||||
comparator,
|
||||
withoutEmptyOption,
|
||||
caseSensitive,
|
||||
getFilter,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
@@ -103,6 +116,7 @@ class SelectFilter extends Component {
|
||||
style={ style }
|
||||
className={ selectClass }
|
||||
onChange={ this.filter }
|
||||
onClick={ e => e.stopPropagation() }
|
||||
defaultValue={ defaultValue !== undefined ? defaultValue : '' }
|
||||
>
|
||||
{ this.getOptions() }
|
||||
@@ -121,7 +135,8 @@ SelectFilter.propTypes = {
|
||||
className: PropTypes.string,
|
||||
withoutEmptyOption: PropTypes.bool,
|
||||
defaultValue: PropTypes.any,
|
||||
caseSensitive: PropTypes.bool
|
||||
caseSensitive: PropTypes.bool,
|
||||
getFilter: PropTypes.func
|
||||
};
|
||||
|
||||
SelectFilter.defaultProps = {
|
||||
|
||||
@@ -17,10 +17,21 @@ class TextFilter extends Component {
|
||||
value: props.defaultValue
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { onFilter, getFilter, column } = this.props;
|
||||
const defaultValue = this.input.value;
|
||||
|
||||
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;
|
||||
this.setState(() => ({ value: filterValue }));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -53,12 +64,12 @@ class TextFilter extends Component {
|
||||
cleanFiltered() {
|
||||
const value = this.props.defaultValue;
|
||||
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) {
|
||||
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) {
|
||||
@@ -77,8 +88,10 @@ class TextFilter extends Component {
|
||||
onFilter,
|
||||
caseSensitive,
|
||||
defaultValue,
|
||||
getFilter,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
// stopPropagation for onClick event is try to prevent sort was triggered.
|
||||
return (
|
||||
<input
|
||||
@@ -105,7 +118,8 @@ TextFilter.propTypes = {
|
||||
placeholder: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
caseSensitive: PropTypes.bool
|
||||
caseSensitive: PropTypes.bool,
|
||||
getFilter: PropTypes.func
|
||||
};
|
||||
|
||||
TextFilter.defaultProps = {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
export const FILTER_TYPE = {
|
||||
TEXT: 'TEXT',
|
||||
SELECT: 'SELECT',
|
||||
NUMBER: 'NUMBER'
|
||||
MULTISELECT: 'MULTISELECT',
|
||||
NUMBER: 'NUMBER',
|
||||
DATE: 'DATE'
|
||||
};
|
||||
|
||||
export const FILTER_DELAY = 500;
|
||||
|
||||
99
packages/react-bootstrap-table2-filter/src/context.js
vendored
Normal file
99
packages/react-bootstrap-table2-filter/src/context.js
vendored
Normal 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
|
||||
};
|
||||
};
|
||||
168
packages/react-bootstrap-table2-filter/src/filter.js
vendored
168
packages/react-bootstrap-table2-filter/src/filter.js
vendored
@@ -6,30 +6,37 @@ import { LIKE, EQ, NE, GT, GE, LT, LE } from './comparison';
|
||||
export const filterByText = _ => (
|
||||
data,
|
||||
dataField,
|
||||
{ filterVal = '', comparator = LIKE, caseSensitive },
|
||||
{ filterVal: userInput = '', comparator = LIKE, caseSensitive },
|
||||
customFilterValue
|
||||
) =>
|
||||
data.filter((row) => {
|
||||
let cell = _.get(row, dataField);
|
||||
if (customFilterValue) {
|
||||
cell = customFilterValue(cell, row);
|
||||
}
|
||||
const cellStr = _.isDefined(cell) ? cell.toString() : '';
|
||||
if (comparator === EQ) {
|
||||
return cellStr === filterVal;
|
||||
}
|
||||
if (caseSensitive) {
|
||||
return cellStr.includes(filterVal);
|
||||
}
|
||||
return cellStr.toLocaleUpperCase().indexOf(filterVal.toLocaleUpperCase()) !== -1;
|
||||
});
|
||||
) => {
|
||||
// make sure filter value to be a string
|
||||
const filterVal = userInput.toString();
|
||||
|
||||
return (
|
||||
data.filter((row) => {
|
||||
let cell = _.get(row, dataField);
|
||||
if (customFilterValue) {
|
||||
cell = customFilterValue(cell, row);
|
||||
}
|
||||
const cellStr = _.isDefined(cell) ? cell.toString() : '';
|
||||
if (comparator === EQ) {
|
||||
return cellStr === filterVal;
|
||||
}
|
||||
if (caseSensitive) {
|
||||
return cellStr.includes(filterVal);
|
||||
}
|
||||
|
||||
return cellStr.toLocaleUpperCase().indexOf(filterVal.toLocaleUpperCase()) !== -1;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const filterByNumber = _ => (
|
||||
data,
|
||||
dataField,
|
||||
{ filterVal: { comparator, number } },
|
||||
customFilterValue
|
||||
) =>
|
||||
) => (
|
||||
data.filter((row) => {
|
||||
if (number === '' || !comparator) return true;
|
||||
let valid = true;
|
||||
@@ -81,7 +88,124 @@ export const filterByNumber = _ => (
|
||||
}
|
||||
}
|
||||
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) => {
|
||||
let filterFn;
|
||||
@@ -90,18 +214,24 @@ export const filterFactory = _ => (filterType) => {
|
||||
case FILTER_TYPE.SELECT:
|
||||
filterFn = filterByText(_);
|
||||
break;
|
||||
case FILTER_TYPE.MULTISELECT:
|
||||
filterFn = filterByArray(_);
|
||||
break;
|
||||
case FILTER_TYPE.NUMBER:
|
||||
filterFn = filterByNumber(_);
|
||||
break;
|
||||
case FILTER_TYPE.DATE:
|
||||
filterFn = filterByDate(_);
|
||||
break;
|
||||
default:
|
||||
filterFn = filterByText(_);
|
||||
}
|
||||
return filterFn;
|
||||
};
|
||||
|
||||
export const filters = (store, columns, _) => (currFilters) => {
|
||||
export const filters = (data, columns, _) => (currFilters) => {
|
||||
const factory = filterFactory(_);
|
||||
let result = store.getAllData();
|
||||
let result = data;
|
||||
let filterFn;
|
||||
Object.keys(currFilters).forEach((dataField) => {
|
||||
const filterObj = currFilters[dataField];
|
||||
|
||||
@@ -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 }
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -5,7 +5,8 @@
|
||||
.react-bootstrap-table > table > thead > tr > th .select-filter option[value=''],
|
||||
.react-bootstrap-table > table > thead > tr > th .select-filter.placeholder-selected,
|
||||
.react-bootstrap-table > table > thead > tr > th .filter::-webkit-input-placeholder,
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input::-webkit-input-placeholder {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input::-webkit-input-placeholder,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter-input::-webkit-input-placeholder {
|
||||
color: lightgrey;
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -15,17 +16,20 @@
|
||||
font-style: initial;
|
||||
}
|
||||
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-input,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter-input {
|
||||
margin-left: 5px;
|
||||
float: left;
|
||||
width: calc(100% - 67px - 5px);
|
||||
}
|
||||
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-comparator {
|
||||
.react-bootstrap-table > table > thead > tr > th .number-filter-comparator,
|
||||
.react-bootstrap-table > table > thead > tr > th .date-filter-comparator {
|
||||
width: 67px;
|
||||
float: left;
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
import 'jsdom-global/register';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import DateFilter from '../../src/components/date';
|
||||
import { FILTER_TYPE } from '../../src/const';
|
||||
import * as Comparator from '../../src/comparison';
|
||||
|
||||
|
||||
describe('Date Filter', () => {
|
||||
let wrapper;
|
||||
|
||||
|
||||
const onFilterFirstReturn = jest.fn();
|
||||
const onFilter = jest.fn().mockReturnValue(onFilterFirstReturn);
|
||||
|
||||
const column = {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
onFilter.mockClear();
|
||||
onFilterFirstReturn.mockClear();
|
||||
|
||||
// onFilter.returns(onFilterFirstReturn);
|
||||
});
|
||||
|
||||
describe('initialization', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter onFilter={ onFilter } column={ column } />
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-input')).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-comparator')).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should rendering comparator options correctly', () => {
|
||||
const select = wrapper.find('select');
|
||||
expect(select.find('option')).toHaveLength(wrapper.prop('comparators').length + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when withoutEmptyComparatorOption prop is true', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
withoutEmptyComparatorOption
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering comparator options correctly', () => {
|
||||
const select = wrapper.find('.date-filter-comparator');
|
||||
expect(select.find('option')).toHaveLength(wrapper.prop('comparators').length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue.date props is defined', () => {
|
||||
const date = new Date(2018, 0, 1);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
defaultValue={ { date } }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering input successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
const input = wrapper.find('.date-filter-input');
|
||||
expect(input).toHaveLength(1);
|
||||
expect(input.props().defaultValue).toEqual(wrapper.instance().getDefaultDate());
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue.comparator props is defined', () => {
|
||||
const comparator = Comparator.EQ;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
defaultValue={ { comparator } }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering comparator select successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
const select = wrapper.find('.date-filter-comparator');
|
||||
expect(select).toHaveLength(1);
|
||||
expect(select.props().defaultValue).toEqual(comparator);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.getFilter is defined', () => {
|
||||
let programmaticallyFilter;
|
||||
|
||||
const comparator = Comparator.EQ;
|
||||
const date = new Date(2018, 0, 1);
|
||||
|
||||
const getFilter = (filter) => {
|
||||
programmaticallyFilter = filter;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter onFilter={ onFilter } column={ column } getFilter={ getFilter } />
|
||||
);
|
||||
|
||||
programmaticallyFilter({ comparator, date });
|
||||
});
|
||||
|
||||
it('should do onFilter correctly when exported function was executed', () => {
|
||||
expect(onFilter).toHaveBeenCalledTimes(1);
|
||||
expect(onFilter).toHaveBeenCalledWith(column, FILTER_TYPE.DATE, undefined);
|
||||
expect(onFilterFirstReturn).toHaveBeenCalledTimes(1);
|
||||
expect(onFilterFirstReturn).toHaveBeenCalledWith({ comparator, date });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue.number and defaultValue.comparator props are defined', () => {
|
||||
let date;
|
||||
let comparator;
|
||||
|
||||
beforeEach(() => {
|
||||
date = new Date();
|
||||
comparator = Comparator.EQ;
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
defaultValue={ { date, comparator } }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should calling onFilter on componentDidMount', () => {
|
||||
expect(onFilter).toHaveBeenCalledTimes(1);
|
||||
expect(onFilter).toHaveBeenCalledWith(column, FILTER_TYPE.DATE, true);
|
||||
expect(onFilterFirstReturn).toHaveBeenCalledTimes(1);
|
||||
// expect(onFilterFirstReturn).toHaveBeenCalledWith({ comparator, date });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when style props is defined', () => {
|
||||
const style = { backgroundColor: 'red' };
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
style={ style }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter').prop('style')).toEqual(style);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when dateStyle props is defined', () => {
|
||||
const dateStyle = { backgroundColor: 'red' };
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
dateStyle={ dateStyle }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-input').prop('style')).toEqual(dateStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when comparatorStyle props is defined', () => {
|
||||
const comparatorStyle = { backgroundColor: 'red' };
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
comparatorStyle={ comparatorStyle }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-comparator').prop('style')).toEqual(comparatorStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when className props is defined', () => {
|
||||
const className = 'test';
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
className={ className }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.hasClass(className)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when dateClassName props is defined', () => {
|
||||
const className = 'test';
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
dateClassName={ className }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-input').prop('className').indexOf(className) > -1).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when comparatorClassName props is defined', () => {
|
||||
const className = 'test';
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
comparatorClassName={ className }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-comparator').prop('className').indexOf(className) > -1).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,354 @@
|
||||
import 'jsdom-global/register';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { mount } from 'enzyme';
|
||||
import MultiSelectFilter from '../../src/components/multiselect';
|
||||
import { FILTER_TYPE } from '../../src/const';
|
||||
|
||||
|
||||
describe('Multi Select Filter', () => {
|
||||
let wrapper;
|
||||
let instance;
|
||||
|
||||
// onFilter(x)(y) = filter result
|
||||
const onFilter = sinon.stub();
|
||||
const onFilterFirstReturn = sinon.stub();
|
||||
|
||||
const column = {
|
||||
dataField: 'quality',
|
||||
text: 'Product Quality'
|
||||
};
|
||||
|
||||
const options = {
|
||||
0: 'Bad',
|
||||
1: 'Good',
|
||||
2: 'Unknown'
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
onFilter.reset();
|
||||
onFilterFirstReturn.reset();
|
||||
|
||||
onFilter.returns(onFilterFirstReturn);
|
||||
});
|
||||
|
||||
describe('initialization', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter onFilter={ onFilter } column={ column } options={ options } />
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
it('should have correct state', () => {
|
||||
expect(instance.state.isSelected).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('select')).toHaveLength(1);
|
||||
expect(wrapper.find('.select-filter')).toHaveLength(1);
|
||||
expect(wrapper.find('.placeholder-selected')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should rendering select options correctly', () => {
|
||||
const select = wrapper.find('select');
|
||||
expect(select.find('option')).toHaveLength(Object.keys(options).length + 1);
|
||||
expect(select.childAt(0).text()).toEqual(`Select ${column.text}...`);
|
||||
|
||||
Object.keys(options).forEach((key, i) => {
|
||||
expect(select.childAt(i + 1).prop('value')).toEqual(key);
|
||||
expect(select.childAt(i + 1).text()).toEqual(options[key]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue is defined', () => {
|
||||
let defaultValue;
|
||||
|
||||
describe('and it is valid', () => {
|
||||
beforeEach(() => {
|
||||
defaultValue = ['0'];
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
defaultValue={ defaultValue }
|
||||
/>
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
it('should have correct state', () => {
|
||||
expect(instance.state.isSelected).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.placeholder-selected')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should calling onFilter on componentDidMount', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.getFilter is defined', () => {
|
||||
let programmaticallyFilter;
|
||||
|
||||
const filterValue = ['foo'];
|
||||
|
||||
const getFilter = (filter) => {
|
||||
programmaticallyFilter = filter;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
getFilter={ getFilter }
|
||||
/>
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
|
||||
programmaticallyFilter(filterValue);
|
||||
});
|
||||
|
||||
it('should do onFilter correctly when exported function was executed', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(filterValue)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should setState correctly when exported function was executed', () => {
|
||||
expect(instance.state.isSelected).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when placeholder is defined', () => {
|
||||
const placeholder = 'test';
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
placeholder={ placeholder }
|
||||
/>
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
const select = wrapper.find('select');
|
||||
expect(select.childAt(0).text()).toEqual(placeholder);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when style is defined', () => {
|
||||
const style = { backgroundColor: 'red' };
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
style={ style }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('select').prop('style')).toEqual(style);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when withoutEmptyOption is defined', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
withoutEmptyOption
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering select without default empty option', () => {
|
||||
const select = wrapper.find('select');
|
||||
expect(select.find('option')).toHaveLength(Object.keys(options).length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('componentDidUpdate', () => {
|
||||
let prevProps;
|
||||
|
||||
describe('when props.defaultValue is diff from prevProps.defaultValue', () => {
|
||||
const defaultValue = ['0'];
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
defaultValue={ defaultValue }
|
||||
/>
|
||||
);
|
||||
prevProps = {
|
||||
column,
|
||||
options,
|
||||
defaultValue: ['1']
|
||||
};
|
||||
instance = wrapper.instance();
|
||||
instance.componentDidUpdate(prevProps);
|
||||
});
|
||||
|
||||
it('should update', () => {
|
||||
expect(onFilter.callCount).toBe(2);
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.callCount).toBe(2);
|
||||
expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.options is diff from prevProps.options', () => {
|
||||
const defaultValue = ['0'];
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ {
|
||||
...options,
|
||||
3: 'Best'
|
||||
} }
|
||||
defaultValue={ defaultValue }
|
||||
/>
|
||||
);
|
||||
prevProps = {
|
||||
column,
|
||||
options
|
||||
};
|
||||
instance = wrapper.instance();
|
||||
instance.componentDidUpdate(prevProps);
|
||||
});
|
||||
|
||||
it('should update', () => {
|
||||
expect(onFilter.callCount).toBe(2);
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.callCount).toBe(2);
|
||||
expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cleanFiltered', () => {
|
||||
describe('when props.defaultValue is defined', () => {
|
||||
const defaultValue = ['0'];
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
defaultValue={ defaultValue }
|
||||
/>
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
instance.cleanFiltered();
|
||||
});
|
||||
|
||||
it('should setting state correctly', () => {
|
||||
expect(instance.state.isSelected).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.callCount).toBe(2);
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.callCount).toBe(2);
|
||||
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.defaultValue is not defined', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
/>
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
instance.cleanFiltered();
|
||||
});
|
||||
|
||||
it('should setting state correctly', () => {
|
||||
expect(instance.state.isSelected).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.callCount).toBe(1);
|
||||
expect(onFilterFirstReturn.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyFilter', () => {
|
||||
const values = ['2'];
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter onFilter={ onFilter } column={ column } options={ options } />
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
instance.applyFilter(values);
|
||||
});
|
||||
|
||||
it('should setting state correctly', () => {
|
||||
expect(instance.state.isSelected).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(values)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter', () => {
|
||||
const event = { target: { selectedOptions: [{ value: 'tester' }] } };
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<MultiSelectFilter onFilter={ onFilter } column={ column } options={ options } />
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
instance.filter(event);
|
||||
});
|
||||
|
||||
it('should setting state correctly', () => {
|
||||
expect(instance.state.isSelected).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(
|
||||
event.target.selectedOptions.map(item => item.value))).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -9,7 +9,11 @@ import * as Comparator from '../../src/comparison';
|
||||
|
||||
describe('Number Filter', () => {
|
||||
let wrapper;
|
||||
|
||||
// onFilter(x)(y) = filter result
|
||||
const onFilter = sinon.stub();
|
||||
const onFilterFirstReturn = sinon.stub();
|
||||
|
||||
const column = {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
@@ -17,6 +21,9 @@ describe('Number Filter', () => {
|
||||
|
||||
afterEach(() => {
|
||||
onFilter.reset();
|
||||
onFilterFirstReturn.reset();
|
||||
|
||||
onFilter.returns(onFilterFirstReturn);
|
||||
});
|
||||
|
||||
describe('initialization', () => {
|
||||
@@ -90,6 +97,36 @@ describe('Number Filter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.getFilter is defined', () => {
|
||||
let programmaticallyFilter;
|
||||
|
||||
const comparator = Comparator.EQ;
|
||||
const number = 123;
|
||||
|
||||
const getFilter = (filter) => {
|
||||
programmaticallyFilter = filter;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<NumberFilter onFilter={ onFilter } column={ column } getFilter={ getFilter } />
|
||||
);
|
||||
|
||||
programmaticallyFilter({ comparator, number });
|
||||
});
|
||||
|
||||
it('should do onFilter correctly when exported function was executed', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.NUMBER)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith({ comparator, number })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should setState correctly when exported function was executed', () => {
|
||||
expect(wrapper.state().isSelected).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue.number and defaultValue.comparator props is defined', () => {
|
||||
const number = 203;
|
||||
const comparator = Comparator.EQ;
|
||||
@@ -110,8 +147,9 @@ describe('Number Filter', () => {
|
||||
|
||||
it('should calling onFilter on componentDidMount', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(
|
||||
column, { number: `${number}`, comparator }, FILTER_TYPE.NUMBER)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.NUMBER, true)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith({ number: `${number}`, comparator })).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -9,19 +9,27 @@ import { FILTER_TYPE } from '../../src/const';
|
||||
describe('Select Filter', () => {
|
||||
let wrapper;
|
||||
let instance;
|
||||
|
||||
// onFilter(x)(y) = filter result
|
||||
const onFilter = sinon.stub();
|
||||
const onFilterFirstReturn = sinon.stub();
|
||||
|
||||
const column = {
|
||||
dataField: 'quality',
|
||||
text: 'Product Quality'
|
||||
};
|
||||
|
||||
const options = {
|
||||
0: 'Bad',
|
||||
1: 'Good',
|
||||
2: 'Unknow'
|
||||
2: 'Unknown'
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
onFilter.reset();
|
||||
onFilterFirstReturn.reset();
|
||||
|
||||
onFilter.returns(onFilterFirstReturn);
|
||||
});
|
||||
|
||||
describe('initialization', () => {
|
||||
@@ -83,11 +91,48 @@ describe('Select Filter', () => {
|
||||
|
||||
it('should calling onFilter on componentDidMount', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, defaultValue, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT, true)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.getFilter is defined', () => {
|
||||
let programmaticallyFilter;
|
||||
|
||||
const filterValue = 'foo';
|
||||
|
||||
const getFilter = (filter) => {
|
||||
programmaticallyFilter = filter;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<SelectFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
options={ options }
|
||||
getFilter={ getFilter }
|
||||
/>
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
|
||||
programmaticallyFilter(filterValue);
|
||||
});
|
||||
|
||||
it('should do onFilter correctly when exported function was executed', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(filterValue)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should setState correctly when exported function was executed', () => {
|
||||
expect(instance.state.isSelected).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when placeholder is defined', () => {
|
||||
const placeholder = 'test';
|
||||
beforeEach(() => {
|
||||
@@ -170,8 +215,9 @@ describe('Select Filter', () => {
|
||||
|
||||
it('should update', () => {
|
||||
expect(onFilter.callCount).toBe(2);
|
||||
expect(onFilter.calledWith(
|
||||
column, instance.props.defaultValue, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.callCount).toBe(2);
|
||||
expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -198,8 +244,9 @@ describe('Select Filter', () => {
|
||||
|
||||
it('should update', () => {
|
||||
expect(onFilter.callCount).toBe(2);
|
||||
expect(onFilter.calledWith(
|
||||
column, instance.props.defaultValue, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.callCount).toBe(2);
|
||||
expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -226,7 +273,9 @@ describe('Select Filter', () => {
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.callCount).toBe(2);
|
||||
expect(onFilter.calledWith(column, defaultValue, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.callCount).toBe(2);
|
||||
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -249,6 +298,7 @@ describe('Select Filter', () => {
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.callCount).toBe(1);
|
||||
expect(onFilterFirstReturn.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -268,8 +318,10 @@ describe('Select Filter', () => {
|
||||
});
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.callCount).toBe(1);
|
||||
expect(onFilter.calledWith(column, value, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(value)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -289,8 +341,10 @@ describe('Select Filter', () => {
|
||||
});
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.callCount).toBe(1);
|
||||
expect(onFilter.calledWith(column, event.target.value, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(event.target.value)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,11 @@ jest.useFakeTimers();
|
||||
describe('Text Filter', () => {
|
||||
let wrapper;
|
||||
let instance;
|
||||
|
||||
// onFilter(x)(y) = filter result
|
||||
const onFilter = sinon.stub();
|
||||
const onFilterFirstReturn = sinon.stub();
|
||||
|
||||
const column = {
|
||||
dataField: 'price',
|
||||
text: 'Price'
|
||||
@@ -17,6 +21,9 @@ describe('Text Filter', () => {
|
||||
|
||||
afterEach(() => {
|
||||
onFilter.reset();
|
||||
onFilterFirstReturn.reset();
|
||||
|
||||
onFilter.returns(onFilterFirstReturn);
|
||||
});
|
||||
|
||||
describe('initialization', () => {
|
||||
@@ -58,7 +65,39 @@ describe('Text Filter', () => {
|
||||
|
||||
it('should calling onFilter on componentDidMount', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, defaultValue, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.TEXT, true)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.getFilter is defined', () => {
|
||||
let programmaticallyFilter;
|
||||
|
||||
const filterValue = 'foo';
|
||||
|
||||
const getFilter = (filter) => {
|
||||
programmaticallyFilter = filter;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TextFilter onFilter={ onFilter } column={ column } getFilter={ getFilter } />
|
||||
);
|
||||
instance = wrapper.instance();
|
||||
|
||||
programmaticallyFilter(filterValue);
|
||||
});
|
||||
|
||||
it('should do onFilter correctly when exported function was executed', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(filterValue)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should setState correctly when exported function was executed', () => {
|
||||
expect(instance.state.value).toEqual(filterValue);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -114,7 +153,9 @@ describe('Text Filter', () => {
|
||||
|
||||
it('should calling onFilter correctly when props.defaultValue is changed', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, nextDefaultValue, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(nextDefaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,8 +174,9 @@ describe('Text Filter', () => {
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(
|
||||
column, instance.props.defaultValue, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -154,7 +196,9 @@ describe('Text Filter', () => {
|
||||
|
||||
it('should calling onFilter correctly', () => {
|
||||
expect(onFilter.calledOnce).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, filterText, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||
expect(onFilterFirstReturn.calledWith(filterText)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
253
packages/react-bootstrap-table2-filter/test/context.test.js
Normal file
253
packages/react-bootstrap-table2-filter/test/context.test.js
Normal file
@@ -0,0 +1,253 @@
|
||||
import 'jsdom-global/register';
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import _ from 'react-bootstrap-table-next/src/utils';
|
||||
import BootstrapTable from 'react-bootstrap-table-next/src/bootstrap-table';
|
||||
|
||||
import {
|
||||
FILTER_TYPE
|
||||
} from '../src/const';
|
||||
import createFilterContext from '../src/context';
|
||||
import { textFilter } from '../index';
|
||||
|
||||
describe('FilterContext', () => {
|
||||
let wrapper;
|
||||
let FilterContext;
|
||||
|
||||
const data = [{
|
||||
id: 1,
|
||||
name: 'A'
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'B'
|
||||
}];
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'ID',
|
||||
filter: textFilter()
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Name',
|
||||
filter: textFilter()
|
||||
}];
|
||||
|
||||
const mockBase = jest.fn((props => (
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
{ ...props }
|
||||
/>
|
||||
)));
|
||||
|
||||
const handleFilterChange = jest.fn();
|
||||
|
||||
function shallowContext(
|
||||
enableRemote = false
|
||||
) {
|
||||
mockBase.mockReset();
|
||||
handleFilterChange.mockReset();
|
||||
FilterContext = createFilterContext(
|
||||
_,
|
||||
jest.fn().mockReturnValue(enableRemote),
|
||||
handleFilterChange
|
||||
);
|
||||
|
||||
return (
|
||||
<FilterContext.Provider
|
||||
columns={ columns }
|
||||
data={ data }
|
||||
>
|
||||
<FilterContext.Consumer>
|
||||
{
|
||||
filterProps => mockBase(filterProps)
|
||||
}
|
||||
</FilterContext.Consumer>
|
||||
</FilterContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('default render', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext());
|
||||
wrapper.render();
|
||||
});
|
||||
|
||||
it('should have correct Provider property after calling createFilterContext', () => {
|
||||
expect(FilterContext.Provider).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have correct Consumer property after calling createFilterContext', () => {
|
||||
expect(FilterContext.Consumer).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have correct currFilters', () => {
|
||||
expect(wrapper.instance().currFilters).toEqual({});
|
||||
});
|
||||
|
||||
it('should pass correct cell editing props to children element', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(mockBase).toHaveBeenCalledWith({
|
||||
data,
|
||||
onFilter: wrapper.instance().onFilter,
|
||||
onExternalFilter: wrapper.instance().onExternalFilter
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when remote filter is enable', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext(true));
|
||||
wrapper.render();
|
||||
wrapper.instance().currFilters = { price: { filterVal: 20, filterType: FILTER_TYPE.TEXT } };
|
||||
});
|
||||
|
||||
it('should pass original data without internal filtering', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(mockBase).toHaveBeenCalledWith({
|
||||
data,
|
||||
onFilter: wrapper.instance().onFilter,
|
||||
onExternalFilter: wrapper.instance().onExternalFilter
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('componentDidMount', () => {
|
||||
describe('when remote filter is disabled', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext());
|
||||
wrapper.render();
|
||||
wrapper.instance().componentDidMount();
|
||||
});
|
||||
|
||||
it('should not call handleFilterChange', () => {
|
||||
expect(handleFilterChange).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when remote filter is enable but currFilters is empty', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext(true));
|
||||
wrapper.render();
|
||||
wrapper.instance().componentDidMount();
|
||||
});
|
||||
|
||||
it('should not call handleFilterChange', () => {
|
||||
expect(handleFilterChange).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when remote filter is enable and currFilters is not empty', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext(true));
|
||||
wrapper.instance().currFilters.price = { filterVal: 40, filterType: FILTER_TYPE.TEXT };
|
||||
});
|
||||
|
||||
it('should not call handleFilterChange', () => {
|
||||
wrapper.instance().componentDidMount();
|
||||
expect(handleFilterChange).toHaveBeenCalledTimes(1);
|
||||
expect(handleFilterChange).toHaveBeenCalledWith(wrapper.instance().currFilters);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onFilter', () => {
|
||||
let instance;
|
||||
describe('when filterVal is empty or undefined', () => {
|
||||
const filterVals = ['', undefined, []];
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext());
|
||||
wrapper.render();
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
it('should correct currFilters', () => {
|
||||
filterVals.forEach((filterVal) => {
|
||||
instance.onFilter(columns[1], FILTER_TYPE.TEXT)(filterVal);
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when filterVal is existing', () => {
|
||||
const filterVal = '3';
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext());
|
||||
wrapper.render();
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
it('should correct currFilters', () => {
|
||||
instance.onFilter(columns[1], FILTER_TYPE.TEXT)(filterVal);
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when remote filter is enabled', () => {
|
||||
const filterVal = '3';
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext(true));
|
||||
wrapper.render();
|
||||
instance = wrapper.instance();
|
||||
instance.onFilter(columns[1], FILTER_TYPE.TEXT)(filterVal);
|
||||
});
|
||||
|
||||
it('should correct currFilters', () => {
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should calling handleFilterChange correctly', () => {
|
||||
expect(handleFilterChange).toHaveBeenCalledTimes(1);
|
||||
expect(handleFilterChange).toHaveBeenCalledWith(instance.currFilters);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when remote filter is enabled but initialize argument is true', () => {
|
||||
const filterVal = '3';
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext(true));
|
||||
wrapper.render();
|
||||
instance = wrapper.instance();
|
||||
instance.onFilter(columns[1], FILTER_TYPE.TEXT, true)(filterVal);
|
||||
});
|
||||
|
||||
it('should correct currFilters', () => {
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should not call handleFilterChange correctly', () => {
|
||||
expect(handleFilterChange).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('combination', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(shallowContext());
|
||||
wrapper.render();
|
||||
instance = wrapper.instance();
|
||||
});
|
||||
|
||||
it('should set correct currFilters', () => {
|
||||
instance.onFilter(columns[0], FILTER_TYPE.TEXT)('3');
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(1);
|
||||
|
||||
instance.onFilter(columns[0], FILTER_TYPE.TEXT)('2');
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(1);
|
||||
|
||||
instance.onFilter(columns[1], FILTER_TYPE.TEXT)('2');
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(2);
|
||||
|
||||
instance.onFilter(columns[1], FILTER_TYPE.TEXT)('');
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(1);
|
||||
|
||||
instance.onFilter(columns[0], FILTER_TYPE.TEXT)('');
|
||||
expect(Object.keys(instance.currFilters)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,4 @@
|
||||
import sinon from 'sinon';
|
||||
import _ from 'react-bootstrap-table-next/src/utils';
|
||||
import Store from 'react-bootstrap-table-next/src/store';
|
||||
|
||||
import { filters } from '../src/filter';
|
||||
import { FILTER_TYPE } from '../src/const';
|
||||
@@ -11,19 +9,16 @@ for (let i = 0; i < 20; i += 1) {
|
||||
data.push({
|
||||
id: i,
|
||||
name: `itme name ${i}`,
|
||||
price: 200 + i
|
||||
price: 200 + i,
|
||||
date: new Date(2017, i, 1)
|
||||
});
|
||||
}
|
||||
|
||||
describe('filter', () => {
|
||||
let store;
|
||||
let filterFn;
|
||||
let currFilters;
|
||||
let columns;
|
||||
|
||||
beforeEach(() => {
|
||||
store = new Store('id');
|
||||
store.data = data;
|
||||
currFilters = {};
|
||||
columns = [{
|
||||
dataField: 'id',
|
||||
@@ -34,12 +29,24 @@ describe('filter', () => {
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Price'
|
||||
}, {
|
||||
dataField: 'date',
|
||||
text: 'Date'
|
||||
}];
|
||||
});
|
||||
|
||||
describe('filterByText', () => {
|
||||
beforeEach(() => {
|
||||
filterFn = filters(store, columns, _);
|
||||
describe('when filter value is not a String', () => {
|
||||
it('should transform to string and do the filter', () => {
|
||||
currFilters.name = {
|
||||
filterVal: 3,
|
||||
filterType: FILTER_TYPE.TEXT
|
||||
};
|
||||
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when default comparator is ${LIKE}`, () => {
|
||||
@@ -49,7 +56,7 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.TEXT
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
@@ -63,7 +70,7 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.TEXT
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
@@ -77,7 +84,7 @@ describe('filter', () => {
|
||||
comparator: EQ
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
@@ -85,8 +92,7 @@ describe('filter', () => {
|
||||
|
||||
describe('column.filterValue is defined', () => {
|
||||
beforeEach(() => {
|
||||
columns[1].filterValue = sinon.stub();
|
||||
filterFn = filters(store, columns, _);
|
||||
columns[1].filterValue = jest.fn();
|
||||
});
|
||||
|
||||
it('should calling custom filterValue callback correctly', () => {
|
||||
@@ -95,22 +101,64 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.TEXT
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(columns[1].filterValue.callCount).toBe(data.length);
|
||||
const calls = columns[1].filterValue.getCalls();
|
||||
calls.forEach((call, i) => {
|
||||
expect(call.calledWith(data[i].name, data[i])).toBeTruthy();
|
||||
expect(columns[1].filterValue).toHaveBeenCalledTimes(data.length);
|
||||
// const calls = columns[1].filterValue.mock.calls;
|
||||
// calls.forEach((call, i) => {
|
||||
// expect(call).toEqual([data[i].name, data[i]]);
|
||||
// expect(call.calledWith(data[i].name, data[i])).toBeTruthy();
|
||||
// });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterByArray', () => {
|
||||
describe('when filter value is empty array', () => {
|
||||
it('should return original data', () => {
|
||||
currFilters.name = {
|
||||
filterVal: [],
|
||||
filterType: FILTER_TYPE.MULTISELECT
|
||||
};
|
||||
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(data.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when filter value is not an empty array', () => {
|
||||
describe(`and comparator is ${EQ}`, () => {
|
||||
it('should return data correctly', () => {
|
||||
currFilters.price = {
|
||||
filterVal: [201, 203],
|
||||
filterType: FILTER_TYPE.MULTISELECT,
|
||||
comparator: EQ
|
||||
};
|
||||
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`and comparator is ${LIKE}`, () => {
|
||||
it('should return data correctly', () => {
|
||||
currFilters.name = {
|
||||
filterVal: ['name 3', '5'],
|
||||
filterType: FILTER_TYPE.MULTISELECT,
|
||||
comparator: LIKE
|
||||
};
|
||||
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterByNumber', () => {
|
||||
beforeEach(() => {
|
||||
filterFn = filters(store, columns, _);
|
||||
});
|
||||
|
||||
describe('when currFilters.filterVal.comparator is empty', () => {
|
||||
it('should returning correct result', () => {
|
||||
currFilters.price = {
|
||||
@@ -118,11 +166,11 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
let result = filterFn(currFilters);
|
||||
let result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(data.length);
|
||||
|
||||
currFilters.price.filterVal.comparator = undefined;
|
||||
result = filterFn(currFilters);
|
||||
result = filters(result, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(data.length);
|
||||
});
|
||||
});
|
||||
@@ -134,7 +182,7 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(data.length);
|
||||
});
|
||||
});
|
||||
@@ -146,11 +194,11 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
let result = filterFn(currFilters);
|
||||
let result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(1);
|
||||
|
||||
currFilters.price.filterVal.number = '0';
|
||||
result = filterFn(currFilters);
|
||||
result = filters(result, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
@@ -162,7 +210,7 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(16);
|
||||
});
|
||||
});
|
||||
@@ -174,7 +222,7 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(17);
|
||||
});
|
||||
});
|
||||
@@ -186,7 +234,7 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
@@ -198,7 +246,7 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
@@ -210,9 +258,46 @@ describe('filter', () => {
|
||||
filterType: FILTER_TYPE.NUMBER
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
const result = filters(data, columns, _)(currFilters);
|
||||
expect(result).toHaveLength(19);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterByDate', () => {
|
||||
let filterFn;
|
||||
beforeEach(() => {
|
||||
filterFn = filters(data, columns, _);
|
||||
});
|
||||
|
||||
describe('when currFilters.filterVal.comparator is empty', () => {
|
||||
it('should returning correct result', () => {
|
||||
currFilters.price = {
|
||||
filterVal: { comparator: '', date: new Date() },
|
||||
filterType: FILTER_TYPE.DATE
|
||||
};
|
||||
|
||||
let result = filterFn(currFilters);
|
||||
expect(result).toHaveLength(data.length);
|
||||
|
||||
currFilters.price.filterVal.comparator = undefined;
|
||||
result = filterFn(currFilters);
|
||||
expect(result).toHaveLength(data.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when currFilters.filterVal.date is empty', () => {
|
||||
it('should returning correct result', () => {
|
||||
currFilters.price = {
|
||||
filterVal: { comparator: EQ, date: '' },
|
||||
filterType: FILTER_TYPE.DATE
|
||||
};
|
||||
|
||||
const result = filterFn(currFilters);
|
||||
expect(result).toHaveLength(data.length);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO....
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user