mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2026-06-29 21:50:07 +00:00
Compare commits
154 Commits
react-boot
...
react-boot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
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 |
@@ -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-editor`](https://www.npmjs.com/package/react-bootstrap-table2-editor)
|
||||||
* [`react-bootstrap-table2-paginator`](https://www.npmjs.com/package/react-bootstrap-table2-paginator)
|
* [`react-bootstrap-table2-paginator`](https://www.npmjs.com/package/react-bootstrap-table2-paginator)
|
||||||
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
|
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
|
||||||
|
* [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit)
|
||||||
|
|
||||||
This can help your application with less bundled size and also help us have clean design to avoid handling to much logic in kernal module(SRP).
|
This can help your application with less bundled size and also help us have clean design to avoid handling to much logic in kernal module(SRP).
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#### Optional
|
#### Optional
|
||||||
* [remote](#remote)
|
* [remote](#remote)
|
||||||
|
* [bootstrap4](#bootstrap4)
|
||||||
* [loading](#loading)
|
* [loading](#loading)
|
||||||
* [caption](#caption)
|
* [caption](#caption)
|
||||||
* [striped](#striped)
|
* [striped](#striped)
|
||||||
@@ -17,8 +18,11 @@
|
|||||||
* [condensed](#condensed)
|
* [condensed](#condensed)
|
||||||
* [id](#id)
|
* [id](#id)
|
||||||
* [classes](#classes)
|
* [classes](#classes)
|
||||||
|
* [wrapperClasses](#wrapperClasses)
|
||||||
|
* [headerClasses](#headerClasses)
|
||||||
* [cellEdit](#cellEdit)
|
* [cellEdit](#cellEdit)
|
||||||
* [selectRow](#selectRow)
|
* [selectRow](#selectRow)
|
||||||
|
* [expandRow](#expandRow)
|
||||||
* [rowStyle](#rowStyle)
|
* [rowStyle](#rowStyle)
|
||||||
* [rowClasses](#rowClasses)
|
* [rowClasses](#rowClasses)
|
||||||
* [rowEvents](#rowEvents)
|
* [rowEvents](#rowEvents)
|
||||||
@@ -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.
|
There is a special case for remote pagination, even you only specified the pagination need to handle as remote, `react-bootstrap-table2` will handle all the table changes(filter, sort etc) as remote mode, because `react-bootstrap-table2` only know the data of current page, but filtering, searching or sort need to work on overall data.
|
||||||
|
|
||||||
|
### <a name='bootstrap4'>bootstrap4 - [Bool]</a>
|
||||||
|
`true` to indicate your bootstrap version is 4. Default version is 3.
|
||||||
|
|
||||||
### <a name='loading'>loading - [Bool]</a>
|
### <a name='loading'>loading - [Bool]</a>
|
||||||
Telling if table is loading or not, for example: waiting data loading, filtering etc. It's **only** valid when [`remote`](#remote) is enabled.
|
Telling if table is loading or not, for example: waiting data loading, filtering etc. It's **only** valid when [`remote`](#remote) is enabled.
|
||||||
When `loading` is `true`, `react-bootstrap-table2` will attend to render a overlay on table via [`overlay`](#overlay) prop, if [`overlay`](#overlay) prop is not given, `react-bootstrap-table2` will ignore the overlay rendering.
|
When `loading` is `true`, `react-bootstrap-table2` will attend to render a overlay on table via [`overlay`](#overlay) prop, if [`overlay`](#overlay) prop is not given, `react-bootstrap-table2` will ignore the overlay rendering.
|
||||||
@@ -107,12 +114,22 @@ Same as bootstrap `.table-condensed` class for making a table more compact by cu
|
|||||||
Customize id on `table` element.
|
Customize id on `table` element.
|
||||||
### <a name='classes'>classes - [String]</a>
|
### <a name='classes'>classes - [String]</a>
|
||||||
Customize class on `table` element.
|
Customize class on `table` element.
|
||||||
|
|
||||||
|
### <a name='wrapperClasses'>wrapperClasses - [String]</a>
|
||||||
|
Customize class on the outer element which wrap up the `table` element.
|
||||||
|
|
||||||
|
### <a name='headerClasses'>headerClasses - [String]</a>
|
||||||
|
Customize class on the header row(`tr`).
|
||||||
|
|
||||||
### <a name='cellEdit'>cellEdit - [Object]</a>
|
### <a name='cellEdit'>cellEdit - [Object]</a>
|
||||||
Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail.
|
Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail.
|
||||||
|
|
||||||
### <a name='selectRow'>selectRow - [Object]</a>
|
### <a name='selectRow'>selectRow - [Object]</a>
|
||||||
Makes table rows selectable, please see [selectRow definition](./row-selection.md) for more detail.
|
Makes table rows selectable, please see [selectRow definition](./row-selection.md) for more detail.
|
||||||
|
|
||||||
|
### <a name='expandRow'>expandRow - [Object]</a>
|
||||||
|
Makes table rows expandable, please see [expandRow definition](./row-expand.md) for more detail.
|
||||||
|
|
||||||
### <a name='rowStyle'>rowStyle = [Object | Function]</a>
|
### <a name='rowStyle'>rowStyle = [Object | Function]</a>
|
||||||
Custom the style of table rows:
|
Custom the style of table rows:
|
||||||
|
|
||||||
@@ -198,6 +215,7 @@ paginator({
|
|||||||
totalSize, // Total data size. It's necessary when remote is enabled
|
totalSize, // Total data size. It's necessary when remote is enabled
|
||||||
pageStartIndex: 0, // first page will be 0, default is 1
|
pageStartIndex: 0, // first page will be 0, default is 1
|
||||||
paginationSize: 3, // the pagination bar size, default is 5
|
paginationSize: 3, // the pagination bar size, default is 5
|
||||||
|
showTotal: true, // display pagination information
|
||||||
sizePerPageList: [ {
|
sizePerPageList: [ {
|
||||||
text: '5', value: 5
|
text: '5', value: 5
|
||||||
}, {
|
}, {
|
||||||
@@ -219,6 +237,7 @@ paginator({
|
|||||||
hidePageListOnlyOnePage: true, // hide pagination bar when only one page, default is false
|
hidePageListOnlyOnePage: true, // hide pagination bar when only one page, default is false
|
||||||
onPageChange: (page, sizePerPage) => {}, // callback function when page was changing
|
onPageChange: (page, sizePerPage) => {}, // callback function when page was changing
|
||||||
onSizePerPageChange: (sizePerPage, page) => {}, // callback function when page size was changing
|
onSizePerPageChange: (sizePerPage, page) => {}, // callback function when page size was changing
|
||||||
|
paginationTotalRenderer: (from, to, size) => { ... } // custom the pagination total
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
106
docs/columns.md
106
docs/columns.md
@@ -34,8 +34,14 @@ Available properties in a column object:
|
|||||||
* [editCellClasses](#editCellClasses)
|
* [editCellClasses](#editCellClasses)
|
||||||
* [editorStyle](#editorStyle)
|
* [editorStyle](#editorStyle)
|
||||||
* [editorClasses](#editorClasses)
|
* [editorClasses](#editorClasses)
|
||||||
|
* [editor](#editor)
|
||||||
|
* [editorRenderer](#editorRenderer)
|
||||||
* [filter](#filter)
|
* [filter](#filter)
|
||||||
* [filterValue](#filterValue)
|
* [filterValue](#filterValue)
|
||||||
|
* [csvType](#csvType)
|
||||||
|
* [csvFormatter](#csvFormatter)
|
||||||
|
* [csvText](#csvText)
|
||||||
|
* [csvExport](#csvExport)
|
||||||
|
|
||||||
Following is a most simplest and basic usage:
|
Following is a most simplest and basic usage:
|
||||||
|
|
||||||
@@ -89,6 +95,10 @@ dataField: 'address.city'
|
|||||||
* `rowIndex`
|
* `rowIndex`
|
||||||
* [`formatExtraData`](#formatExtraData)
|
* [`formatExtraData`](#formatExtraData)
|
||||||
|
|
||||||
|
> Attention:
|
||||||
|
> Don't use any state data or any external data in formatter function, please pass them via [`formatExtraData`](#formatExtraData).
|
||||||
|
> In addition, please make formatter function as pure function as possible as you can.
|
||||||
|
|
||||||
## <a name='headerFormatter'>column.headerFormatter - [Function]</a>
|
## <a name='headerFormatter'>column.headerFormatter - [Function]</a>
|
||||||
`headerFormatter` allow you to customize the header column and only accept a callback function which take three arguments and a JSX/String are expected for return.
|
`headerFormatter` allow you to customize the header column and only accept a callback function which take three arguments and a JSX/String are expected for return.
|
||||||
|
|
||||||
@@ -560,11 +570,94 @@ This is almost same as [`column.editCellStyle`](#editCellStyle), but `column.edi
|
|||||||
## <a name='editorClasses'>column.editorClasses - [Object | Function]</a>
|
## <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`).
|
This is almost same as [`column.editCellClasses`](#editCellClasses), but `column.editorClasses` is custom the class on editor instead of cell(`td`).
|
||||||
|
|
||||||
|
## <a name='editor'>column.editor - [Object]</a>
|
||||||
|
`column.editor` allow you to custom the type of cell editor by following predefined type:
|
||||||
|
|
||||||
|
* Text(Default)
|
||||||
|
* Dropdown
|
||||||
|
* Date
|
||||||
|
* Textarea
|
||||||
|
* Checkbox
|
||||||
|
|
||||||
|
Following is a quite example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
//...
|
||||||
|
, {
|
||||||
|
dataField: 'done',
|
||||||
|
text: 'Done',
|
||||||
|
editor: {
|
||||||
|
type: Type.CHECKBOX,
|
||||||
|
value: 'Y:N'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want more information, please check [here](https://github.com/react-bootstrap-table/react-bootstrap-table2/tree/master/packages/react-bootstrap-table2-editor).
|
||||||
|
|
||||||
|
## <a name='editorRenderer'>column.editorRenderer - [Function]</a>
|
||||||
|
If you feel above predefined editors are not satisfied to your requirement, you can totally custom the editor via `column.editorRenderer`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
|
||||||
|
|
||||||
|
// Custom Editor
|
||||||
|
class QualityRanger extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
value: PropTypes.number,
|
||||||
|
onUpdate: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
static defaultProps = {
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
getValue() {
|
||||||
|
return parseInt(this.range.value, 10);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { value, onUpdate, ...rest } = this.props;
|
||||||
|
return [
|
||||||
|
<input
|
||||||
|
{ ...rest }
|
||||||
|
key="range"
|
||||||
|
ref={ node => this.range = node }
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
/>,
|
||||||
|
<button
|
||||||
|
key="submit"
|
||||||
|
className="btn btn-default"
|
||||||
|
onClick={ () => onUpdate(this.getValue()) }
|
||||||
|
>
|
||||||
|
done
|
||||||
|
</button>
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
//...
|
||||||
|
, {
|
||||||
|
dataField: 'done',
|
||||||
|
text: 'Done',
|
||||||
|
editorRenderer: (editorProps, value, row, column, rowIndex, columnIndex) =>
|
||||||
|
<QualityRanger { ...editorProps } value={ value } />;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
## <a name='filter'>column.filter - [Object]</a>
|
## <a name='filter'>column.filter - [Object]</a>
|
||||||
Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters:
|
Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters:
|
||||||
|
|
||||||
* Text(`textFilter`)
|
* Text(`textFilter`)
|
||||||
* Select(`selectFilter`)
|
* Select(`selectFilter`)
|
||||||
|
* Number(`numberFilter`)
|
||||||
|
* Date(`dateFilter`)
|
||||||
|
|
||||||
We have a quick example to show you how to use `column.filter`:
|
We have a quick example to show you how to use `column.filter`:
|
||||||
|
|
||||||
@@ -601,3 +694,16 @@ A final `String` value you want to be filtered.
|
|||||||
filterValue: (cell, row) => owners[cell]
|
filterValue: (cell, row) => owners[cell]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## <a name='csvType'>column.csvType - [Object]</a>
|
||||||
|
Default is `String`. Currently, the available value is `String` and `Number`. If `Number` assigned, the cell value will not wrapped with double quote.
|
||||||
|
|
||||||
|
## <a name='csvFormatter'>column.csvFormatter - [Function]</a>
|
||||||
|
|
||||||
|
This is same as [`column.formatter`](#formatter). But `csvFormatter` only for CSV export and called when export CSV.
|
||||||
|
|
||||||
|
## <a name='csvText'>column.csvText - [String]</a>
|
||||||
|
Custom the CSV header cell, Default is [`column.text`](#text).
|
||||||
|
|
||||||
|
## <a name='csvExport'>column.csvExport - [Bool]</a>
|
||||||
|
Default is `true`, `false` will hide this column when export CSV.
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ Currently, **I still can't implement all the mainly features in legacy `react-bo
|
|||||||
* Pagination Addons
|
* Pagination Addons
|
||||||
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
|
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
|
||||||
* Overlay/Loading Addons
|
* Overlay/Loading Addons
|
||||||
|
* [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit)
|
||||||
|
* Table Toolkits, like search, csv etc.
|
||||||
|
|
||||||
This can help your application with less bundled size and also help `react-bootstrap-table2` have clean design to avoid handling to much logic in kernel module(SRP). Hence, which means you probably need to install above addons when you need specific features.
|
This can help your application with less bundled size and also help `react-bootstrap-table2` have clean design to avoid handling to much logic in kernel module(SRP). Hence, which means you probably need to install above addons when you need specific features.
|
||||||
|
|
||||||
@@ -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 [Work with selection](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/basic-row-select.html).
|
||||||
Please see [available selectRow configurations](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/row-select-props.html).
|
Please see [available selectRow configurations](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/row-select-props.html).
|
||||||
|
|
||||||
No huge change for row selection, but can not custom the selection column currently. Coming soon!!!
|
No huge change for row selection.
|
||||||
|
|
||||||
## Column Filter
|
## Column Filter
|
||||||
|
|
||||||
@@ -82,14 +84,14 @@ Please see [available filter configuration](https://react-bootstrap-table.github
|
|||||||
- [x] Text Filter
|
- [x] Text Filter
|
||||||
- [x] Custom Text Filter
|
- [x] Custom Text Filter
|
||||||
- [x] Remote Filter
|
- [x] Remote Filter
|
||||||
- [ ] Custom Filter Component
|
- [x] Custom Filter Component
|
||||||
- [ ] Regex Filter
|
- [ ] Regex Filter
|
||||||
- [x] Select Filter
|
- [x] Select Filter
|
||||||
- [x] Custom Select Filter
|
- [x] Custom Select Filter
|
||||||
- [X] Number Filter
|
- [X] Number Filter
|
||||||
- [ ] Date Filter
|
- [X] Date Filter
|
||||||
- [ ] Array Filter
|
- [X] Array Filter
|
||||||
- [ ] Programmatically Filter
|
- [X] Programmatically Filter
|
||||||
|
|
||||||
Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly.
|
Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly.
|
||||||
|
|
||||||
@@ -113,6 +115,34 @@ Remember to install [`react-bootstrap-table2-paginator`](https://www.npmjs.com/p
|
|||||||
|
|
||||||
No big changes for pagination, but still can't custom the pagination list, button and sizePerPage dropdown.
|
No big changes for pagination, but still can't custom the pagination list, button and sizePerPage dropdown.
|
||||||
|
|
||||||
|
## Table Search
|
||||||
|
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
|
## 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)
|
* [onSelect](#onSelect)
|
||||||
* [onSelectAll](#onSelectAll)
|
* [onSelectAll](#onSelectAll)
|
||||||
* [hideSelectColumn](#hideSelectColumn)
|
* [hideSelectColumn](#hideSelectColumn)
|
||||||
|
* [selectionRenderer](#selectionRenderer)
|
||||||
|
* [selectionHeaderRenderer](#selectionHeaderRenderer)
|
||||||
|
|
||||||
### <a name="mode">selectRow.mode - [String]</a>
|
### <a name="mode">selectRow.mode - [String]</a>
|
||||||
|
|
||||||
@@ -156,6 +158,34 @@ const selectRow = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <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',
|
||||||
|
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>
|
### <a name='onSelect'>selectRow.onSelect - [Function]</a>
|
||||||
This callback function will be called when a row is select/unselect and pass following three arguments:
|
This callback function will be called when a row is select/unselect and pass following three arguments:
|
||||||
`row`, `isSelect`, `rowIndex` and `e`.
|
`row`, `isSelect`, `rowIndex` and `e`.
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ const JS_PKGS = [
|
|||||||
'react-bootstrap-table2-editor',
|
'react-bootstrap-table2-editor',
|
||||||
'react-bootstrap-table2-filter',
|
'react-bootstrap-table2-filter',
|
||||||
'react-bootstrap-table2-overlay',
|
'react-bootstrap-table2-overlay',
|
||||||
'react-bootstrap-table2-paginator'
|
'react-bootstrap-table2-paginator',
|
||||||
|
'react-bootstrap-table2-toolkit'
|
||||||
].reduce((pkg, curr) => `${curr}|${pkg}`, '');
|
].reduce((pkg, curr) => `${curr}|${pkg}`, '');
|
||||||
|
|
||||||
const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`;
|
const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`;
|
||||||
@@ -25,7 +26,8 @@ const JS_SKIPS = `+(${TEST}|${LIB}|${DIST}|${NODE_MODULES})`;
|
|||||||
const STYLE_PKGS = [
|
const STYLE_PKGS = [
|
||||||
'react-bootstrap-table2',
|
'react-bootstrap-table2',
|
||||||
'react-bootstrap-table2-filter',
|
'react-bootstrap-table2-filter',
|
||||||
'react-bootstrap-table2-paginator'
|
'react-bootstrap-table2-paginator',
|
||||||
|
'react-bootstrap-table2-toolkit',
|
||||||
].reduce((pkg, curr) => `${curr}|${pkg}`, '');
|
].reduce((pkg, curr) => `${curr}|${pkg}`, '');
|
||||||
|
|
||||||
const STYLE_SKIPS = `+(${NODE_MODULES})`;
|
const STYLE_SKIPS = `+(${NODE_MODULES})`;
|
||||||
@@ -78,7 +80,8 @@ function umd(done) {
|
|||||||
() => gulp.src('./webpack/editor.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/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/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/paginator.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>'])),
|
||||||
|
() => gulp.src('./webpack/toolkit.umd.babel.js').pipe(shell(['webpack --config <%= file.path %>']))
|
||||||
)();
|
)();
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|||||||
17
package.json
17
package.json
@@ -11,10 +11,11 @@
|
|||||||
"pretest": "yarn lint --cache",
|
"pretest": "yarn lint --cache",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --coverage --watch",
|
||||||
"storybook": "cd ./packages/react-bootstrap-table2-example && yarn storybook",
|
"storybook": "cd ./packages/react-bootstrap-table2-example && yarn storybook",
|
||||||
"gh-pages:clean": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:clean",
|
"gh-pages:clean": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:clean",
|
||||||
"gh-pages:build": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:build"
|
"gh-pages:build": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:build",
|
||||||
|
"release": "yarn install && yarn build && lerna publish"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -49,8 +50,8 @@
|
|||||||
"babel-preset-stage-0": "6.24.1",
|
"babel-preset-stage-0": "6.24.1",
|
||||||
"babel-register": "6.24.1",
|
"babel-register": "6.24.1",
|
||||||
"css-loader": "0.28.1",
|
"css-loader": "0.28.1",
|
||||||
"enzyme": "3.1.1",
|
"enzyme": "3.3.0",
|
||||||
"enzyme-adapter-react-16": "1.0.4",
|
"enzyme-adapter-react-16": "1.1.1",
|
||||||
"eslint": "4.5.0",
|
"eslint": "4.5.0",
|
||||||
"eslint-config-airbnb": "15.1.0",
|
"eslint-config-airbnb": "15.1.0",
|
||||||
"eslint-loader": "1.9.0",
|
"eslint-loader": "1.9.0",
|
||||||
@@ -80,12 +81,14 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "2.2.5",
|
"classnames": "2.2.5",
|
||||||
"prop-types": "15.5.10",
|
"prop-types": "15.5.10",
|
||||||
"react": "16.0.0",
|
"react": "16.3.2",
|
||||||
"react-dom": "16.0.0"
|
"react-dom": "16.3.2",
|
||||||
|
"underscore": "1.9.1"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": [
|
||||||
"packages/**/*.js"
|
"packages/*/src/**/*.js",
|
||||||
|
"packages/*/index.js"
|
||||||
],
|
],
|
||||||
"roots": [
|
"roots": [
|
||||||
"<rootDir>/packages"
|
"<rootDir>/packages"
|
||||||
|
|||||||
@@ -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)
|
* Column Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as bool value)
|
||||||
* Cell Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as a callback function)
|
* Cell Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as a callback function)
|
||||||
|
|
||||||
|
## 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
|
## Customize Style/Class
|
||||||
### Editing Cell
|
### 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 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)
|
* 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 editingCellFactory from './src/editing-cell';
|
||||||
import {
|
import {
|
||||||
|
EDITTYPE,
|
||||||
CLICK_TO_CELL_EDIT,
|
CLICK_TO_CELL_EDIT,
|
||||||
DBCLICK_TO_CELL_EDIT,
|
DBCLICK_TO_CELL_EDIT,
|
||||||
DELAY_FOR_DBCLICK
|
DELAY_FOR_DBCLICK
|
||||||
} from './src/const';
|
} from './src/const';
|
||||||
|
|
||||||
export default (options = {}) => ({
|
export default (options = {}) => ({
|
||||||
wrapperFactory,
|
createContext,
|
||||||
editingCellFactory,
|
editingCellFactory,
|
||||||
CLICK_TO_CELL_EDIT,
|
CLICK_TO_CELL_EDIT,
|
||||||
DBCLICK_TO_CELL_EDIT,
|
DBCLICK_TO_CELL_EDIT,
|
||||||
DELAY_FOR_DBCLICK,
|
DELAY_FOR_DBCLICK,
|
||||||
options
|
options
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const Type = EDITTYPE;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table2-editor",
|
"name": "react-bootstrap-table2-editor",
|
||||||
"version": "0.1.5",
|
"version": "1.0.0",
|
||||||
"description": "it's the editor addon for react-bootstrap-table2",
|
"description": "it's the editor addon for react-bootstrap-table2",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prop-types": "^15.0.0",
|
"prop-types": "^15.0.0",
|
||||||
"react": "^16.0.0",
|
"react": "^16.3.0",
|
||||||
"react-dom": "^16.0.0"
|
"react-dom": "^16.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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 DELAY_FOR_DBCLICK = 200;
|
||||||
export const CLICK_TO_CELL_EDIT = 'click';
|
export const CLICK_TO_CELL_EDIT = 'click';
|
||||||
export const DBCLICK_TO_CELL_EDIT = 'dbclick';
|
export const DBCLICK_TO_CELL_EDIT = 'dbclick';
|
||||||
|
|
||||||
|
export const EDITTYPE = {
|
||||||
|
TEXT: 'text',
|
||||||
|
SELECT: 'select',
|
||||||
|
TEXTAREA: 'textarea',
|
||||||
|
CHECKBOX: 'checkbox',
|
||||||
|
DATE: 'date'
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
/* eslint react/prop-types: 0 */
|
/* eslint react/prop-types: 0 */
|
||||||
import React, { Component } from 'react';
|
/* eslint react/require-default-props: 0 */
|
||||||
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
|
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
|
||||||
|
|
||||||
export default (
|
export default (
|
||||||
Base,
|
_,
|
||||||
{ _, remoteResolver }
|
dataOperator,
|
||||||
|
isRemoteCellEdit,
|
||||||
|
handleCellChange
|
||||||
) => {
|
) => {
|
||||||
let EditingCell;
|
let EditingCell;
|
||||||
return class CellEditWrapper extends remoteResolver(Component) {
|
const CellEditContext = React.createContext();
|
||||||
|
|
||||||
|
class CellEditProvider extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
data: PropTypes.array.isRequired,
|
||||||
|
selectRow: PropTypes.object,
|
||||||
options: PropTypes.shape({
|
options: PropTypes.shape({
|
||||||
mode: PropTypes.oneOf([CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT]).isRequired,
|
mode: PropTypes.oneOf([CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT]).isRequired,
|
||||||
onErrorMessageDisappear: PropTypes.func,
|
onErrorMessageDisappear: PropTypes.func,
|
||||||
@@ -19,7 +25,7 @@ export default (
|
|||||||
afterSaveCell: PropTypes.func,
|
afterSaveCell: PropTypes.func,
|
||||||
nonEditableRows: PropTypes.func,
|
nonEditableRows: PropTypes.func,
|
||||||
timeToCloseMessage: PropTypes.number,
|
timeToCloseMessage: PropTypes.number,
|
||||||
errorMessage: PropTypes.string
|
errorMessage: PropTypes.any
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,41 +39,32 @@ export default (
|
|||||||
this.state = {
|
this.state = {
|
||||||
ridx: null,
|
ridx: null,
|
||||||
cidx: null,
|
cidx: null,
|
||||||
message: null,
|
message: null
|
||||||
isDataChanged: false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if (nextProps.cellEdit && this.isRemoteCellEdit()) {
|
if (nextProps.cellEdit && isRemoteCellEdit()) {
|
||||||
if (nextProps.cellEdit.options.errorMessage) {
|
if (nextProps.cellEdit.options.errorMessage) {
|
||||||
this.setState(() => ({
|
this.setState(() => ({
|
||||||
isDataChanged: false,
|
|
||||||
message: nextProps.cellEdit.options.errorMessage
|
message: nextProps.cellEdit.options.errorMessage
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
this.setState(() => ({
|
|
||||||
isDataChanged: true
|
|
||||||
}));
|
|
||||||
this.escapeEditing();
|
this.escapeEditing();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.setState(() => ({
|
|
||||||
isDataChanged: false
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCellUpdate(row, column, newValue) {
|
handleCellUpdate(row, column, newValue) {
|
||||||
const { keyField, cellEdit, store } = this.props;
|
const { keyField, cellEdit, data } = this.props;
|
||||||
const { beforeSaveCell, afterSaveCell } = cellEdit.options;
|
const { beforeSaveCell, afterSaveCell } = cellEdit.options;
|
||||||
const oldValue = _.get(row, column.dataField);
|
const oldValue = _.get(row, column.dataField);
|
||||||
const rowId = _.get(row, keyField);
|
const rowId = _.get(row, keyField);
|
||||||
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
|
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
|
||||||
if (this.isRemoteCellEdit()) {
|
if (isRemoteCellEdit()) {
|
||||||
this.handleCellChange(rowId, column.dataField, newValue);
|
handleCellChange(rowId, column.dataField, newValue);
|
||||||
} else {
|
} else {
|
||||||
store.edit(rowId, column.dataField, newValue);
|
dataOperator.editCell(data, keyField, rowId, column.dataField, newValue);
|
||||||
if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column);
|
if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column);
|
||||||
this.completeEditing();
|
this.completeEditing();
|
||||||
}
|
}
|
||||||
@@ -77,8 +74,7 @@ export default (
|
|||||||
this.setState(() => ({
|
this.setState(() => ({
|
||||||
ridx: null,
|
ridx: null,
|
||||||
cidx: null,
|
cidx: null,
|
||||||
message: null,
|
message: null
|
||||||
isDataChanged: true
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,8 +82,7 @@ export default (
|
|||||||
const editing = () => {
|
const editing = () => {
|
||||||
this.setState(() => ({
|
this.setState(() => ({
|
||||||
ridx,
|
ridx,
|
||||||
cidx,
|
cidx
|
||||||
isDataChanged: false
|
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -103,18 +98,19 @@ export default (
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isDataChanged, ...stateRest } = this.state;
|
|
||||||
const {
|
const {
|
||||||
cellEdit: {
|
cellEdit: {
|
||||||
options: { nonEditableRows, errorMessage, ...optionsRest },
|
options: { nonEditableRows, errorMessage, ...optionsRest },
|
||||||
editingCellFactory,
|
editingCellFactory,
|
||||||
|
createContext,
|
||||||
...cellEditRest
|
...cellEditRest
|
||||||
}
|
}
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const newCellEdit = {
|
const newCellEdit = {
|
||||||
...optionsRest,
|
...optionsRest,
|
||||||
...cellEditRest,
|
...cellEditRest,
|
||||||
...stateRest,
|
...this.state,
|
||||||
EditingCell,
|
EditingCell,
|
||||||
nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [],
|
nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [],
|
||||||
onStart: this.startEditing,
|
onStart: this.startEditing,
|
||||||
@@ -123,13 +119,16 @@ export default (
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Base
|
<CellEditContext.Provider
|
||||||
{ ...this.props }
|
value={ { cellEdit: newCellEdit } }
|
||||||
data={ this.props.store.data }
|
>
|
||||||
isDataChanged={ isDataChanged }
|
{ this.props.children }
|
||||||
cellEdit={ newCellEdit }
|
</CellEditContext.Provider>
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
Provider: CellEditProvider,
|
||||||
|
Consumer: CellEditContext.Consumer
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
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 cs from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import DropdownEditor from './dropdown-editor';
|
||||||
|
import TextAreaEditor from './textarea-editor';
|
||||||
|
import CheckBoxEditor from './checkbox-editor';
|
||||||
|
import DateEditor from './date-editor';
|
||||||
import TextEditor from './text-editor';
|
import TextEditor from './text-editor';
|
||||||
import EditorIndicator from './editor-indicator';
|
import EditorIndicator from './editor-indicator';
|
||||||
import { TIME_TO_CLOSE_MESSAGE } from './const';
|
import { TIME_TO_CLOSE_MESSAGE, EDITTYPE } from './const';
|
||||||
|
|
||||||
export default _ =>
|
export default _ =>
|
||||||
class EditingCell extends Component {
|
class EditingCell extends Component {
|
||||||
@@ -73,8 +77,8 @@ export default _ =>
|
|||||||
}, timeToCloseMessage);
|
}, timeToCloseMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeComplete(row, column, newValue) {
|
beforeComplete(newValue) {
|
||||||
const { onUpdate } = this.props;
|
const { onUpdate, row, column } = this.props;
|
||||||
if (_.isFunction(column.validator)) {
|
if (_.isFunction(column.validator)) {
|
||||||
const validateForm = column.validator(newValue, row, column);
|
const validateForm = column.validator(newValue, row, column);
|
||||||
if (_.isObject(validateForm) && !validateForm.valid) {
|
if (_.isObject(validateForm) && !validateForm.valid) {
|
||||||
@@ -89,28 +93,20 @@ export default _ =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleBlur() {
|
handleBlur() {
|
||||||
const { onEscape, blurToSave, row, column } = this.props;
|
const { onEscape, blurToSave } = this.props;
|
||||||
if (blurToSave) {
|
if (blurToSave) {
|
||||||
const value = this.editor.text.value;
|
this.beforeComplete(this.editor.getValue());
|
||||||
if (!_.isDefined(value)) {
|
|
||||||
// TODO: for other custom or embed editor
|
|
||||||
}
|
|
||||||
this.beforeComplete(row, column, value);
|
|
||||||
} else {
|
} else {
|
||||||
onEscape();
|
onEscape();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDown(e) {
|
handleKeyDown(e) {
|
||||||
const { onEscape, row, column } = this.props;
|
const { onEscape } = this.props;
|
||||||
if (e.keyCode === 27) { // ESC
|
if (e.keyCode === 27) { // ESC
|
||||||
onEscape();
|
onEscape();
|
||||||
} else if (e.keyCode === 13) { // ENTER
|
} else if (e.keyCode === 13) { // ENTER
|
||||||
const value = e.currentTarget.value;
|
this.beforeComplete(this.editor.getValue());
|
||||||
if (!_.isDefined(value)) {
|
|
||||||
// TODO: for other custom or embed editor
|
|
||||||
}
|
|
||||||
this.beforeComplete(row, column, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,17 +120,13 @@ export default _ =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { invalidMessage } = this.state;
|
let editor;
|
||||||
const { row, column, className, style, rowIndex, columnIndex } = this.props;
|
const { row, column, className, style, rowIndex, columnIndex } = this.props;
|
||||||
const { dataField } = column;
|
const { dataField } = column;
|
||||||
|
|
||||||
const value = _.get(row, dataField);
|
const value = _.get(row, dataField);
|
||||||
const editorAttrs = {
|
const hasError = _.isDefined(this.state.invalidMessage);
|
||||||
onKeyDown: this.handleKeyDown,
|
|
||||||
onBlur: this.handleBlur
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasError = _.isDefined(invalidMessage);
|
|
||||||
let customEditorClass = column.editorClasses || '';
|
let customEditorClass = column.editorClasses || '';
|
||||||
if (_.isFunction(column.editorClasses)) {
|
if (_.isFunction(column.editorClasses)) {
|
||||||
customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex);
|
customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex);
|
||||||
@@ -150,20 +142,51 @@ export default _ =>
|
|||||||
shake: hasError
|
shake: hasError
|
||||||
}, customEditorClass);
|
}, 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 (
|
return (
|
||||||
<td
|
<td
|
||||||
className={ cs('react-bootstrap-table-editing-cell', className) }
|
className={ cs('react-bootstrap-table-editing-cell', className) }
|
||||||
style={ style }
|
style={ style }
|
||||||
onClick={ this.handleClick }
|
onClick={ this.handleClick }
|
||||||
>
|
>
|
||||||
<TextEditor
|
{ editor }
|
||||||
ref={ node => this.editor = node }
|
{ hasError ? <EditorIndicator invalidMessage={ this.state.invalidMessage } /> : null }
|
||||||
defaultValue={ value }
|
|
||||||
style={ editorStyle }
|
|
||||||
className={ editorClass }
|
|
||||||
{ ...editorAttrs }
|
|
||||||
/>
|
|
||||||
{ hasError ? <EditorIndicator invalidMessage={ invalidMessage } /> : null }
|
|
||||||
</td>
|
</td>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ class TextEditor extends Component {
|
|||||||
this.text.focus();
|
this.text.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValue() {
|
||||||
|
return this.text.value;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { defaultValue, className, ...rest } = this.props;
|
const { defaultValue, className, ...rest } = this.props;
|
||||||
const editorClass = cs('form-control editor edit-text', className);
|
const editorClass = cs('form-control editor edit-text', className);
|
||||||
|
|||||||
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 _ from 'react-bootstrap-table-next/src/utils';
|
||||||
import editingCellFactory from '../src/editing-cell';
|
import editingCellFactory from '../src/editing-cell';
|
||||||
|
import * as constants from '../src/const';
|
||||||
import TextEditor from '../src/text-editor';
|
import TextEditor from '../src/text-editor';
|
||||||
|
import DateEditor from '../src/date-editor';
|
||||||
|
import DropDownEditor from '../src/dropdown-editor';
|
||||||
|
import TextAreaEditor from '../src/textarea-editor';
|
||||||
|
import CheckBoxEditor from '../src/checkbox-editor';
|
||||||
import EditorIndicator from '../src/editor-indicator';
|
import EditorIndicator from '../src/editor-indicator';
|
||||||
|
|
||||||
const EditingCell = editingCellFactory(_);
|
const EditingCell = editingCellFactory(_);
|
||||||
@@ -39,7 +44,7 @@ describe('EditingCell', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
onEscape = sinon.stub();
|
onEscape = sinon.stub();
|
||||||
onUpdate = sinon.stub();
|
onUpdate = sinon.stub();
|
||||||
wrapper = shallow(
|
wrapper = mount(
|
||||||
<EditingCell
|
<EditingCell
|
||||||
row={ row }
|
row={ row }
|
||||||
rowIndex={ rowIndex }
|
rowIndex={ rowIndex }
|
||||||
@@ -74,7 +79,8 @@ describe('EditingCell', () => {
|
|||||||
it('when press ENTER on TextEditor should call onUpdate correctly', () => {
|
it('when press ENTER on TextEditor should call onUpdate correctly', () => {
|
||||||
const newValue = 'test';
|
const newValue = 'test';
|
||||||
const textEditor = wrapper.find(TextEditor);
|
const textEditor = wrapper.find(TextEditor);
|
||||||
textEditor.simulate('keyDown', { keyCode: 13, currentTarget: { value: newValue } });
|
sinon.stub(textEditor.instance(), 'getValue').returns(newValue);
|
||||||
|
textEditor.simulate('keyDown', { keyCode: 13 });
|
||||||
expect(onUpdate.callCount).toBe(1);
|
expect(onUpdate.callCount).toBe(1);
|
||||||
expect(onUpdate.calledWith(row, column, newValue)).toBe(true);
|
expect(onUpdate.calledWith(row, column, newValue)).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -311,7 +317,7 @@ describe('EditingCell', () => {
|
|||||||
onEscape={ onEscape }
|
onEscape={ onEscape }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
wrapper.instance().beforeComplete(row, column, newValue);
|
wrapper.instance().beforeComplete(newValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call column.validator successfully', () => {
|
it('should call column.validator successfully', () => {
|
||||||
@@ -357,7 +363,17 @@ describe('EditingCell', () => {
|
|||||||
text: 'ID',
|
text: 'ID',
|
||||||
validator: validatorCallBack
|
validator: validatorCallBack
|
||||||
};
|
};
|
||||||
wrapper.instance().beforeComplete(row, column, newValue);
|
wrapper = mount(
|
||||||
|
<EditingCell
|
||||||
|
row={ row }
|
||||||
|
rowIndex={ rowIndex }
|
||||||
|
columnIndex={ columnIndex }
|
||||||
|
column={ column }
|
||||||
|
onUpdate={ onUpdate }
|
||||||
|
onEscape={ onEscape }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
wrapper.instance().beforeComplete(newValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call column.validator successfully', () => {
|
it('should call column.validator successfully', () => {
|
||||||
@@ -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>
|
<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 sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style');
|
||||||
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
|
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
|
||||||
const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style');
|
const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style');
|
||||||
|
const toolkitSourcePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/index.js');
|
||||||
const storyPath = path.join(__dirname, '../stories');
|
const storyPath = path.join(__dirname, '../stories');
|
||||||
const examplesPath = path.join(__dirname, '../examples');
|
const examplesPath = path.join(__dirname, '../examples');
|
||||||
const srcPath = path.join(__dirname, '../src');
|
const srcPath = path.join(__dirname, '../src');
|
||||||
@@ -23,6 +24,7 @@ const aliasPath = {
|
|||||||
'react-bootstrap-table2-filter': filterSourcePath,
|
'react-bootstrap-table2-filter': filterSourcePath,
|
||||||
'react-bootstrap-table2-overlay': overlaySourcePath,
|
'react-bootstrap-table2-overlay': overlaySourcePath,
|
||||||
'react-bootstrap-table2-paginator': paginationSourcePath,
|
'react-bootstrap-table2-paginator': paginationSourcePath,
|
||||||
|
'react-bootstrap-table2-toolkit': toolkitSourcePath
|
||||||
};
|
};
|
||||||
|
|
||||||
const loaders = [{
|
const loaders = [{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const columns = [{
|
|||||||
|
|
||||||
<BootstrapTable id="bar" keyField='id' data={ products } columns={ columns } />
|
<BootstrapTable id="bar" keyField='id' data={ products } columns={ columns } />
|
||||||
<BootstrapTable classes="foo" 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 () => (
|
export default () => (
|
||||||
@@ -43,6 +44,9 @@ export default () => (
|
|||||||
<h4> Customized table className </h4>
|
<h4> Customized table className </h4>
|
||||||
<BootstrapTable classes="foo" keyField="id" data={ products } columns={ columns } />
|
<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>
|
<Code>{ sourceCode }</Code>
|
||||||
</div>
|
</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 BootstrapTable from 'react-bootstrap-table-next';
|
||||||
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
|
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
|
||||||
import Code from 'components/common/code-block';
|
import Code from 'components/common/code-block';
|
||||||
import { jobsGenerator } from 'utils/common';
|
import { jobsGenerator1 } from 'utils/common';
|
||||||
|
|
||||||
const jobs = jobsGenerator(5);
|
const jobs = jobsGenerator1(5);
|
||||||
|
|
||||||
const owners = ['Allen', 'Bob', 'Cat'];
|
const owners = ['Allen', 'Bob', 'Cat'];
|
||||||
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
|
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
|
||||||
|
|||||||
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>
|
||||||
|
);
|
||||||
@@ -41,7 +41,7 @@ const columns = [{
|
|||||||
|
|
||||||
export default () => (
|
export default () => (
|
||||||
<div>
|
<div>
|
||||||
<h3>Try to hover on Product Name header column</h3>
|
<h3>Try to hover on Product ID Cell</h3>
|
||||||
<BootstrapTable keyField="id" data={ products } columns={ columns } />
|
<BootstrapTable keyField="id" data={ products } columns={ columns } />
|
||||||
<Code>{ sourceCode }</Code>
|
<Code>{ sourceCode }</Code>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
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>
|
||||||
|
);
|
||||||
@@ -24,6 +24,12 @@ import BootstrapTable from 'react-bootstrap-table-next';
|
|||||||
import paginationFactory from 'react-bootstrap-table2-paginator';
|
import paginationFactory from 'react-bootstrap-table2-paginator';
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
|
const customTotal = (from, to, size) => (
|
||||||
|
<span className="react-bootstrap-table-pagination-total">
|
||||||
|
Showing { from } to { to } of { size } Results
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
paginationSize: 4,
|
paginationSize: 4,
|
||||||
pageStartIndex: 0,
|
pageStartIndex: 0,
|
||||||
@@ -39,6 +45,8 @@ const options = {
|
|||||||
prePageTitle: 'Pre page',
|
prePageTitle: 'Pre page',
|
||||||
firstPageTitle: 'Next page',
|
firstPageTitle: 'Next page',
|
||||||
lastPageTitle: 'Last page',
|
lastPageTitle: 'Last page',
|
||||||
|
showTotal: true,
|
||||||
|
paginationTotalRenderer: customTotal,
|
||||||
sizePerPageList: [{
|
sizePerPageList: [{
|
||||||
text: '5', value: 5
|
text: '5', value: 5
|
||||||
}, {
|
}, {
|
||||||
@@ -50,11 +58,18 @@ const options = {
|
|||||||
|
|
||||||
<BootstrapTable keyField='id' data={ products } columns={ columns } pagination={ paginationFactory(options) } />
|
<BootstrapTable keyField='id' data={ products } columns={ columns } pagination={ paginationFactory(options) } />
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const customTotal = (from, to, size) => (
|
||||||
|
<span className="react-bootstrap-table-pagination-total">
|
||||||
|
Showing { from } to { to } of { size } Results
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
paginationSize: 4,
|
paginationSize: 4,
|
||||||
pageStartIndex: 0,
|
pageStartIndex: 0,
|
||||||
// alwaysShowAllBtns: true // Always show next and previous button
|
// alwaysShowAllBtns: true, // Always show next and previous button
|
||||||
// withFirstAndLast: false // Hide the going to First and Last page button
|
// withFirstAndLast: false, // Hide the going to First and Last page button
|
||||||
// hideSizePerPage: true, // Hide the sizePerPage dropdown always
|
// hideSizePerPage: true, // Hide the sizePerPage dropdown always
|
||||||
// hidePageListOnlyOnePage: true, // Hide the pagination list when only one page
|
// hidePageListOnlyOnePage: true, // Hide the pagination list when only one page
|
||||||
firstPageText: 'First',
|
firstPageText: 'First',
|
||||||
@@ -65,6 +80,8 @@ const options = {
|
|||||||
prePageTitle: 'Pre page',
|
prePageTitle: 'Pre page',
|
||||||
firstPageTitle: 'Next page',
|
firstPageTitle: 'Next page',
|
||||||
lastPageTitle: 'Last page',
|
lastPageTitle: 'Last page',
|
||||||
|
showTotal: true,
|
||||||
|
paginationTotalRenderer: customTotal,
|
||||||
sizePerPageList: [{
|
sizePerPageList: [{
|
||||||
text: '5', value: 5
|
text: '5', value: 5
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -4,53 +4,76 @@ import React from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import BootstrapTable from 'react-bootstrap-table-next';
|
import BootstrapTable from 'react-bootstrap-table-next';
|
||||||
import paginationFactory from 'react-bootstrap-table2-paginator';
|
import paginationFactory from 'react-bootstrap-table2-paginator';
|
||||||
|
import cellEditFactory from 'react-bootstrap-table2-editor';
|
||||||
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
|
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
|
||||||
import Code from 'components/common/code-block';
|
import Code from 'components/common/code-block';
|
||||||
import { productsGenerator } from 'utils/common';
|
import { productsGenerator } from 'utils/common';
|
||||||
|
|
||||||
const products = productsGenerator(87);
|
let products = productsGenerator(87);
|
||||||
|
|
||||||
const columns = [{
|
const columns = [{
|
||||||
dataField: 'id',
|
dataField: 'id',
|
||||||
text: 'Product ID'
|
text: 'Product ID',
|
||||||
|
sort: true
|
||||||
}, {
|
}, {
|
||||||
dataField: 'name',
|
dataField: 'name',
|
||||||
text: 'Product Name',
|
text: 'Product Name',
|
||||||
filter: textFilter()
|
filter: textFilter({
|
||||||
|
defaultValue: '8'
|
||||||
|
}),
|
||||||
|
sort: true
|
||||||
}, {
|
}, {
|
||||||
dataField: 'price',
|
dataField: 'price',
|
||||||
text: 'Product Price',
|
text: 'Product Price',
|
||||||
filter: textFilter()
|
filter: textFilter(),
|
||||||
|
sort: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const sourceCode = `\
|
const sourceCode = `\
|
||||||
import BootstrapTable from 'react-bootstrap-table-next';
|
import BootstrapTable from 'react-bootstrap-table-next';
|
||||||
import paginationFactory from 'react-bootstrap-table2-paginator';
|
import paginationFactory from 'react-bootstrap-table2-paginator';
|
||||||
|
import cellEditFactory from 'react-bootstrap-table2-editor';
|
||||||
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
|
import filterFactory, { textFilter, Comparator } from 'react-bootstrap-table2-filter';
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
const columns = [{
|
const columns = [{
|
||||||
dataField: 'id',
|
dataField: 'id',
|
||||||
text: 'Product ID'
|
text: 'Product ID',
|
||||||
|
sort: true
|
||||||
}, {
|
}, {
|
||||||
dataField: 'name',
|
dataField: 'name',
|
||||||
text: 'Product Name',
|
text: 'Product Name',
|
||||||
filter: textFilter()
|
filter: textFilter({
|
||||||
|
defaultValue: '8'
|
||||||
|
}),
|
||||||
|
sort: true
|
||||||
}, {
|
}, {
|
||||||
dataField: 'price',
|
dataField: 'price',
|
||||||
text: 'Product Price',
|
text: 'Product Price',
|
||||||
filter: textFilter()
|
filter: textFilter(),
|
||||||
|
sort: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
const defaultSorted = [{
|
||||||
|
dataField: 'name',
|
||||||
|
order: 'desc'
|
||||||
|
}];
|
||||||
|
|
||||||
|
const cellEditProps = {
|
||||||
|
mode: 'click'
|
||||||
|
};
|
||||||
|
|
||||||
const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
|
const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
|
||||||
<div>
|
<div>
|
||||||
<BootstrapTable
|
<BootstrapTable
|
||||||
remote={ { pagination: true } }
|
remote
|
||||||
keyField="id"
|
keyField="id"
|
||||||
data={ data }
|
data={ data }
|
||||||
columns={ columns }
|
columns={ columns }
|
||||||
|
defaultSorted={ defaultSorted }
|
||||||
filter={ filterFactory() }
|
filter={ filterFactory() }
|
||||||
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
|
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
|
||||||
|
cellEdit={ cellEditFactory(cellEditProps) }
|
||||||
onTableChange={ onTableChange }
|
onTableChange={ onTableChange }
|
||||||
/>
|
/>
|
||||||
<Code>{ sourceCode }</Code>
|
<Code>{ sourceCode }</Code>
|
||||||
@@ -77,10 +100,25 @@ class Container extends React.Component {
|
|||||||
this.handleTableChange = this.handleTableChange.bind(this);
|
this.handleTableChange = this.handleTableChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTableChange = (type, { page, sizePerPage, filters }) => {
|
handleTableChange = (type, { page, sizePerPage, filters, sortField, sortOrder, cellEdit }) => {
|
||||||
const currentIndex = (page - 1) * sizePerPage;
|
const currentIndex = (page - 1) * sizePerPage;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const result = products.filter((row) => {
|
// Handle cell editing
|
||||||
|
if (type === 'cellEdit') {
|
||||||
|
const { rowId, dataField, newValue } = cellEdit;
|
||||||
|
products = products.map((row) => {
|
||||||
|
if (row.id === rowId) {
|
||||||
|
const newRow = { ...row };
|
||||||
|
newRow[dataField] = newValue;
|
||||||
|
return newRow;
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let result = products;
|
||||||
|
|
||||||
|
// Handle column filters
|
||||||
|
result = result.filter((row) => {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
for (const dataField in filters) {
|
for (const dataField in filters) {
|
||||||
const { filterVal, filterType, comparator } = filters[dataField];
|
const { filterVal, filterType, comparator } = filters[dataField];
|
||||||
@@ -96,6 +134,26 @@ class Container extends React.Component {
|
|||||||
}
|
}
|
||||||
return valid;
|
return valid;
|
||||||
});
|
});
|
||||||
|
// Handle column sort
|
||||||
|
if (sortOrder === 'asc') {
|
||||||
|
result = result.sort((a, b) => {
|
||||||
|
if (a[sortField] > b[sortField]) {
|
||||||
|
return 1;
|
||||||
|
} else if (b[sortField] > a[sortField]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result = result.sort((a, b) => {
|
||||||
|
if (a[sortField] > b[sortField]) {
|
||||||
|
return -1;
|
||||||
|
} else if (b[sortField] > a[sortField]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
this.setState(() => ({
|
this.setState(() => ({
|
||||||
page,
|
page,
|
||||||
data: result.slice(currentIndex, currentIndex + sizePerPage),
|
data: result.slice(currentIndex, currentIndex + sizePerPage),
|
||||||
@@ -120,18 +178,29 @@ class Container extends React.Component {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const defaultSorted = [{
|
||||||
|
dataField: 'name',
|
||||||
|
order: 'desc'
|
||||||
|
}];
|
||||||
|
|
||||||
|
const cellEditProps = {
|
||||||
|
mode: 'click'
|
||||||
|
};
|
||||||
|
|
||||||
const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
|
const RemoteAll = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
|
||||||
<div>
|
<div>
|
||||||
<h3>When <code>remote.pagination</code> is enabled, the filtering,
|
<h3>When <code>remote.pagination</code> is enabled, the filtering,
|
||||||
sorting and searching will also change to remote mode automatically</h3>
|
sorting and searching will also change to remote mode automatically</h3>
|
||||||
<BootstrapTable
|
<BootstrapTable
|
||||||
remote={ { pagination: true } }
|
remote
|
||||||
keyField="id"
|
keyField="id"
|
||||||
data={ data }
|
data={ data }
|
||||||
columns={ columns }
|
columns={ columns }
|
||||||
|
defaultSorted={ defaultSorted }
|
||||||
filter={ filterFactory() }
|
filter={ filterFactory() }
|
||||||
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
|
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
|
||||||
onTableChange={ onTableChange }
|
onTableChange={ onTableChange }
|
||||||
|
cellEdit={ cellEditFactory(cellEditProps) }
|
||||||
/>
|
/>
|
||||||
<Code>{ sourceCode }</Code>
|
<Code>{ sourceCode }</Code>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,10 +226,24 @@ class Container extends React.Component {
|
|||||||
this.handleTableChange = this.handleTableChange.bind(this);
|
this.handleTableChange = this.handleTableChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTableChange = (type, { page, sizePerPage, filters }) => {
|
handleTableChange = (type, { page, sizePerPage, filters, sortField, sortOrder, cellEdit }) => {
|
||||||
const currentIndex = (page - 1) * sizePerPage;
|
const currentIndex = (page - 1) * sizePerPage;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const result = products.filter((row) => {
|
// Handle cell editing
|
||||||
|
if (type === 'cellEdit') {
|
||||||
|
const { rowId, dataField, newValue } = cellEdit;
|
||||||
|
products = products.map((row) => {
|
||||||
|
if (row.id === rowId) {
|
||||||
|
const newRow = { ...row };
|
||||||
|
newRow[dataField] = newValue;
|
||||||
|
return newRow;
|
||||||
|
}
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let result = products;
|
||||||
|
// Handle column filters
|
||||||
|
result = result.filter((row) => {
|
||||||
let valid = true;
|
let valid = true;
|
||||||
for (const dataField in filters) {
|
for (const dataField in filters) {
|
||||||
const { filterVal, filterType, comparator } = filters[dataField];
|
const { filterVal, filterType, comparator } = filters[dataField];
|
||||||
@@ -176,6 +259,26 @@ class Container extends React.Component {
|
|||||||
}
|
}
|
||||||
return valid;
|
return valid;
|
||||||
});
|
});
|
||||||
|
// Handle column sort
|
||||||
|
if (sortOrder === 'asc') {
|
||||||
|
result = result.sort((a, b) => {
|
||||||
|
if (a[sortField] > b[sortField]) {
|
||||||
|
return 1;
|
||||||
|
} else if (b[sortField] > a[sortField]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result = result.sort((a, b) => {
|
||||||
|
if (a[sortField] > b[sortField]) {
|
||||||
|
return -1;
|
||||||
|
} else if (b[sortField] > a[sortField]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
this.setState(() => ({
|
this.setState(() => ({
|
||||||
page,
|
page,
|
||||||
data: result.slice(currentIndex, currentIndex + sizePerPage),
|
data: result.slice(currentIndex, currentIndex + sizePerPage),
|
||||||
|
|||||||
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>
|
||||||
|
);
|
||||||
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",
|
"name": "react-bootstrap-table2-example",
|
||||||
"version": "0.1.6",
|
"version": "1.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prop-types": "^15.0.0",
|
"prop-types": "^15.0.0",
|
||||||
"react": "^15.0.0",
|
"react": "^16.3.0",
|
||||||
"react-dom": "^15.0.0"
|
"react-dom": "^116.3.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^3.3.7"
|
"bootstrap": "^3.3.7"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
/* eslint no-mixed-operators: 0 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* products generator for stories
|
* products generator for stories
|
||||||
*
|
*
|
||||||
@@ -27,12 +29,56 @@ export const productsQualityGenerator = (quantity = 5) =>
|
|||||||
quality: index % 3
|
quality: index % 3
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const jobType = ['A', 'B', 'C', 'D', 'E'];
|
||||||
|
|
||||||
|
const jobOwner = ['Allen', 'Bob', 'Cindy'];
|
||||||
|
|
||||||
export const jobsGenerator = (quantity = 5) =>
|
export const jobsGenerator = (quantity = 5) =>
|
||||||
Array.from({ length: quantity }, (value, index) => ({
|
Array.from({ length: quantity }, (value, index) => ({
|
||||||
id: index,
|
id: index,
|
||||||
name: `Job name ${index}`,
|
name: `Job name ${index}`,
|
||||||
owner: Math.floor(Math.random() * 3),
|
owner: jobOwner[Math.floor((Math.random() * 2) + 1)],
|
||||||
type: Math.floor(Math.random() * 5)
|
type: jobType[Math.floor((Math.random() * 4) + 1)]
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const jobsGenerator1 = (quantity = 5) =>
|
||||||
|
Array.from({ length: quantity }, (value, index) => ({
|
||||||
|
id: index,
|
||||||
|
name: `Job name ${index}`,
|
||||||
|
owner: Math.floor((Math.random() * 2) + 1),
|
||||||
|
type: Math.floor((Math.random() * 4) + 1)
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const todosGenerator = (quantity = 5) =>
|
||||||
|
Array.from({ length: quantity }, (value, index) => ({
|
||||||
|
id: index,
|
||||||
|
todo: `Todo item ${index}`,
|
||||||
|
done: Math.random() > 0.4 ? 'Y' : 'N'
|
||||||
|
}));
|
||||||
|
|
||||||
|
const startDate = new Date(2017, 0, 1);
|
||||||
|
const endDate = new Date();
|
||||||
|
|
||||||
|
export const stockGenerator = (quantity = 5) =>
|
||||||
|
Array.from({ length: quantity }, (value, index) => ({
|
||||||
|
id: index,
|
||||||
|
name: `Todo item ${index}`,
|
||||||
|
inStockDate:
|
||||||
|
new Date(startDate.getTime() + Math.random() * (endDate.getTime() - startDate.getTime()))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
export const productsExpandRowsGenerator = (quantity = 5, callback) => {
|
||||||
|
if (callback) return Array.from({ length: quantity }, callback);
|
||||||
|
|
||||||
|
// if no given callback, retrun default product format.
|
||||||
|
return (
|
||||||
|
Array.from({ length: quantity }, (value, index) => ({
|
||||||
|
id: index,
|
||||||
|
name: `Item name ${index}`,
|
||||||
|
price: 2100 + index,
|
||||||
|
expand: productsQualityGenerator(index)
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import StripHoverCondensedTable from 'examples/basic/striped-hover-condensed-tab
|
|||||||
import NoDataTable from 'examples/basic/no-data-table';
|
import NoDataTable from 'examples/basic/no-data-table';
|
||||||
import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
|
import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
|
||||||
import CaptionTable from 'examples/basic/caption-table';
|
import CaptionTable from 'examples/basic/caption-table';
|
||||||
|
import LargeTable from 'examples/basic/large-table';
|
||||||
|
|
||||||
// work on columns
|
// work on columns
|
||||||
import NestedDataTable from 'examples/columns/nested-data-table';
|
import NestedDataTable from 'examples/columns/nested-data-table';
|
||||||
@@ -33,6 +34,7 @@ import HeaderColumnEventTable from 'examples/header-columns/column-event-table';
|
|||||||
import HeaderColumnClassTable from 'examples/header-columns/column-class-table';
|
import HeaderColumnClassTable from 'examples/header-columns/column-class-table';
|
||||||
import HeaderColumnStyleTable from 'examples/header-columns/column-style-table';
|
import HeaderColumnStyleTable from 'examples/header-columns/column-style-table';
|
||||||
import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
|
import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
|
||||||
|
import HeaderClassTable from 'examples/header-columns/header-class-table';
|
||||||
|
|
||||||
// column filter
|
// column filter
|
||||||
import TextFilter from 'examples/column-filter/text-filter';
|
import TextFilter from 'examples/column-filter/text-filter';
|
||||||
@@ -45,12 +47,23 @@ import SelectFilter from 'examples/column-filter/select-filter';
|
|||||||
import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value';
|
import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value';
|
||||||
import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator';
|
import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator';
|
||||||
import CustomSelectFilter from 'examples/column-filter/custom-select-filter';
|
import CustomSelectFilter from 'examples/column-filter/custom-select-filter';
|
||||||
|
import MultiSelectFilter from 'examples/column-filter/multi-select-filter';
|
||||||
|
import MultiSelectFilterDefaultValue from 'examples/column-filter/multi-select-filter-default-value';
|
||||||
|
import CustomMultiSelectFilter from 'examples/column-filter/custom-multi-select-filter';
|
||||||
import NumberFilter from 'examples/column-filter/number-filter';
|
import NumberFilter from 'examples/column-filter/number-filter';
|
||||||
import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value';
|
import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value';
|
||||||
import CustomNumberFilter from 'examples/column-filter/custom-number-filter';
|
import CustomNumberFilter from 'examples/column-filter/custom-number-filter';
|
||||||
|
import DateFilter from 'examples/column-filter/date-filter';
|
||||||
|
import DateFilterWithDefaultValue from 'examples/column-filter/date-filter-default-value';
|
||||||
|
import CustomDateFilter from 'examples/column-filter/custom-date-filter';
|
||||||
import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-text-filter';
|
import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-text-filter';
|
||||||
import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter';
|
import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter';
|
||||||
import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter';
|
import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter';
|
||||||
|
import ProgrammaticallyDateFilter from 'examples/column-filter/programmatically-date-filter';
|
||||||
|
import ProgrammaticallyMultiSelectFilter from 'examples/column-filter/programmatically-multi-select-filter';
|
||||||
|
import CustomFilter from 'examples/column-filter/custom-filter';
|
||||||
|
import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter';
|
||||||
|
import ClearAllFilters from 'examples/column-filter/clear-all-filters';
|
||||||
|
|
||||||
// work on rows
|
// work on rows
|
||||||
import RowStyleTable from 'examples/rows/row-style';
|
import RowStyleTable from 'examples/rows/row-style';
|
||||||
@@ -79,6 +92,11 @@ import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table';
|
|||||||
import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
|
import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
|
||||||
import EditorStyleTable from 'examples/cell-edit/editor-style-table';
|
import EditorStyleTable from 'examples/cell-edit/editor-style-table';
|
||||||
import EditorClassTable from 'examples/cell-edit/editor-class-table';
|
import EditorClassTable from 'examples/cell-edit/editor-class-table';
|
||||||
|
import DropdownEditorTable from 'examples/cell-edit/dropdown-editor-table';
|
||||||
|
import TextareaEditorTable from 'examples/cell-edit/textarea-editor-table';
|
||||||
|
import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table';
|
||||||
|
import DateEditorTable from 'examples/cell-edit/date-editor-table';
|
||||||
|
import CustomEditorTable from 'examples/cell-edit/custom-editor-table';
|
||||||
|
|
||||||
// work on row selection
|
// work on row selection
|
||||||
import SingleSelectionTable from 'examples/row-selection/single-selection';
|
import SingleSelectionTable from 'examples/row-selection/single-selection';
|
||||||
@@ -90,16 +108,41 @@ import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-sele
|
|||||||
import SelectionNoDataTable from 'examples/row-selection/selection-no-data';
|
import SelectionNoDataTable from 'examples/row-selection/selection-no-data';
|
||||||
import SelectionStyleTable from 'examples/row-selection/selection-style';
|
import SelectionStyleTable from 'examples/row-selection/selection-style';
|
||||||
import SelectionClassTable from 'examples/row-selection/selection-class';
|
import SelectionClassTable from 'examples/row-selection/selection-class';
|
||||||
|
import CustomSelectionTable from 'examples/row-selection/custom-selection';
|
||||||
import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows';
|
import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows';
|
||||||
import SelectionBgColorTable from 'examples/row-selection/selection-bgcolor';
|
import SelectionBgColorTable from 'examples/row-selection/selection-bgcolor';
|
||||||
import SelectionHooks from 'examples/row-selection/selection-hooks';
|
import SelectionHooks from 'examples/row-selection/selection-hooks';
|
||||||
import HideSelectionColumnTable from 'examples/row-selection/hide-selection-column';
|
import HideSelectionColumnTable from 'examples/row-selection/hide-selection-column';
|
||||||
|
|
||||||
|
// work on row expand
|
||||||
|
import BasicRowExpand from 'examples/row-expand';
|
||||||
|
import RowExpandManagement from 'examples/row-expand/expand-management';
|
||||||
|
import NonExpandableRows from 'examples/row-expand/non-expandable-rows';
|
||||||
|
import ExpandColumn from 'examples/row-expand/expand-column';
|
||||||
|
import CustomExpandColumn from 'examples/row-expand/custom-expand-column';
|
||||||
|
import ExpandHooks from 'examples/row-expand/expand-hooks';
|
||||||
|
|
||||||
// pagination
|
// pagination
|
||||||
import PaginationTable from 'examples/pagination';
|
import PaginationTable from 'examples/pagination';
|
||||||
import PaginationHooksTable from 'examples/pagination/pagination-hooks';
|
import PaginationHooksTable from 'examples/pagination/pagination-hooks';
|
||||||
import CustomPaginationTable from 'examples/pagination/custom-pagination';
|
import CustomPaginationTable from 'examples/pagination/custom-pagination';
|
||||||
|
|
||||||
|
// search
|
||||||
|
import SearchTable from 'examples/search';
|
||||||
|
import DefaultCustomSearch from 'examples/search/default-custom-search';
|
||||||
|
import FullyCustomSearch from 'examples/search/fully-custom-search';
|
||||||
|
import SearchFormattedData from 'examples/search/search-formatted';
|
||||||
|
import CustomSearchValue from 'examples/search/custom-search-value';
|
||||||
|
|
||||||
|
// CSV
|
||||||
|
import ExportCSV from 'examples/csv';
|
||||||
|
import CSVFormatter from 'examples/csv/csv-column-formatter';
|
||||||
|
import CustomCSVHeader from 'examples/csv/custom-csv-header';
|
||||||
|
import HideCSVColumn from 'examples/csv/hide-column';
|
||||||
|
import CSVColumnType from 'examples/csv/csv-column-type';
|
||||||
|
import CustomCSVButton from 'examples/csv/custom-csv-button';
|
||||||
|
import CustomCSV from 'examples/csv/custom-csv';
|
||||||
|
|
||||||
// loading overlay
|
// loading overlay
|
||||||
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
|
import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay';
|
||||||
import TableOverlay from 'examples/loading-overlay/table-overlay';
|
import TableOverlay from 'examples/loading-overlay/table-overlay';
|
||||||
@@ -108,6 +151,7 @@ import TableOverlay from 'examples/loading-overlay/table-overlay';
|
|||||||
import RemoteSort from 'examples/remote/remote-sort';
|
import RemoteSort from 'examples/remote/remote-sort';
|
||||||
import RemoteFilter from 'examples/remote/remote-filter';
|
import RemoteFilter from 'examples/remote/remote-filter';
|
||||||
import RemotePaginationTable from 'examples/remote/remote-pagination';
|
import RemotePaginationTable from 'examples/remote/remote-pagination';
|
||||||
|
import RemoteSearch from 'examples/remote/remote-search';
|
||||||
import RemoteCellEdit from 'examples/remote/remote-celledit';
|
import RemoteCellEdit from 'examples/remote/remote-celledit';
|
||||||
import RemoteAll from 'examples/remote/remote-all';
|
import RemoteAll from 'examples/remote/remote-all';
|
||||||
|
|
||||||
@@ -131,7 +175,8 @@ storiesOf('Basic Table', module)
|
|||||||
.add('borderless table', () => <BorderlessTable />)
|
.add('borderless table', () => <BorderlessTable />)
|
||||||
.add('Indication For Empty Table', () => <NoDataTable />)
|
.add('Indication For Empty Table', () => <NoDataTable />)
|
||||||
.add('Customized id and class table', () => <CustomizedIdClassesTable />)
|
.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)
|
storiesOf('Work on Columns', module)
|
||||||
.add('Display Nested Data', () => <NestedDataTable />)
|
.add('Display Nested Data', () => <NestedDataTable />)
|
||||||
@@ -153,7 +198,8 @@ storiesOf('Work on Header Columns', module)
|
|||||||
.add('Column Event', () => <HeaderColumnEventTable />)
|
.add('Column Event', () => <HeaderColumnEventTable />)
|
||||||
.add('Customize Column Class', () => <HeaderColumnClassTable />)
|
.add('Customize Column Class', () => <HeaderColumnClassTable />)
|
||||||
.add('Customize Column Style', () => <HeaderColumnStyleTable />)
|
.add('Customize Column Style', () => <HeaderColumnStyleTable />)
|
||||||
.add('Customize Column HTML attribute', () => <HeaderColumnAttrsTable />);
|
.add('Customize Column HTML attribute', () => <HeaderColumnAttrsTable />)
|
||||||
|
.add('Header Class', () => <HeaderClassTable />);
|
||||||
|
|
||||||
storiesOf('Column Filter', module)
|
storiesOf('Column Filter', module)
|
||||||
.add('Text Filter', () => <TextFilter />)
|
.add('Text Filter', () => <TextFilter />)
|
||||||
@@ -164,15 +210,26 @@ storiesOf('Column Filter', module)
|
|||||||
.add('Select Filter', () => <SelectFilter />)
|
.add('Select Filter', () => <SelectFilter />)
|
||||||
.add('Select Filter with Default Value', () => <SelectFilterWithDefaultValue />)
|
.add('Select Filter with Default Value', () => <SelectFilterWithDefaultValue />)
|
||||||
.add('Select Filter with Comparator', () => <SelectFilterComparator />)
|
.add('Select Filter with Comparator', () => <SelectFilterComparator />)
|
||||||
|
.add('MultiSelect Filter', () => <MultiSelectFilter />)
|
||||||
|
.add('MultiSelect Filter with Default Value', () => <MultiSelectFilterDefaultValue />)
|
||||||
.add('Number Filter', () => <NumberFilter />)
|
.add('Number Filter', () => <NumberFilter />)
|
||||||
.add('Number Filter with Default Value', () => <NumberFilterWithDefaultValue />)
|
.add('Number Filter with Default Value', () => <NumberFilterWithDefaultValue />)
|
||||||
|
.add('Date Filter', () => <DateFilter />)
|
||||||
|
.add('Date Filter with Default Value', () => <DateFilterWithDefaultValue />)
|
||||||
.add('Custom Text Filter', () => <CustomTextFilter />)
|
.add('Custom Text Filter', () => <CustomTextFilter />)
|
||||||
.add('Custom Select Filter', () => <CustomSelectFilter />)
|
.add('Custom Select Filter', () => <CustomSelectFilter />)
|
||||||
.add('Custom Number Filter', () => <CustomNumberFilter />)
|
.add('Custom Number Filter', () => <CustomNumberFilter />)
|
||||||
|
.add('Custom Date Filter', () => <CustomDateFilter />)
|
||||||
|
.add('Custom MultiSelect Filter', () => <CustomMultiSelectFilter />)
|
||||||
.add('Custom Filter Value', () => <CustomFilterValue />)
|
.add('Custom Filter Value', () => <CustomFilterValue />)
|
||||||
.add('Programmatically Text Filter ', () => <ProgrammaticallyTextFilter />)
|
.add('Programmatically Text Filter', () => <ProgrammaticallyTextFilter />)
|
||||||
.add('Programmatically Select Filter ', () => <ProgrammaticallySelectFilter />)
|
.add('Programmatically Select Filter', () => <ProgrammaticallySelectFilter />)
|
||||||
.add('Programmatically Number Filter ', () => <ProgrammaticallyNumberFilter />);
|
.add('Programmatically Number Filter', () => <ProgrammaticallyNumberFilter />)
|
||||||
|
.add('Programmatically Date Filter', () => <ProgrammaticallyDateFilter />)
|
||||||
|
.add('Programmatically Multi Select Filter', () => <ProgrammaticallyMultiSelectFilter />)
|
||||||
|
.add('Custom Filter', () => <CustomFilter />)
|
||||||
|
.add('Advance Custom Filter', () => <AdvanceCustomFilter />)
|
||||||
|
.add('Clear All Filters', () => <ClearAllFilters />);
|
||||||
|
|
||||||
storiesOf('Work on Rows', module)
|
storiesOf('Work on Rows', module)
|
||||||
.add('Customize Row Style', () => <RowStyleTable />)
|
.add('Customize Row Style', () => <RowStyleTable />)
|
||||||
@@ -200,7 +257,12 @@ storiesOf('Cell Editing', module)
|
|||||||
.add('Custom Cell Style', () => <CellEditStyleTable />)
|
.add('Custom Cell Style', () => <CellEditStyleTable />)
|
||||||
.add('Custom Cell Classes', () => <CellEditClassTable />)
|
.add('Custom Cell Classes', () => <CellEditClassTable />)
|
||||||
.add('Custom Editor Classes', () => <EditorClassTable />)
|
.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)
|
storiesOf('Row Selection', module)
|
||||||
.add('Single Selection', () => <SingleSelectionTable />)
|
.add('Single Selection', () => <SingleSelectionTable />)
|
||||||
@@ -212,16 +274,41 @@ storiesOf('Row Selection', module)
|
|||||||
.add('Selection without Data', () => <SelectionNoDataTable />)
|
.add('Selection without Data', () => <SelectionNoDataTable />)
|
||||||
.add('Selection Style', () => <SelectionStyleTable />)
|
.add('Selection Style', () => <SelectionStyleTable />)
|
||||||
.add('Selection Class', () => <SelectionClassTable />)
|
.add('Selection Class', () => <SelectionClassTable />)
|
||||||
|
.add('Custom Selection', () => <CustomSelectionTable />)
|
||||||
.add('Selection Background Color', () => <SelectionBgColorTable />)
|
.add('Selection Background Color', () => <SelectionBgColorTable />)
|
||||||
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />)
|
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />)
|
||||||
.add('Selection Hooks', () => <SelectionHooks />)
|
.add('Selection Hooks', () => <SelectionHooks />)
|
||||||
.add('Hide Selection Column', () => <HideSelectionColumnTable />);
|
.add('Hide Selection Column', () => <HideSelectionColumnTable />);
|
||||||
|
|
||||||
|
storiesOf('Row Expand', module)
|
||||||
|
.add('Basic Row Expand', () => <BasicRowExpand />)
|
||||||
|
.add('Expand Management', () => <RowExpandManagement />)
|
||||||
|
.add('Non Expandabled Rows', () => <NonExpandableRows />)
|
||||||
|
.add('Expand Indicator', () => <ExpandColumn />)
|
||||||
|
.add('Custom Expand Indicator', () => <CustomExpandColumn />)
|
||||||
|
.add('Expand Hooks', () => <ExpandHooks />);
|
||||||
|
|
||||||
storiesOf('Pagination', module)
|
storiesOf('Pagination', module)
|
||||||
.add('Basic Pagination Table', () => <PaginationTable />)
|
.add('Basic Pagination Table', () => <PaginationTable />)
|
||||||
.add('Pagination Hooks', () => <PaginationHooksTable />)
|
.add('Pagination Hooks', () => <PaginationHooksTable />)
|
||||||
.add('Custom Pagination', () => <CustomPaginationTable />);
|
.add('Custom Pagination', () => <CustomPaginationTable />);
|
||||||
|
|
||||||
|
storiesOf('Table Search', module)
|
||||||
|
.add('Basic Search Table', () => <SearchTable />)
|
||||||
|
.add('Default Custom Search', () => <DefaultCustomSearch />)
|
||||||
|
.add('Fully Custom Search', () => <FullyCustomSearch />)
|
||||||
|
.add('Search Fromatted Value', () => <SearchFormattedData />)
|
||||||
|
.add('Custom Search Value', () => <CustomSearchValue />);
|
||||||
|
|
||||||
|
storiesOf('Export CSV', module)
|
||||||
|
.add('Basic Export CSV', () => <ExportCSV />)
|
||||||
|
.add('Format CSV Column', () => <CSVFormatter />)
|
||||||
|
.add('Custom CSV Header', () => <CustomCSVHeader />)
|
||||||
|
.add('Hide CSV Column', () => <HideCSVColumn />)
|
||||||
|
.add('CSV Column Type', () => <CSVColumnType />)
|
||||||
|
.add('Custom CSV Button', () => <CustomCSVButton />)
|
||||||
|
.add('Custom CSV', () => <CustomCSV />);
|
||||||
|
|
||||||
storiesOf('EmptyTableOverlay', module)
|
storiesOf('EmptyTableOverlay', module)
|
||||||
.add('Empty Table Overlay', () => <EmptyTableOverlay />)
|
.add('Empty Table Overlay', () => <EmptyTableOverlay />)
|
||||||
.add('Table Overlay', () => <TableOverlay />);
|
.add('Table Overlay', () => <TableOverlay />);
|
||||||
@@ -230,5 +317,6 @@ storiesOf('Remote', module)
|
|||||||
.add('Remote Sort', () => <RemoteSort />)
|
.add('Remote Sort', () => <RemoteSort />)
|
||||||
.add('Remote Filter', () => <RemoteFilter />)
|
.add('Remote Filter', () => <RemoteFilter />)
|
||||||
.add('Remote Pagination', () => <RemotePaginationTable />)
|
.add('Remote Pagination', () => <RemotePaginationTable />)
|
||||||
|
.add('Remote Search', () => <RemoteSearch />)
|
||||||
.add('Remote Cell Editing', () => <RemoteCellEdit />)
|
.add('Remote Cell Editing', () => <RemoteCellEdit />)
|
||||||
.add('Remote All', () => <RemoteAll />);
|
.add('Remote All', () => <RemoteAll />);
|
||||||
|
|||||||
@@ -5,3 +5,7 @@ table.foo {
|
|||||||
table#bar {
|
table#bar {
|
||||||
background-color: $light-blue;
|
background-color: $light-blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.boo {
|
||||||
|
border: 2px solid salmon;
|
||||||
|
}
|
||||||
@@ -10,3 +10,7 @@
|
|||||||
.demo-row-odd {
|
.demo-row-odd {
|
||||||
background-color: $green-lighten-4;
|
background-color: $green-lighten-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-class {
|
||||||
|
background-color: $green-lighten-4;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.custome-search-field {
|
||||||
|
background-color: #c8e6c9;
|
||||||
|
}
|
||||||
@@ -10,4 +10,5 @@
|
|||||||
@import "row-selection/index";
|
@import "row-selection/index";
|
||||||
@import "rows/index";
|
@import "rows/index";
|
||||||
@import "sort/index";
|
@import "sort/index";
|
||||||
|
@import "search/index";
|
||||||
@import "loading-overlay/index";
|
@import "loading-overlay/index";
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ You can get all types of filters via import and these filters are a factory func
|
|||||||
|
|
||||||
* TextFilter
|
* TextFilter
|
||||||
* SelectFilter
|
* SelectFilter
|
||||||
|
* MultiSelectFilter
|
||||||
* NumberFilter
|
* NumberFilter
|
||||||
|
* DateFilter
|
||||||
|
* CustomFilter
|
||||||
* **Coming soon!**
|
* **Coming soon!**
|
||||||
|
|
||||||
## Add CSS
|
## Add CSS
|
||||||
@@ -112,6 +115,52 @@ const qualityFilter = selectFilter({
|
|||||||
// omit...
|
// omit...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## MultiSelect Filter
|
||||||
|
|
||||||
|
A quick example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
|
||||||
|
|
||||||
|
// omit...
|
||||||
|
const selectOptions = {
|
||||||
|
0: 'good',
|
||||||
|
1: 'Bad',
|
||||||
|
2: 'unknown'
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
..., {
|
||||||
|
dataField: 'quality',
|
||||||
|
text: 'Product Quailty',
|
||||||
|
formatter: cell => selectOptions[cell],
|
||||||
|
filter: multiSelectFilter({
|
||||||
|
options: selectOptions
|
||||||
|
})
|
||||||
|
}];
|
||||||
|
|
||||||
|
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
|
||||||
|
```
|
||||||
|
|
||||||
|
Following is an example for custom select filter:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import filterFactory, { multiSelectFilter, Comparator } from 'react-bootstrap-table2-filter';
|
||||||
|
// omit...
|
||||||
|
|
||||||
|
const qualityFilter = multiSelectFilter({
|
||||||
|
options: selectOptions,
|
||||||
|
placeholder: 'My Custom PlaceHolder', // custom the input placeholder
|
||||||
|
className: 'my-custom-text-filter', // custom classname on input
|
||||||
|
defaultValue: '2', // default filtering value
|
||||||
|
comparator: Comparator.LIKE, // default is Comparator.EQ
|
||||||
|
style: { ... }, // your custom styles on input
|
||||||
|
withoutEmptyOption: true // hide the default select option
|
||||||
|
});
|
||||||
|
|
||||||
|
// omit...
|
||||||
|
```
|
||||||
|
|
||||||
## Number Filter
|
## Number Filter
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -129,7 +178,7 @@ const columns = [..., {
|
|||||||
Numner filter is same as other filter, you can custom the number filter via `numberFilter` factory function:
|
Numner filter is same as other filter, you can custom the number filter via `numberFilter` factory function:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import filterFactory, { selectFilter, Comparator } from 'react-bootstrap-table2-filter';
|
import filterFactory, { selectFilter, Comparator, numberFilter } from 'react-bootstrap-table2-filter';
|
||||||
// omit...
|
// omit...
|
||||||
|
|
||||||
const numberFilter = numberFilter({
|
const numberFilter = numberFilter({
|
||||||
@@ -150,3 +199,92 @@ const numberFilter = numberFilter({
|
|||||||
|
|
||||||
// omit...
|
// omit...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Date Filter
|
||||||
|
|
||||||
|
```js
|
||||||
|
import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter';
|
||||||
|
|
||||||
|
const columns = [..., {
|
||||||
|
dataField: 'date',
|
||||||
|
text: 'Product date',
|
||||||
|
filter: dateFilter()
|
||||||
|
}];
|
||||||
|
|
||||||
|
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Notes:** date filter accept a Javascript Date object in your raw data and you have to use `column.formatter` to make it as your prefer string result
|
||||||
|
|
||||||
|
Date filter is same as other filter, you can custom the date filter via `dateFilter` factory function:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import filterFactory, { selectFilter, Comparator } from 'react-bootstrap-table2-filter';
|
||||||
|
// omit...
|
||||||
|
|
||||||
|
const dateFilter = dateFilter({
|
||||||
|
delay: 600, // how long will trigger filtering after user typing, default is 500 ms
|
||||||
|
placeholder: 'custom placeholder', // placeholder for date input
|
||||||
|
withoutEmptyComparatorOption: true, // dont render empty option for comparator
|
||||||
|
comparators: [Comparator.EQ, Comparator.GT, Comparator.LT], // Custom the comparators
|
||||||
|
style: { display: 'inline-grid' }, // custom the style on date filter
|
||||||
|
className: 'custom-dateFilter-class', // custom the class on date filter
|
||||||
|
comparatorStyle: { backgroundColor: 'antiquewhite' }, // custom the style on comparator select
|
||||||
|
comparatorClassName: 'custom-comparator-class', // custom the class on comparator select
|
||||||
|
dateStyle: { backgroundColor: 'cadetblue', margin: '0px' }, // custom the style on date input
|
||||||
|
dateClassName: 'custom-date-class', // custom the class on date input
|
||||||
|
defaultValue: { date: new Date(2018, 0, 1), comparator: Comparator.GT } // default value
|
||||||
|
})
|
||||||
|
|
||||||
|
// omit...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Filter
|
||||||
|
|
||||||
|
```js
|
||||||
|
import filterFactory, { customFilter } from 'react-bootstrap-table2-filter';
|
||||||
|
|
||||||
|
const columns = [..., {
|
||||||
|
dataField: 'date',
|
||||||
|
text: 'Product Name',
|
||||||
|
filter: customFilter(),
|
||||||
|
filterRenderer: (onFilter, column) => .....
|
||||||
|
}];
|
||||||
|
|
||||||
|
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
|
||||||
|
```
|
||||||
|
|
||||||
|
In custom filter case, you are suppose to finish following two steps:
|
||||||
|
1. Call `customFilter` and pass to `column.filter`
|
||||||
|
2. Give `column.filterRenderer` as a callback function and return your custom filter element.
|
||||||
|
|
||||||
|
### column.filterRenderer
|
||||||
|
|
||||||
|
This function will pass two argument to you:
|
||||||
|
1. `onFilter`: call it to trigger filter when you need.
|
||||||
|
2. `column`: Just the column object!
|
||||||
|
|
||||||
|
In the end, please remember to return your custom filter element!
|
||||||
|
|
||||||
|
### customFilter
|
||||||
|
|
||||||
|
`customFilter` function just same as `textFilter`, `selectFilter` etc, it is for customization reason. However, in the custom filter case, there's only one props is valid: `type`
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
import filterFactory, { FILTER_TYPES } from 'react-bootstrap-table2-filter';
|
||||||
|
|
||||||
|
const customFilter = customFilter({
|
||||||
|
type: FILTER_TYPES.NUMBER, // default is FILTER_TYPES.TEXT
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
`type` is a way to ask `react-bootstrap-table` to filter you data as number, select, date or normal text.
|
||||||
|
|
||||||
|
### FILTER_TYPES
|
||||||
|
|
||||||
|
Following properties is valid in `FILTER_TYPES`:
|
||||||
|
* TEXT
|
||||||
|
* SELECT
|
||||||
|
* NUMBER
|
||||||
|
* DATE
|
||||||
|
|||||||
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 TextFilter from './src/components/text';
|
||||||
import SelectFilter from './src/components/select';
|
import SelectFilter from './src/components/select';
|
||||||
|
import MultiSelectFilter from './src/components/multiselect';
|
||||||
import NumberFilter from './src/components/number';
|
import NumberFilter from './src/components/number';
|
||||||
import wrapperFactory from './src/wrapper';
|
import DateFilter from './src/components/date';
|
||||||
|
import createContext from './src/context';
|
||||||
import * as Comparison from './src/comparison';
|
import * as Comparison from './src/comparison';
|
||||||
|
import { FILTER_TYPE } from './src/const';
|
||||||
|
|
||||||
export default (options = {}) => ({
|
export default (options = {}) => ({
|
||||||
wrapperFactory,
|
createContext,
|
||||||
options
|
options
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const FILTER_TYPES = FILTER_TYPE;
|
||||||
|
|
||||||
export const Comparator = Comparison;
|
export const Comparator = Comparison;
|
||||||
|
|
||||||
export const textFilter = (props = {}) => ({
|
export const textFilter = (props = {}) => ({
|
||||||
@@ -21,7 +26,21 @@ export const selectFilter = (props = {}) => ({
|
|||||||
props
|
props
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const multiSelectFilter = (props = {}) => ({
|
||||||
|
Filter: MultiSelectFilter,
|
||||||
|
props
|
||||||
|
});
|
||||||
|
|
||||||
export const numberFilter = (props = {}) => ({
|
export const numberFilter = (props = {}) => ({
|
||||||
Filter: NumberFilter,
|
Filter: NumberFilter,
|
||||||
props
|
props
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const dateFilter = (props = {}) => ({
|
||||||
|
Filter: DateFilter,
|
||||||
|
props
|
||||||
|
});
|
||||||
|
|
||||||
|
export const customFilter = (props = {}) => ({
|
||||||
|
props
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table2-filter",
|
"name": "react-bootstrap-table2-filter",
|
||||||
"version": "0.1.6",
|
"version": "1.0.0",
|
||||||
"description": "it's a column filter addon for react-bootstrap-table2",
|
"description": "it's a column filter addon for react-bootstrap-table2",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prop-types": "^15.0.0",
|
"prop-types": "^15.0.0",
|
||||||
"react": "^16.0.0",
|
"react": "^16.3.0",
|
||||||
"react-dom": "^16.0.0"
|
"react-dom": "^16.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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,4 @@
|
|||||||
|
/* eslint jsx-a11y/no-static-element-interactions: 0 */
|
||||||
/* eslint react/require-default-props: 0 */
|
/* eslint react/require-default-props: 0 */
|
||||||
/* eslint no-return-assign: 0 */
|
/* eslint no-return-assign: 0 */
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ class NumberFilter extends Component {
|
|||||||
const comparator = this.numberFilterComparator.value;
|
const comparator = this.numberFilterComparator.value;
|
||||||
const number = this.numberFilter.value;
|
const number = this.numberFilter.value;
|
||||||
if (comparator && number) {
|
if (comparator && number) {
|
||||||
onFilter(column, FILTER_TYPE.NUMBER)({ number, comparator });
|
onFilter(column, FILTER_TYPE.NUMBER, true)({ number, comparator });
|
||||||
}
|
}
|
||||||
|
|
||||||
// export onFilter function to allow users to access
|
// export onFilter function to allow users to access
|
||||||
@@ -167,7 +168,11 @@ class NumberFilter extends Component {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ `filter number-filter ${className}` } style={ style }>
|
<div
|
||||||
|
onClick={ e => e.stopPropagation() }
|
||||||
|
className={ `filter number-filter ${className}` }
|
||||||
|
style={ style }
|
||||||
|
>
|
||||||
<select
|
<select
|
||||||
ref={ n => this.numberFilterComparator = n }
|
ref={ n => this.numberFilterComparator = n }
|
||||||
style={ comparatorStyle }
|
style={ comparatorStyle }
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class SelectFilter extends Component {
|
|||||||
|
|
||||||
const value = this.selectInput.value;
|
const value = this.selectInput.value;
|
||||||
if (value && value !== '') {
|
if (value && value !== '') {
|
||||||
onFilter(column, FILTER_TYPE.SELECT)(value);
|
onFilter(column, FILTER_TYPE.SELECT, true)(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// export onFilter function to allow users to access
|
// export onFilter function to allow users to access
|
||||||
@@ -116,6 +116,7 @@ class SelectFilter extends Component {
|
|||||||
style={ style }
|
style={ style }
|
||||||
className={ selectClass }
|
className={ selectClass }
|
||||||
onChange={ this.filter }
|
onChange={ this.filter }
|
||||||
|
onClick={ e => e.stopPropagation() }
|
||||||
defaultValue={ defaultValue !== undefined ? defaultValue : '' }
|
defaultValue={ defaultValue !== undefined ? defaultValue : '' }
|
||||||
>
|
>
|
||||||
{ this.getOptions() }
|
{ this.getOptions() }
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class TextFilter extends Component {
|
|||||||
const defaultValue = this.input.value;
|
const defaultValue = this.input.value;
|
||||||
|
|
||||||
if (defaultValue) {
|
if (defaultValue) {
|
||||||
onFilter(this.props.column, FILTER_TYPE.TEXT)(defaultValue);
|
onFilter(this.props.column, FILTER_TYPE.TEXT, true)(defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// export onFilter function to allow users to access
|
// export onFilter function to allow users to access
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
export const FILTER_TYPE = {
|
export const FILTER_TYPE = {
|
||||||
TEXT: 'TEXT',
|
TEXT: 'TEXT',
|
||||||
SELECT: 'SELECT',
|
SELECT: 'SELECT',
|
||||||
NUMBER: 'NUMBER'
|
MULTISELECT: 'MULTISELECT',
|
||||||
|
NUMBER: 'NUMBER',
|
||||||
|
DATE: 'DATE'
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FILTER_DELAY = 500;
|
export const FILTER_DELAY = 500;
|
||||||
|
|||||||
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
|
||||||
|
};
|
||||||
|
};
|
||||||
126
packages/react-bootstrap-table2-filter/src/filter.js
vendored
126
packages/react-bootstrap-table2-filter/src/filter.js
vendored
@@ -91,6 +91,122 @@ export const filterByNumber = _ => (
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const filterByDate = _ => (
|
||||||
|
data,
|
||||||
|
dataField,
|
||||||
|
{ filterVal: { comparator, date } },
|
||||||
|
customFilterValue
|
||||||
|
) => {
|
||||||
|
if (!date || !comparator) return data;
|
||||||
|
const filterDate = date.getDate();
|
||||||
|
const filterMonth = date.getMonth();
|
||||||
|
const filterYear = date.getFullYear();
|
||||||
|
|
||||||
|
return data.filter((row) => {
|
||||||
|
let valid = true;
|
||||||
|
let cell = _.get(row, dataField);
|
||||||
|
|
||||||
|
if (customFilterValue) {
|
||||||
|
cell = customFilterValue(cell, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof cell !== 'object') {
|
||||||
|
cell = new Date(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetDate = cell.getDate();
|
||||||
|
const targetMonth = cell.getMonth();
|
||||||
|
const targetYear = cell.getFullYear();
|
||||||
|
|
||||||
|
|
||||||
|
switch (comparator) {
|
||||||
|
case EQ: {
|
||||||
|
if (
|
||||||
|
filterDate !== targetDate ||
|
||||||
|
filterMonth !== targetMonth ||
|
||||||
|
filterYear !== targetYear
|
||||||
|
) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GT: {
|
||||||
|
if (cell <= date) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GE: {
|
||||||
|
if (targetYear < filterYear) {
|
||||||
|
valid = false;
|
||||||
|
} else if (targetYear === filterYear &&
|
||||||
|
targetMonth < filterMonth) {
|
||||||
|
valid = false;
|
||||||
|
} else if (targetYear === filterYear &&
|
||||||
|
targetMonth === filterMonth &&
|
||||||
|
targetDate < filterDate) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LT: {
|
||||||
|
if (cell >= date) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LE: {
|
||||||
|
if (targetYear > filterYear) {
|
||||||
|
valid = false;
|
||||||
|
} else if (targetYear === filterYear &&
|
||||||
|
targetMonth > filterMonth) {
|
||||||
|
valid = false;
|
||||||
|
} else if (targetYear === filterYear &&
|
||||||
|
targetMonth === filterMonth &&
|
||||||
|
targetDate > filterDate) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NE: {
|
||||||
|
if (
|
||||||
|
filterDate === targetDate &&
|
||||||
|
filterMonth === targetMonth &&
|
||||||
|
filterYear === targetYear
|
||||||
|
) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
console.error('Date comparator provided is not supported');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterByArray = _ => (
|
||||||
|
data,
|
||||||
|
dataField,
|
||||||
|
{ filterVal, comparator }
|
||||||
|
) => {
|
||||||
|
if (filterVal.length === 0) return data;
|
||||||
|
const refinedFilterVal = filterVal
|
||||||
|
.filter(x => _.isDefined(x))
|
||||||
|
.map(x => x.toString());
|
||||||
|
return data.filter((row) => {
|
||||||
|
const cell = _.get(row, dataField);
|
||||||
|
let cellStr = _.isDefined(cell) ? cell.toString() : '';
|
||||||
|
if (comparator === EQ) {
|
||||||
|
return refinedFilterVal.indexOf(cellStr) !== -1;
|
||||||
|
}
|
||||||
|
cellStr = cellStr.toLocaleUpperCase();
|
||||||
|
return refinedFilterVal.some(item => cellStr.indexOf(item.toLocaleUpperCase()) !== -1);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const filterFactory = _ => (filterType) => {
|
export const filterFactory = _ => (filterType) => {
|
||||||
let filterFn;
|
let filterFn;
|
||||||
switch (filterType) {
|
switch (filterType) {
|
||||||
@@ -98,18 +214,24 @@ export const filterFactory = _ => (filterType) => {
|
|||||||
case FILTER_TYPE.SELECT:
|
case FILTER_TYPE.SELECT:
|
||||||
filterFn = filterByText(_);
|
filterFn = filterByText(_);
|
||||||
break;
|
break;
|
||||||
|
case FILTER_TYPE.MULTISELECT:
|
||||||
|
filterFn = filterByArray(_);
|
||||||
|
break;
|
||||||
case FILTER_TYPE.NUMBER:
|
case FILTER_TYPE.NUMBER:
|
||||||
filterFn = filterByNumber(_);
|
filterFn = filterByNumber(_);
|
||||||
break;
|
break;
|
||||||
|
case FILTER_TYPE.DATE:
|
||||||
|
filterFn = filterByDate(_);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
filterFn = filterByText(_);
|
filterFn = filterByText(_);
|
||||||
}
|
}
|
||||||
return filterFn;
|
return filterFn;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const filters = (store, columns, _) => (currFilters) => {
|
export const filters = (data, columns, _) => (currFilters) => {
|
||||||
const factory = filterFactory(_);
|
const factory = filterFactory(_);
|
||||||
let result = store.getAllData();
|
let result = data;
|
||||||
let filterFn;
|
let filterFn;
|
||||||
Object.keys(currFilters).forEach((dataField) => {
|
Object.keys(currFilters).forEach((dataField) => {
|
||||||
const filterObj = currFilters[dataField];
|
const filterObj = currFilters[dataField];
|
||||||
|
|||||||
@@ -1,92 +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 }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* filter the table like below:
|
|
||||||
* onFilter(column, filterType)(filterVal)
|
|
||||||
* @param {Object} column
|
|
||||||
* @param {String} filterType
|
|
||||||
* @param {String} filterVal - user input for filtering.
|
|
||||||
*/
|
|
||||||
onFilter(column, filterType) {
|
|
||||||
return (filterVal) => {
|
|
||||||
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 option[value=''],
|
||||||
.react-bootstrap-table > table > thead > tr > th .select-filter.placeholder-selected,
|
.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 .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;
|
color: lightgrey;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
@@ -15,17 +16,20 @@
|
|||||||
font-style: initial;
|
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;
|
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;
|
margin-left: 5px;
|
||||||
float: left;
|
float: left;
|
||||||
width: calc(100% - 67px - 5px);
|
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;
|
width: 67px;
|
||||||
float: left;
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -147,7 +147,7 @@ describe('Number Filter', () => {
|
|||||||
|
|
||||||
it('should calling onFilter on componentDidMount', () => {
|
it('should calling onFilter on componentDidMount', () => {
|
||||||
expect(onFilter.calledOnce).toBeTruthy();
|
expect(onFilter.calledOnce).toBeTruthy();
|
||||||
expect(onFilter.calledWith(column, FILTER_TYPE.NUMBER)).toBeTruthy();
|
expect(onFilter.calledWith(column, FILTER_TYPE.NUMBER, true)).toBeTruthy();
|
||||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||||
expect(onFilterFirstReturn.calledWith({ number: `${number}`, comparator })).toBeTruthy();
|
expect(onFilterFirstReturn.calledWith({ number: `${number}`, comparator })).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ describe('Select Filter', () => {
|
|||||||
|
|
||||||
it('should calling onFilter on componentDidMount', () => {
|
it('should calling onFilter on componentDidMount', () => {
|
||||||
expect(onFilter.calledOnce).toBeTruthy();
|
expect(onFilter.calledOnce).toBeTruthy();
|
||||||
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy();
|
expect(onFilter.calledWith(column, FILTER_TYPE.SELECT, true)).toBeTruthy();
|
||||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||||
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ describe('Text Filter', () => {
|
|||||||
|
|
||||||
it('should calling onFilter on componentDidMount', () => {
|
it('should calling onFilter on componentDidMount', () => {
|
||||||
expect(onFilter.calledOnce).toBeTruthy();
|
expect(onFilter.calledOnce).toBeTruthy();
|
||||||
expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy();
|
expect(onFilter.calledWith(column, FILTER_TYPE.TEXT, true)).toBeTruthy();
|
||||||
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
|
||||||
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
|
expect(onFilterFirstReturn.calledWith(defaultValue)).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 _ from 'react-bootstrap-table-next/src/utils';
|
||||||
import Store from 'react-bootstrap-table-next/src/store';
|
|
||||||
|
|
||||||
import { filters } from '../src/filter';
|
import { filters } from '../src/filter';
|
||||||
import { FILTER_TYPE } from '../src/const';
|
import { FILTER_TYPE } from '../src/const';
|
||||||
@@ -11,19 +9,16 @@ for (let i = 0; i < 20; i += 1) {
|
|||||||
data.push({
|
data.push({
|
||||||
id: i,
|
id: i,
|
||||||
name: `itme name ${i}`,
|
name: `itme name ${i}`,
|
||||||
price: 200 + i
|
price: 200 + i,
|
||||||
|
date: new Date(2017, i, 1)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('filter', () => {
|
describe('filter', () => {
|
||||||
let store;
|
|
||||||
let filterFn;
|
|
||||||
let currFilters;
|
let currFilters;
|
||||||
let columns;
|
let columns;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
store = new Store('id');
|
|
||||||
store.data = data;
|
|
||||||
currFilters = {};
|
currFilters = {};
|
||||||
columns = [{
|
columns = [{
|
||||||
dataField: 'id',
|
dataField: 'id',
|
||||||
@@ -34,14 +29,13 @@ describe('filter', () => {
|
|||||||
}, {
|
}, {
|
||||||
dataField: 'price',
|
dataField: 'price',
|
||||||
text: 'Price'
|
text: 'Price'
|
||||||
|
}, {
|
||||||
|
dataField: 'date',
|
||||||
|
text: 'Date'
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('filterByText', () => {
|
describe('filterByText', () => {
|
||||||
beforeEach(() => {
|
|
||||||
filterFn = filters(store, columns, _);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when filter value is not a String', () => {
|
describe('when filter value is not a String', () => {
|
||||||
it('should transform to string and do the filter', () => {
|
it('should transform to string and do the filter', () => {
|
||||||
currFilters.name = {
|
currFilters.name = {
|
||||||
@@ -49,7 +43,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.TEXT
|
filterType: FILTER_TYPE.TEXT
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
expect(result).toHaveLength(2);
|
expect(result).toHaveLength(2);
|
||||||
});
|
});
|
||||||
@@ -62,7 +56,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.TEXT
|
filterType: FILTER_TYPE.TEXT
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
expect(result).toHaveLength(2);
|
expect(result).toHaveLength(2);
|
||||||
});
|
});
|
||||||
@@ -76,7 +70,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.TEXT
|
filterType: FILTER_TYPE.TEXT
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
expect(result).toHaveLength(0);
|
expect(result).toHaveLength(0);
|
||||||
});
|
});
|
||||||
@@ -90,7 +84,7 @@ describe('filter', () => {
|
|||||||
comparator: EQ
|
comparator: EQ
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
expect(result).toHaveLength(1);
|
expect(result).toHaveLength(1);
|
||||||
});
|
});
|
||||||
@@ -98,8 +92,7 @@ describe('filter', () => {
|
|||||||
|
|
||||||
describe('column.filterValue is defined', () => {
|
describe('column.filterValue is defined', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
columns[1].filterValue = sinon.stub();
|
columns[1].filterValue = jest.fn();
|
||||||
filterFn = filters(store, columns, _);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should calling custom filterValue callback correctly', () => {
|
it('should calling custom filterValue callback correctly', () => {
|
||||||
@@ -108,22 +101,64 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.TEXT
|
filterType: FILTER_TYPE.TEXT
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toBeDefined();
|
expect(result).toBeDefined();
|
||||||
expect(columns[1].filterValue.callCount).toBe(data.length);
|
expect(columns[1].filterValue).toHaveBeenCalledTimes(data.length);
|
||||||
const calls = columns[1].filterValue.getCalls();
|
// const calls = columns[1].filterValue.mock.calls;
|
||||||
calls.forEach((call, i) => {
|
// calls.forEach((call, i) => {
|
||||||
expect(call.calledWith(data[i].name, data[i])).toBeTruthy();
|
// 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', () => {
|
describe('filterByNumber', () => {
|
||||||
beforeEach(() => {
|
|
||||||
filterFn = filters(store, columns, _);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when currFilters.filterVal.comparator is empty', () => {
|
describe('when currFilters.filterVal.comparator is empty', () => {
|
||||||
it('should returning correct result', () => {
|
it('should returning correct result', () => {
|
||||||
currFilters.price = {
|
currFilters.price = {
|
||||||
@@ -131,11 +166,11 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = filterFn(currFilters);
|
let result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(data.length);
|
expect(result).toHaveLength(data.length);
|
||||||
|
|
||||||
currFilters.price.filterVal.comparator = undefined;
|
currFilters.price.filterVal.comparator = undefined;
|
||||||
result = filterFn(currFilters);
|
result = filters(result, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(data.length);
|
expect(result).toHaveLength(data.length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -147,7 +182,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(data.length);
|
expect(result).toHaveLength(data.length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -159,11 +194,11 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = filterFn(currFilters);
|
let result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(1);
|
expect(result).toHaveLength(1);
|
||||||
|
|
||||||
currFilters.price.filterVal.number = '0';
|
currFilters.price.filterVal.number = '0';
|
||||||
result = filterFn(currFilters);
|
result = filters(result, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(0);
|
expect(result).toHaveLength(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -175,7 +210,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(16);
|
expect(result).toHaveLength(16);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -187,7 +222,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(17);
|
expect(result).toHaveLength(17);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -199,7 +234,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(3);
|
expect(result).toHaveLength(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -211,7 +246,7 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(4);
|
expect(result).toHaveLength(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -223,9 +258,46 @@ describe('filter', () => {
|
|||||||
filterType: FILTER_TYPE.NUMBER
|
filterType: FILTER_TYPE.NUMBER
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = filterFn(currFilters);
|
const result = filters(data, columns, _)(currFilters);
|
||||||
expect(result).toHaveLength(19);
|
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....
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,252 +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 BootstrapTable from 'react-bootstrap-table-next/src/bootstrap-table';
|
|
||||||
import Store from 'react-bootstrap-table-next/src/store';
|
|
||||||
import filter, { textFilter } from '..';
|
|
||||||
import wrapperFactory from '../src/wrapper';
|
|
||||||
import { FILTER_TYPE } from '../src/const';
|
|
||||||
|
|
||||||
const data = [];
|
|
||||||
for (let i = 0; i < 20; i += 1) {
|
|
||||||
data.push({
|
|
||||||
id: i,
|
|
||||||
name: `itme name ${i}`,
|
|
||||||
price: 200 + i
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Wrapper', () => {
|
|
||||||
let wrapper;
|
|
||||||
let instance;
|
|
||||||
const onTableChangeCB = sinon.stub();
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
onTableChangeCB.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
const createTableProps = (props) => {
|
|
||||||
const tableProps = {
|
|
||||||
keyField: 'id',
|
|
||||||
columns: [{
|
|
||||||
dataField: 'id',
|
|
||||||
text: 'ID'
|
|
||||||
}, {
|
|
||||||
dataField: 'name',
|
|
||||||
text: 'Name',
|
|
||||||
filter: textFilter()
|
|
||||||
}, {
|
|
||||||
dataField: 'price',
|
|
||||||
text: 'Price',
|
|
||||||
filter: textFilter()
|
|
||||||
}],
|
|
||||||
data,
|
|
||||||
filter: filter(),
|
|
||||||
_,
|
|
||||||
store: new Store('id'),
|
|
||||||
onTableChange: onTableChangeCB,
|
|
||||||
...props
|
|
||||||
};
|
|
||||||
tableProps.store.data = data;
|
|
||||||
return tableProps;
|
|
||||||
};
|
|
||||||
|
|
||||||
const FilterWrapper = wrapperFactory(BootstrapTable, {
|
|
||||||
_,
|
|
||||||
remoteResolver
|
|
||||||
});
|
|
||||||
|
|
||||||
const createFilterWrapper = (props, renderFragment = true) => {
|
|
||||||
wrapper = shallow(<FilterWrapper { ...props } />);
|
|
||||||
instance = wrapper.instance();
|
|
||||||
if (renderFragment) {
|
|
||||||
const fragment = instance.render();
|
|
||||||
wrapper = shallow(<div>{ fragment }</div>);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('default filter wrapper', () => {
|
|
||||||
const props = createTableProps();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
createFilterWrapper(props);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should rendering correctly', () => {
|
|
||||||
expect(wrapper.length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should initializing state correctly', () => {
|
|
||||||
expect(instance.state.isDataChanged).toBeFalsy();
|
|
||||||
expect(instance.state.currFilters).toEqual({});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should rendering BootstraTable correctly', () => {
|
|
||||||
const table = wrapper.find(BootstrapTable);
|
|
||||||
expect(table.length).toBe(1);
|
|
||||||
expect(table.prop('onFilter')).toBeDefined();
|
|
||||||
expect(table.prop('isDataChanged')).toEqual(instance.state.isDataChanged);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('componentWillReceiveProps', () => {
|
|
||||||
let nextProps;
|
|
||||||
|
|
||||||
describe('when props.store.filters is same as current state.currFilters', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
nextProps = createTableProps();
|
|
||||||
instance.componentWillReceiveProps(nextProps);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should setting isDataChanged as false (Temporary solution)', () => {
|
|
||||||
expect(instance.state.isDataChanged).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when props.isDataChanged is true', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
nextProps = createTableProps({ isDataChanged: true });
|
|
||||||
instance.componentWillReceiveProps(nextProps);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should setting isDataChanged as true', () => {
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when props.store.filters is different from current state.currFilters', () => {
|
|
||||||
const nextData = [];
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
nextProps = createTableProps();
|
|
||||||
nextProps.store.filters = { price: { filterVal: 20, filterType: FILTER_TYPE.TEXT } };
|
|
||||||
nextProps.store.setAllData(nextData);
|
|
||||||
instance.componentWillReceiveProps(nextProps);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should setting states correctly', () => {
|
|
||||||
expect(nextProps.store.filteredData).toEqual(nextData);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(instance.state.currFilters).toBe(nextProps.store.filters);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when remote filter is enabled', () => {
|
|
||||||
let props;
|
|
||||||
const nextData = [];
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
props = createTableProps({ remote: { filter: true } });
|
|
||||||
createFilterWrapper(props);
|
|
||||||
nextProps = createTableProps({ remote: { filter: true } });
|
|
||||||
nextProps.store.setAllData(nextData);
|
|
||||||
instance.componentWillReceiveProps(nextProps);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should setting states correctly', () => {
|
|
||||||
expect(nextProps.store.filteredData).toEqual(nextData);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(instance.state.currFilters).toBe(nextProps.store.filters);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onFilter', () => {
|
|
||||||
let props;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
props = createTableProps();
|
|
||||||
createFilterWrapper(props);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when filterVal is empty or undefined', () => {
|
|
||||||
const filterVals = ['', undefined];
|
|
||||||
|
|
||||||
it('should setting store object correctly', () => {
|
|
||||||
filterVals.forEach((filterVal) => {
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal);
|
|
||||||
expect(props.store.filtering).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should setting state correctly', () => {
|
|
||||||
filterVals.forEach((filterVal) => {
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when filterVal is existing', () => {
|
|
||||||
const filterVal = '3';
|
|
||||||
|
|
||||||
it('should setting store object correctly', () => {
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal);
|
|
||||||
expect(props.store.filters).toEqual(instance.state.currFilters);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should setting state correctly', () => {
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('when remote filter is enabled', () => {
|
|
||||||
const filterVal = '3';
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
props = createTableProps();
|
|
||||||
props.remote = { filter: true };
|
|
||||||
createFilterWrapper(props);
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not setting store object correctly', () => {
|
|
||||||
expect(props.store.filters).not.toEqual(instance.state.currFilters);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not setting state', () => {
|
|
||||||
expect(instance.state.isDataChanged).toBeFalsy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should calling props.onRemoteFilterChange correctly', () => {
|
|
||||||
expect(onTableChangeCB.calledOnce).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('combination', () => {
|
|
||||||
it('should setting store object correctly', () => {
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)('3');
|
|
||||||
expect(props.store.filters).toEqual(instance.state.currFilters);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(1);
|
|
||||||
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)('2');
|
|
||||||
expect(props.store.filters).toEqual(instance.state.currFilters);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(1);
|
|
||||||
|
|
||||||
instance.onFilter(props.columns[2], FILTER_TYPE.TEXT)('2');
|
|
||||||
expect(props.store.filters).toEqual(instance.state.currFilters);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(2);
|
|
||||||
|
|
||||||
instance.onFilter(props.columns[2], FILTER_TYPE.TEXT)('');
|
|
||||||
expect(props.store.filters).toEqual(instance.state.currFilters);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(1);
|
|
||||||
|
|
||||||
instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)('');
|
|
||||||
expect(props.store.filters).toEqual(instance.state.currFilters);
|
|
||||||
expect(instance.state.isDataChanged).toBeTruthy();
|
|
||||||
expect(Object.keys(instance.state.currFilters)).toHaveLength(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
19
packages/react-bootstrap-table2-overlay/index.js
vendored
19
packages/react-bootstrap-table2-overlay/index.js
vendored
@@ -1,16 +1,29 @@
|
|||||||
/* eslint no-return-assign: 0 */
|
/* eslint no-return-assign: 0 */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import LoadingOverlay from 'react-loading-overlay';
|
import LoadingOverlay from 'react-loading-overlay';
|
||||||
|
|
||||||
export default options => (element, loading) =>
|
export default options => loading =>
|
||||||
class TableLoadingOverlayWrapper extends React.Component {
|
class TableLoadingOverlayWrapper extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.element.isRequired
|
||||||
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
const { wrapper } = this.overlay;
|
const { wrapper } = this.overlay;
|
||||||
const masker = wrapper.firstChild;
|
const masker = wrapper.firstChild;
|
||||||
const headerDOM = wrapper.parentElement.querySelector('thead');
|
const headerDOM = wrapper.parentElement.querySelector('thead');
|
||||||
const bodyDOM = wrapper.parentElement.querySelector('tbody');
|
const bodyDOM = wrapper.parentElement.querySelector('tbody');
|
||||||
masker.style.marginTop = window.getComputedStyle(headerDOM).height;
|
const captionDOM = wrapper.parentElement.querySelector('caption');
|
||||||
|
|
||||||
|
let marginTop = window.getComputedStyle(headerDOM).height;
|
||||||
|
if (captionDOM) {
|
||||||
|
marginTop = parseFloat(marginTop.replace('px', ''));
|
||||||
|
marginTop += parseFloat(window.getComputedStyle(captionDOM).height.replace('px', ''));
|
||||||
|
marginTop = `${marginTop}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
masker.style.marginTop = marginTop;
|
||||||
masker.style.height = window.getComputedStyle(bodyDOM).height;
|
masker.style.height = window.getComputedStyle(bodyDOM).height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +35,7 @@ export default options => (element, loading) =>
|
|||||||
{ ...options }
|
{ ...options }
|
||||||
active={ loading }
|
active={ loading }
|
||||||
>
|
>
|
||||||
{ element }
|
{ this.props.children }
|
||||||
</LoadingOverlay>
|
</LoadingOverlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table2-overlay",
|
"name": "react-bootstrap-table2-overlay",
|
||||||
"version": "0.1.1",
|
"version": "1.0.0",
|
||||||
"description": "it's a loading overlay addons for react-bootstrap-table2",
|
"description": "it's a loading overlay addons for react-bootstrap-table2",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prop-types": "^15.0.0",
|
"prop-types": "^15.0.0",
|
||||||
"react": "^16.0.0",
|
"react": "^16.3.0",
|
||||||
"react-dom": "^16.0.0"
|
"react-dom": "^16.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow, render } from 'enzyme';
|
import { render, shallow } from 'enzyme';
|
||||||
import LoadingOverlay from 'react-loading-overlay';
|
import LoadingOverlay from 'react-loading-overlay';
|
||||||
|
|
||||||
import overlayFactory from '..';
|
import overlayFactory from '../index.js';
|
||||||
|
|
||||||
describe('overlayFactory', () => {
|
describe('overlayFactory', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
// let instance;
|
|
||||||
|
|
||||||
const createTable = () => (
|
const createTable = () => (
|
||||||
<table>
|
<table>
|
||||||
@@ -27,8 +26,8 @@ describe('overlayFactory', () => {
|
|||||||
describe('when loading is false', () => {
|
describe('when loading is false', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const tableElm = createTable();
|
const tableElm = createTable();
|
||||||
const Overlay = overlayFactory()(tableElm, false);
|
const Overlay = overlayFactory()(false);
|
||||||
wrapper = shallow(<Overlay />);
|
wrapper = shallow(<Overlay>{ tableElm }</Overlay>);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should rendering Overlay component correctly', () => {
|
it('should rendering Overlay component correctly', () => {
|
||||||
@@ -42,14 +41,12 @@ describe('overlayFactory', () => {
|
|||||||
describe('when loading is true', () => {
|
describe('when loading is true', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const tableElm = createTable();
|
const tableElm = createTable();
|
||||||
const Overlay = overlayFactory()(tableElm, true);
|
const Overlay = overlayFactory()(true);
|
||||||
wrapper = render(<Overlay />);
|
wrapper = render(<Overlay>{ tableElm }</Overlay>);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should rendering Overlay component correctly', () => {
|
it('should rendering Overlay component correctly', () => {
|
||||||
const overlay = wrapper.find(LoadingOverlay);
|
|
||||||
expect(wrapper.length).toBe(1);
|
expect(wrapper.length).toBe(1);
|
||||||
expect(overlay.length).toBe(0);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,8 +57,8 @@ describe('overlayFactory', () => {
|
|||||||
};
|
};
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const tableElm = createTable();
|
const tableElm = createTable();
|
||||||
const Overlay = overlayFactory(options)(tableElm, false);
|
const Overlay = overlayFactory(options)(false);
|
||||||
wrapper = shallow(<Overlay />);
|
wrapper = shallow(<Overlay>{ tableElm }</Overlay>);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should rendering Overlay component with options correctly', () => {
|
it('should rendering Overlay component with options correctly', () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import wrapperFactory from './src/wrapper';
|
import createContext from './src/context';
|
||||||
|
|
||||||
export default (options = {}) => ({
|
export default (options = {}) => ({
|
||||||
wrapperFactory,
|
createContext,
|
||||||
options
|
options
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table2-paginator",
|
"name": "react-bootstrap-table2-paginator",
|
||||||
"version": "0.1.1",
|
"version": "1.0.1",
|
||||||
"description": "it's the pagination addon for react-bootstrap-table2",
|
"description": "it's the pagination addon for react-bootstrap-table2",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prop-types": "^15.0.0",
|
"prop-types": "^15.0.0",
|
||||||
"react": "^16.0.0",
|
"react": "^16.3.0",
|
||||||
"react-dom": "^16.0.0"
|
"react-dom": "^16.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user