mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2026-06-29 13:40:07 +00:00
Compare commits
33 Commits
react-boot
...
react-boot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -18,6 +18,7 @@
|
|||||||
* [id](#id)
|
* [id](#id)
|
||||||
* [classes](#classes)
|
* [classes](#classes)
|
||||||
* [wrapperClasses](#wrapperClasses)
|
* [wrapperClasses](#wrapperClasses)
|
||||||
|
* [headerClasses](#headerClasses)
|
||||||
* [cellEdit](#cellEdit)
|
* [cellEdit](#cellEdit)
|
||||||
* [selectRow](#selectRow)
|
* [selectRow](#selectRow)
|
||||||
* [rowStyle](#rowStyle)
|
* [rowStyle](#rowStyle)
|
||||||
@@ -111,6 +112,10 @@ Customize class on `table` element.
|
|||||||
|
|
||||||
### <a name='wrapperClasses'>wrapperClasses - [String]</a>
|
### <a name='wrapperClasses'>wrapperClasses - [String]</a>
|
||||||
Customize class on the outer element which wrap up the `table` element.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -82,13 +82,13 @@ 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
|
||||||
- [X] Date Filter
|
- [X] Date Filter
|
||||||
- [ ] Array Filter
|
- [X] Array Filter
|
||||||
- [X] 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.
|
||||||
|
|||||||
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>
|
||||||
|
);
|
||||||
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>
|
||||||
|
);
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
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>
|
||||||
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table2-example",
|
"name": "react-bootstrap-table2-example",
|
||||||
"version": "0.1.9",
|
"version": "0.1.12",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -33,6 +33,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,6 +46,9 @@ 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';
|
||||||
@@ -55,6 +59,9 @@ import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-
|
|||||||
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 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';
|
||||||
|
|
||||||
// work on rows
|
// work on rows
|
||||||
import RowStyleTable from 'examples/rows/row-style';
|
import RowStyleTable from 'examples/rows/row-style';
|
||||||
@@ -163,7 +170,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 />)
|
||||||
@@ -174,6 +182,8 @@ 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', () => <DateFilter />)
|
||||||
@@ -182,11 +192,15 @@ storiesOf('Column Filter', module)
|
|||||||
.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 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 Date Filter', () => <ProgrammaticallyDateFilter />)
|
||||||
|
.add('Programmatically Multi Select Filter', () => <ProgrammaticallyMultiSelectFilter />)
|
||||||
|
.add('Custom Filter', () => <CustomFilter />)
|
||||||
|
.add('Advance Custom Filter', () => <AdvanceCustomFilter />);
|
||||||
|
|
||||||
storiesOf('Work on Rows', module)
|
storiesOf('Work on Rows', module)
|
||||||
.add('Customize Row Style', () => <RowStyleTable />)
|
.add('Customize Row Style', () => <RowStyleTable />)
|
||||||
|
|||||||
@@ -9,4 +9,8 @@
|
|||||||
|
|
||||||
.demo-row-odd {
|
.demo-row-odd {
|
||||||
background-color: $green-lighten-4;
|
background-color: $green-lighten-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-class {
|
||||||
|
background-color: $green-lighten-4;
|
||||||
}
|
}
|
||||||
@@ -18,8 +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
|
* DateFilter
|
||||||
|
* CustomFilter
|
||||||
* **Coming soon!**
|
* **Coming soon!**
|
||||||
|
|
||||||
## Add CSS
|
## Add CSS
|
||||||
@@ -113,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
|
||||||
@@ -130,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({
|
||||||
@@ -189,4 +237,54 @@ const dateFilter = dateFilter({
|
|||||||
})
|
})
|
||||||
|
|
||||||
// omit...
|
// 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
|
||||||
|
|||||||
13
packages/react-bootstrap-table2-filter/index.js
vendored
13
packages/react-bootstrap-table2-filter/index.js
vendored
@@ -1,15 +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 DateFilter from './src/components/date';
|
import DateFilter from './src/components/date';
|
||||||
import wrapperFactory from './src/wrapper';
|
import wrapperFactory from './src/wrapper';
|
||||||
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,
|
wrapperFactory,
|
||||||
options
|
options
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const FILTER_TYPES = FILTER_TYPE;
|
||||||
|
|
||||||
export const Comparator = Comparison;
|
export const Comparator = Comparison;
|
||||||
|
|
||||||
export const textFilter = (props = {}) => ({
|
export const textFilter = (props = {}) => ({
|
||||||
@@ -22,6 +26,11 @@ 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
|
||||||
@@ -31,3 +40,7 @@ export const dateFilter = (props = {}) => ({
|
|||||||
Filter: DateFilter,
|
Filter: DateFilter,
|
||||||
props
|
props
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const customFilter = (props = {}) => ({
|
||||||
|
props
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table2-filter",
|
"name": "react-bootstrap-table2-filter",
|
||||||
"version": "0.2.0",
|
"version": "0.3.2",
|
||||||
"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": {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* eslint react/require-default-props: 0 */
|
/* eslint react/require-default-props: 0 */
|
||||||
|
/* eslint jsx-a11y/no-static-element-interactions: 0 */
|
||||||
/* eslint no-return-assign: 0 */
|
/* eslint no-return-assign: 0 */
|
||||||
/* eslint prefer-template: 0 */
|
/* eslint prefer-template: 0 */
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
@@ -92,12 +93,16 @@ class DateFilter extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyFilter(value, comparator) {
|
applyFilter(value, comparator) {
|
||||||
if (!comparator || !value) {
|
// if (!comparator || !value) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
const { column, onFilter, delay } = this.props;
|
const { column, onFilter, delay } = this.props;
|
||||||
const execute = () => {
|
const execute = () => {
|
||||||
const date = typeof value !== 'object' ? new Date(value) : value;
|
// 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)({ date, comparator });
|
onFilter(column, FILTER_TYPE.DATE)({ date, comparator });
|
||||||
};
|
};
|
||||||
if (delay) {
|
if (delay) {
|
||||||
@@ -121,7 +126,11 @@ class DateFilter extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ `filter date-filter ${className}` } style={ style }>
|
<div
|
||||||
|
onClick={ e => e.stopPropagation() }
|
||||||
|
className={ `filter date-filter ${className}` }
|
||||||
|
style={ style }
|
||||||
|
>
|
||||||
<select
|
<select
|
||||||
ref={ n => this.dateFilterComparator = n }
|
ref={ n => this.dateFilterComparator = n }
|
||||||
style={ comparatorStyle }
|
style={ comparatorStyle }
|
||||||
|
|||||||
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 */
|
||||||
|
|
||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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() }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export const FILTER_TYPE = {
|
export const FILTER_TYPE = {
|
||||||
TEXT: 'TEXT',
|
TEXT: 'TEXT',
|
||||||
SELECT: 'SELECT',
|
SELECT: 'SELECT',
|
||||||
|
MULTISELECT: 'MULTISELECT',
|
||||||
NUMBER: 'NUMBER',
|
NUMBER: 'NUMBER',
|
||||||
DATE: 'DATE'
|
DATE: 'DATE'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -187,6 +187,26 @@ export const filterByDate = _ => (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -194,6 +214,9 @@ 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;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default (Base, {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = { currFilters: {}, isDataChanged: props.isDataChanged || false };
|
this.state = { currFilters: {}, isDataChanged: props.isDataChanged || false };
|
||||||
this.onFilter = this.onFilter.bind(this);
|
this.onFilter = this.onFilter.bind(this);
|
||||||
|
this.onExternalFilter = this.onExternalFilter.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps({ isDataChanged, store, columns }) {
|
componentWillReceiveProps({ isDataChanged, store, columns }) {
|
||||||
@@ -52,12 +53,20 @@ export default (Base, {
|
|||||||
const currFilters = Object.assign({}, store.filters);
|
const currFilters = Object.assign({}, store.filters);
|
||||||
const { dataField, filter } = column;
|
const { dataField, filter } = column;
|
||||||
|
|
||||||
if (!_.isDefined(filterVal) || filterVal === '') {
|
const needClearFilters =
|
||||||
|
!_.isDefined(filterVal) ||
|
||||||
|
filterVal === '' ||
|
||||||
|
filterVal.length === 0;
|
||||||
|
|
||||||
|
if (needClearFilters) {
|
||||||
delete currFilters[dataField];
|
delete currFilters[dataField];
|
||||||
} else {
|
} else {
|
||||||
// select default comparator is EQ, others are LIKE
|
// select default comparator is EQ, others are LIKE
|
||||||
const {
|
const {
|
||||||
comparator = (filterType === FILTER_TYPE.SELECT ? EQ : LIKE),
|
comparator = (
|
||||||
|
(filterType === FILTER_TYPE.SELECT) || (
|
||||||
|
filterType === FILTER_TYPE.MULTISELECT) ? EQ : LIKE
|
||||||
|
),
|
||||||
caseSensitive = false
|
caseSensitive = false
|
||||||
} = filter.props;
|
} = filter.props;
|
||||||
currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive };
|
currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive };
|
||||||
@@ -78,12 +87,19 @@ export default (Base, {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onExternalFilter(column, filterType) {
|
||||||
|
return (value) => {
|
||||||
|
this.onFilter(column, filterType)(value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Base
|
<Base
|
||||||
{ ...this.props }
|
{ ...this.props }
|
||||||
data={ this.props.store.data }
|
data={ this.props.store.data }
|
||||||
onFilter={ this.onFilter }
|
onFilter={ this.onFilter }
|
||||||
|
onExternalFilter={ this.onExternalFilter }
|
||||||
isDataChanged={ this.state.isDataChanged }
|
isDataChanged={ this.state.isDataChanged }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -123,6 +123,55 @@ describe('filter', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('filterByArray', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
filterFn = filters(store, columns, _);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when filter value is empty array', () => {
|
||||||
|
it('should return original data', () => {
|
||||||
|
currFilters.name = {
|
||||||
|
filterVal: [],
|
||||||
|
filterType: FILTER_TYPE.MULTISELECT
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = filterFn(currFilters);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result).toHaveLength(store.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 = filterFn(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 = filterFn(currFilters);
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result).toHaveLength(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('filterByNumber', () => {
|
describe('filterByNumber', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
filterFn = filters(store, columns, _);
|
filterFn = filters(store, columns, _);
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ describe('Wrapper', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when filterVal is empty or undefined', () => {
|
describe('when filterVal is empty or undefined', () => {
|
||||||
const filterVals = ['', undefined];
|
const filterVals = ['', undefined, []];
|
||||||
|
|
||||||
it('should setting store object correctly', () => {
|
it('should setting store object correctly', () => {
|
||||||
filterVals.forEach((filterVal) => {
|
filterVals.forEach((filterVal) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table2-paginator",
|
"name": "react-bootstrap-table2-paginator",
|
||||||
"version": "0.1.4",
|
"version": "0.1.6",
|
||||||
"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": {
|
||||||
|
|||||||
@@ -1,12 +1,39 @@
|
|||||||
|
/* eslint no-param-reassign: 0 */
|
||||||
|
|
||||||
|
const getNormalizedPage = (
|
||||||
|
page,
|
||||||
|
pageStartIndex
|
||||||
|
) => {
|
||||||
|
const offset = Math.abs(1 - pageStartIndex);
|
||||||
|
return page + offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
const endIndex = (
|
||||||
|
page,
|
||||||
|
sizePerPage,
|
||||||
|
pageStartIndex
|
||||||
|
) => (getNormalizedPage(page, pageStartIndex) * sizePerPage) - 1;
|
||||||
|
|
||||||
|
const startIndex = (
|
||||||
|
end,
|
||||||
|
sizePerPage,
|
||||||
|
) => end - (sizePerPage - 1);
|
||||||
|
|
||||||
|
export const alignPage = (store, pageStartIndex, sizePerPage) => {
|
||||||
|
const end = endIndex(store.page, sizePerPage, pageStartIndex);
|
||||||
|
const dataSize = store.data.length;
|
||||||
|
|
||||||
|
if (end - 1 > dataSize) {
|
||||||
|
return pageStartIndex;
|
||||||
|
}
|
||||||
|
return store.page;
|
||||||
|
};
|
||||||
|
|
||||||
export const getByCurrPage = (store, pageStartIndex) => {
|
export const getByCurrPage = (store, pageStartIndex) => {
|
||||||
const dataSize = store.data.length;
|
const dataSize = store.data.length;
|
||||||
if (!dataSize) return [];
|
if (!dataSize) return [];
|
||||||
const getNormalizedPage = () => {
|
const end = endIndex(store.page, store.sizePerPage, pageStartIndex);
|
||||||
const offset = Math.abs(1 - pageStartIndex);
|
const start = startIndex(end, store.sizePerPage);
|
||||||
return store.page + offset;
|
|
||||||
};
|
|
||||||
const end = (getNormalizedPage() * store.sizePerPage) - 1;
|
|
||||||
const start = end - (store.sizePerPage - 1);
|
|
||||||
|
|
||||||
const result = [];
|
const result = [];
|
||||||
for (let i = start; i <= end; i += 1) {
|
for (let i = start; i <= end; i += 1) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import Const from './const';
|
import Const from './const';
|
||||||
import Pagination from './pagination';
|
import Pagination from './pagination';
|
||||||
import { getByCurrPage } from './page';
|
import { getByCurrPage, alignPage } from './page';
|
||||||
|
|
||||||
export default (Base, {
|
export default (Base, {
|
||||||
remoteResolver
|
remoteResolver
|
||||||
@@ -49,16 +49,23 @@ export default (Base, {
|
|||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
let needNewState = false;
|
let needNewState = false;
|
||||||
let { currPage, currSizePerPage } = this.state;
|
let { currPage, currSizePerPage } = this.state;
|
||||||
const { page, sizePerPage, pageStartIndex, onPageChange } = nextProps.pagination.options;
|
const { page, sizePerPage, onPageChange } = nextProps.pagination.options;
|
||||||
|
|
||||||
|
const pageStartIndex = typeof nextProps.pagination.options.pageStartIndex !== 'undefined' ?
|
||||||
|
nextProps.pagination.options.pageStartIndex : Const.PAGE_START_INDEX;
|
||||||
|
|
||||||
if (typeof page !== 'undefined' && currPage !== page) { // user defined page
|
if (typeof page !== 'undefined' && currPage !== page) { // user defined page
|
||||||
currPage = page;
|
currPage = page;
|
||||||
needNewState = true;
|
needNewState = true;
|
||||||
} else if (nextProps.isDataChanged) { // user didn't defined page but data change
|
} else if (nextProps.isDataChanged) {
|
||||||
currPage = typeof pageStartIndex !== 'undefined' ? pageStartIndex : Const.PAGE_START_INDEX;
|
currPage = alignPage(this.props.store, pageStartIndex, currSizePerPage);
|
||||||
needNewState = true;
|
needNewState = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof currPage === 'undefined') {
|
||||||
|
currPage = pageStartIndex;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof sizePerPage !== 'undefined') {
|
if (typeof sizePerPage !== 'undefined') {
|
||||||
currSizePerPage = sizePerPage;
|
currSizePerPage = sizePerPage;
|
||||||
needNewState = true;
|
needNewState = true;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Store from 'react-bootstrap-table-next/src/store';
|
import Store from 'react-bootstrap-table-next/src/store';
|
||||||
import { getByCurrPage } from '../src/page';
|
import { getByCurrPage, alignPage } from '../src/page';
|
||||||
|
|
||||||
describe('Page Functions', () => {
|
describe('Page Functions', () => {
|
||||||
let data;
|
let data;
|
||||||
@@ -48,4 +48,40 @@ describe('Page Functions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('alignPage', () => {
|
||||||
|
const pageStartIndex = 1;
|
||||||
|
const sizePerPage = 10;
|
||||||
|
describe('if the length of store.data is less than the end page index', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
data = [];
|
||||||
|
for (let i = 0; i < 15; i += 1) {
|
||||||
|
data.push({ id: i, name: `test_name${i}` });
|
||||||
|
}
|
||||||
|
store = new Store('id');
|
||||||
|
store.data = data;
|
||||||
|
store.page = 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return pageStartIndex argument', () => {
|
||||||
|
expect(alignPage(store, pageStartIndex, sizePerPage)).toEqual(pageStartIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if the length of store.data is large than the end page index', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
data = [];
|
||||||
|
for (let i = 0; i < 30; i += 1) {
|
||||||
|
data.push({ id: i, name: `test_name${i}` });
|
||||||
|
}
|
||||||
|
store = new Store('id');
|
||||||
|
store.data = data;
|
||||||
|
store.page = 2;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return current page', () => {
|
||||||
|
expect(alignPage(store, pageStartIndex, sizePerPage)).toEqual(store.page);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -176,22 +176,6 @@ describe('Wrapper', () => {
|
|||||||
expect(props.store.page).toEqual(instance.state.currPage);
|
expect(props.store.page).toEqual(instance.state.currPage);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when nextProps.isDataChanged is true and options.pageStartIndex is existing', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
nextProps.isDataChanged = true;
|
|
||||||
nextProps.pagination.options.pageStartIndex = 0;
|
|
||||||
instance.componentWillReceiveProps(nextProps);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should setting currPage state correctly', () => {
|
|
||||||
expect(instance.state.currPage).toBe(nextProps.pagination.options.pageStartIndex);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should saving store.page correctly', () => {
|
|
||||||
expect(props.store.page).toEqual(instance.state.currPage);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-bootstrap-table-next",
|
"name": "react-bootstrap-table-next",
|
||||||
"version": "0.1.13",
|
"version": "0.1.15",
|
||||||
"description": "Next generation of react-bootstrap-table",
|
"description": "Next generation of react-bootstrap-table",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -86,10 +86,12 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
|||||||
{ tableCaption }
|
{ tableCaption }
|
||||||
<Header
|
<Header
|
||||||
columns={ columns }
|
columns={ columns }
|
||||||
|
className={ this.props.headerClasses }
|
||||||
sortField={ store.sortField }
|
sortField={ store.sortField }
|
||||||
sortOrder={ store.sortOrder }
|
sortOrder={ store.sortOrder }
|
||||||
onSort={ this.props.onSort }
|
onSort={ this.props.onSort }
|
||||||
onFilter={ this.props.onFilter }
|
onFilter={ this.props.onFilter }
|
||||||
|
onExternalFilter={ this.props.onExternalFilter }
|
||||||
selectRow={ headerCellSelectionInfo }
|
selectRow={ headerCellSelectionInfo }
|
||||||
/>
|
/>
|
||||||
<Body
|
<Body
|
||||||
@@ -154,6 +156,7 @@ BootstrapTable.propTypes = {
|
|||||||
rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
|
rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
|
||||||
rowEvents: PropTypes.object,
|
rowEvents: PropTypes.object,
|
||||||
rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||||
|
headerClasses: PropTypes.string,
|
||||||
defaultSorted: PropTypes.arrayOf(PropTypes.shape({
|
defaultSorted: PropTypes.arrayOf(PropTypes.shape({
|
||||||
dataField: PropTypes.string.isRequired,
|
dataField: PropTypes.string.isRequired,
|
||||||
order: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]).isRequired
|
order: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]).isRequired
|
||||||
@@ -162,7 +165,8 @@ BootstrapTable.propTypes = {
|
|||||||
overlay: PropTypes.func,
|
overlay: PropTypes.func,
|
||||||
onTableChange: PropTypes.func,
|
onTableChange: PropTypes.func,
|
||||||
onSort: PropTypes.func,
|
onSort: PropTypes.func,
|
||||||
onFilter: PropTypes.func
|
onFilter: PropTypes.func,
|
||||||
|
onExternalFilter: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
BootstrapTable.defaultProps = {
|
BootstrapTable.defaultProps = {
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ const HeaderCell = (props) => {
|
|||||||
sorting,
|
sorting,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
isLastSorting,
|
isLastSorting,
|
||||||
onFilter
|
onFilter,
|
||||||
|
onExternalFilter
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
text,
|
text,
|
||||||
sort,
|
sort,
|
||||||
filter,
|
filter,
|
||||||
|
filterRenderer,
|
||||||
headerTitle,
|
headerTitle,
|
||||||
headerAlign,
|
headerAlign,
|
||||||
headerFormatter,
|
headerFormatter,
|
||||||
@@ -89,7 +91,11 @@ const HeaderCell = (props) => {
|
|||||||
|
|
||||||
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
|
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
|
||||||
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
|
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
|
||||||
if (filter) {
|
|
||||||
|
if (filterRenderer) {
|
||||||
|
const onCustomFilter = onExternalFilter(column, filter.props.type);
|
||||||
|
filterElm = filterRenderer(onCustomFilter, column);
|
||||||
|
} else if (filter) {
|
||||||
filterElm = <filter.Filter { ...filter.props } onFilter={ onFilter } column={ column } />;
|
filterElm = <filter.Filter { ...filter.props } onFilter={ onFilter } column={ column } />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +142,7 @@ HeaderCell.propTypes = {
|
|||||||
editorRenderer: PropTypes.func,
|
editorRenderer: PropTypes.func,
|
||||||
validator: PropTypes.func,
|
validator: PropTypes.func,
|
||||||
filter: PropTypes.object,
|
filter: PropTypes.object,
|
||||||
|
filterRenderer: PropTypes.func,
|
||||||
filterValue: PropTypes.func
|
filterValue: PropTypes.func
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
index: PropTypes.number.isRequired,
|
index: PropTypes.number.isRequired,
|
||||||
@@ -143,7 +150,8 @@ HeaderCell.propTypes = {
|
|||||||
sorting: PropTypes.bool,
|
sorting: PropTypes.bool,
|
||||||
sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]),
|
sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]),
|
||||||
isLastSorting: PropTypes.bool,
|
isLastSorting: PropTypes.bool,
|
||||||
onFilter: PropTypes.func
|
onFilter: PropTypes.func,
|
||||||
|
onExternalFilter: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default HeaderCell;
|
export default HeaderCell;
|
||||||
|
|||||||
11
packages/react-bootstrap-table2/src/header.js
vendored
11
packages/react-bootstrap-table2/src/header.js
vendored
@@ -10,17 +10,19 @@ const Header = (props) => {
|
|||||||
const { ROW_SELECT_DISABLED } = Const;
|
const { ROW_SELECT_DISABLED } = Const;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
className,
|
||||||
columns,
|
columns,
|
||||||
onSort,
|
onSort,
|
||||||
onFilter,
|
onFilter,
|
||||||
sortField,
|
sortField,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
selectRow
|
selectRow,
|
||||||
|
onExternalFilter
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr className={ className }>
|
||||||
{
|
{
|
||||||
(selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn)
|
(selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn)
|
||||||
? <SelectionHeaderCell { ...selectRow } /> : null
|
? <SelectionHeaderCell { ...selectRow } /> : null
|
||||||
@@ -39,6 +41,7 @@ const Header = (props) => {
|
|||||||
onSort={ onSort }
|
onSort={ onSort }
|
||||||
sorting={ currSort }
|
sorting={ currSort }
|
||||||
onFilter={ onFilter }
|
onFilter={ onFilter }
|
||||||
|
onExternalFilter={ onExternalFilter }
|
||||||
sortOrder={ sortOrder }
|
sortOrder={ sortOrder }
|
||||||
isLastSorting={ isLastSorting }
|
isLastSorting={ isLastSorting }
|
||||||
/>);
|
/>);
|
||||||
@@ -57,7 +60,9 @@ Header.propTypes = {
|
|||||||
onFilter: PropTypes.func,
|
onFilter: PropTypes.func,
|
||||||
sortField: PropTypes.string,
|
sortField: PropTypes.string,
|
||||||
sortOrder: PropTypes.string,
|
sortOrder: PropTypes.string,
|
||||||
selectRow: PropTypes.object
|
selectRow: PropTypes.object,
|
||||||
|
onExternalFilter: PropTypes.func,
|
||||||
|
className: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Header;
|
export default Header;
|
||||||
|
|||||||
@@ -669,4 +669,74 @@ describe('HeaderCell', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when column.filter is defined', () => {
|
||||||
|
const onFilter = jest.fn();
|
||||||
|
const filterProps = { a: 123 };
|
||||||
|
const Filter = () => <div>test</div>;
|
||||||
|
let column;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
onFilter.mockClear();
|
||||||
|
column = {
|
||||||
|
dataField: 'id',
|
||||||
|
text: 'ID',
|
||||||
|
filter: {
|
||||||
|
props: filterProps,
|
||||||
|
Filter
|
||||||
|
}
|
||||||
|
};
|
||||||
|
wrapper = shallow(<HeaderCell column={ column } index={ index } onFilter={ onFilter } />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render successfully', () => {
|
||||||
|
expect(wrapper.length).toBe(1);
|
||||||
|
expect(wrapper.find('th').length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render filter correctly', () => {
|
||||||
|
expect(wrapper.find(Filter).length).toBe(1);
|
||||||
|
expect(wrapper.find(Filter).props()).toEqual({
|
||||||
|
column,
|
||||||
|
onFilter,
|
||||||
|
...filterProps
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when column.filter and column.filterRenderer is defined', () => {
|
||||||
|
const onExternalFilter = jest.fn();
|
||||||
|
const filterProps = { a: 123 };
|
||||||
|
const Filter = () => <div>test</div>;
|
||||||
|
const filterRenderer = jest.fn().mockReturnValue(<Filter />);
|
||||||
|
let column;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
onExternalFilter.mockClear();
|
||||||
|
filterRenderer.mockClear();
|
||||||
|
column = {
|
||||||
|
dataField: 'id',
|
||||||
|
text: 'ID',
|
||||||
|
filter: {
|
||||||
|
props: filterProps
|
||||||
|
},
|
||||||
|
filterRenderer
|
||||||
|
};
|
||||||
|
wrapper = shallow(
|
||||||
|
<HeaderCell column={ column } index={ index } onExternalFilter={ onExternalFilter } />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render successfully', () => {
|
||||||
|
expect(wrapper.length).toBe(1);
|
||||||
|
expect(wrapper.find('th').length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render filter correctly', () => {
|
||||||
|
expect(wrapper.find(Filter).length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call filterRenderer function correctly', () => {
|
||||||
|
expect(filterRenderer).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,6 +29,25 @@ describe('Header', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('className prop is exists', () => {
|
||||||
|
const className = 'test-class';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = shallow(
|
||||||
|
<Header
|
||||||
|
{ ...mockHeaderResolvedProps }
|
||||||
|
columns={ columns }
|
||||||
|
className={ className }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render successfully', () => {
|
||||||
|
expect(wrapper.length).toBe(1);
|
||||||
|
expect(wrapper.find(`.${className}`).length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('header with columns enable sort', () => {
|
describe('header with columns enable sort', () => {
|
||||||
const sortField = columns[1].dataField;
|
const sortField = columns[1].dataField;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user