diff --git a/docs/migration.md b/docs/migration.md
index 60edbd2..32d137d 100644
--- a/docs/migration.md
+++ b/docs/migration.md
@@ -88,7 +88,7 @@ Please see [available filter configuration](https://react-bootstrap-table.github
- [x] Custom Select Filter
- [X] Number Filter
- [X] Date Filter
-- [ ] Array Filter
+- [X] Array Filter
- [X] Programmatically Filter
Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly.
diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/custom-multi-select-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/custom-multi-select-filter.js
new file mode 100644
index 0000000..0f90ac9
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/column-filter/custom-multi-select-filter.js
@@ -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'
+ })
+}];
+
+
+`;
+
+export default () => (
+
+
+ { sourceCode }
+
+);
diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter-default-value.js b/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter-default-value.js
index 2e2b0e9..d2b4e19 100644
--- a/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter-default-value.js
+++ b/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter-default-value.js
@@ -30,7 +30,7 @@ const columns = [{
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
-import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter';
+import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
const selectOptions = {
0: 'good',
@@ -48,7 +48,7 @@ const columns = [{
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
- filter: selectFilter({
+ filter: multiSelectFilter({
options: selectOptions,
defaultValue: [0, 2]
})
diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter.js
index ac67a69..dce63cd 100644
--- a/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter.js
+++ b/packages/react-bootstrap-table2-example/examples/column-filter/multi-select-filter.js
@@ -29,7 +29,7 @@ const columns = [{
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
-import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter';
+import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
const selectOptions = {
0: 'good',
@@ -47,7 +47,7 @@ const columns = [{
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
- filter: selectFilter({
+ filter: multiSelectFilter({
options: selectOptions
})
}];
diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-multi-select-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-multi-select-filter.js
new file mode 100644
index 0000000..e6c20e3
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-multi-select-filter.js
@@ -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 () => (
+
+
+
+
+);
+`;
+
+export default () => (
+
+
+
+
+ { sourceCode }
+
+);
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index c48dbe9..0617922 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -45,9 +45,10 @@ import CustomFilterValue from 'examples/column-filter/custom-filter-value';
import SelectFilter from 'examples/column-filter/select-filter';
import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value';
import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator';
+import CustomSelectFilter from 'examples/column-filter/custom-select-filter';
import MultiSelectFilter from 'examples/column-filter/multi-select-filter';
import MultiSelectFilterDefaultValue from 'examples/column-filter/multi-select-filter-default-value';
-import CustomSelectFilter from 'examples/column-filter/custom-select-filter';
+import CustomMultiSelectFilter from 'examples/column-filter/custom-multi-select-filter';
import NumberFilter from 'examples/column-filter/number-filter';
import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value';
import CustomNumberFilter from 'examples/column-filter/custom-number-filter';
@@ -58,6 +59,7 @@ import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-
import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter';
import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter';
import ProgrammaticallyDateFilter from 'examples/column-filter/programmatically-date-filter';
+import ProgrammaticallyMultiSelectFilter from 'examples/column-filter/programmatically-multi-select-filter';
import CustomFilter from 'examples/column-filter/custom-filter';
import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter';
@@ -190,11 +192,13 @@ storiesOf('Column Filter', module)
.add('Custom Select Filter', () => )
.add('Custom Number Filter', () => )
.add('Custom Date Filter', () => )
+ .add('Custom MultiSelect Filter', () => )
.add('Custom Filter Value', () => )
.add('Programmatically Text Filter', () => )
.add('Programmatically Select Filter', () => )
.add('Programmatically Number Filter', () => )
.add('Programmatically Date Filter', () => )
+ .add('Programmatically Multi Select Filter', () => )
.add('Custom Filter', () => )
.add('Advance Custom Filter', () => );
diff --git a/packages/react-bootstrap-table2-filter/README.md b/packages/react-bootstrap-table2-filter/README.md
index 6cf47a0..38c278e 100644
--- a/packages/react-bootstrap-table2-filter/README.md
+++ b/packages/react-bootstrap-table2-filter/README.md
@@ -18,6 +18,7 @@ You can get all types of filters via import and these filters are a factory func
* TextFilter
* SelectFilter
+* MultiSelectFilter
* NumberFilter
* DateFilter
* CustomFilter
@@ -114,6 +115,52 @@ const qualityFilter = selectFilter({
// omit...
```
+## MultiSelect Filter
+
+A quick example:
+
+```js
+import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
+
+// omit...
+const selectOptions = {
+ 0: 'good',
+ 1: 'Bad',
+ 2: 'unknown'
+};
+
+const columns = [
+ ..., {
+ dataField: 'quality',
+ text: 'Product Quailty',
+ formatter: cell => selectOptions[cell],
+ filter: multiSelectFilter({
+ options: selectOptions
+ })
+}];
+
+
+```
+
+Following is an example for custom select filter:
+
+```js
+import filterFactory, { multiSelectFilter, Comparator } from 'react-bootstrap-table2-filter';
+// omit...
+
+const qualityFilter = multiSelectFilter({
+ options: selectOptions,
+ placeholder: 'My Custom PlaceHolder', // custom the input placeholder
+ className: 'my-custom-text-filter', // custom classname on input
+ defaultValue: '2', // default filtering value
+ comparator: Comparator.LIKE, // default is Comparator.EQ
+ style: { ... }, // your custom styles on input
+ withoutEmptyOption: true // hide the default select option
+});
+
+// omit...
+```
+
## Number Filter
```js
diff --git a/packages/react-bootstrap-table2-filter/src/components/multiselect.js b/packages/react-bootstrap-table2-filter/src/components/multiselect.js
index 9630bbd..5f4623b 100644
--- a/packages/react-bootstrap-table2-filter/src/components/multiselect.js
+++ b/packages/react-bootstrap-table2-filter/src/components/multiselect.js
@@ -1,5 +1,6 @@
/* 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';
@@ -24,25 +25,24 @@ 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 { column, onFilter, getFilter } = this.props;
+ const { getFilter } = this.props;
const value = getSelections(this.selectInput);
if (value && value.length > 0) {
- onFilter(column, FILTER_TYPE.MULTISELECT)(value);
+ this.applyFilter(value);
}
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
- this.setState(() => ({ isSelected: filterVal.length > 0 }));
this.selectInput.value = filterVal;
-
- onFilter(column, FILTER_TYPE.MULTISELECT)(filterVal);
+ this.applyFilter(filterVal);
});
}
}
@@ -55,10 +55,7 @@ class MultiSelectFilter extends Component {
needFilter = true;
}
if (needFilter) {
- const value = this.selectInput.value;
- if (value) {
- this.props.onFilter(this.props.column, FILTER_TYPE.MULTISELECT)(value);
- }
+ this.applyFilter(this.selectInput.value);
}
}
@@ -78,21 +75,21 @@ class MultiSelectFilter extends Component {
cleanFiltered() {
const value = (this.props.defaultValue !== undefined) ? this.props.defaultValue : [];
- this.setState(() => ({ isSelected: value.length > 0 }));
this.selectInput.value = value;
- this.props.onFilter(this.props.column, FILTER_TYPE.MULTISELECT)(value);
+ this.applyFilter(value);
}
applyFilter(value) {
- this.selectInput.value = 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.setState(() => ({ isSelected: value.length > 0 }));
- this.props.onFilter(this.props.column, FILTER_TYPE.MULTISELECT)(value);
+ this.applyFilter(value);
}
render() {
diff --git a/packages/react-bootstrap-table2-filter/src/filter.js b/packages/react-bootstrap-table2-filter/src/filter.js
index 73d84c4..c1a8666 100644
--- a/packages/react-bootstrap-table2-filter/src/filter.js
+++ b/packages/react-bootstrap-table2-filter/src/filter.js
@@ -191,17 +191,21 @@ export const filterByArray = _ => (
data,
dataField,
{ filterVal, comparator }
-) => (
- data.filter((row) => {
+) => {
+ 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 filterVal.indexOf(cellStr) !== -1;
+ return refinedFilterVal.indexOf(cellStr) !== -1;
}
cellStr = cellStr.toLocaleUpperCase();
- return filterVal.some(item => cellStr.indexOf(item.toLocaleUpperCase()) !== -1);
- })
-);
+ return refinedFilterVal.some(item => cellStr.indexOf(item.toLocaleUpperCase()) !== -1);
+ });
+};
export const filterFactory = _ => (filterType) => {
let filterFn;
diff --git a/packages/react-bootstrap-table2-filter/src/wrapper.js b/packages/react-bootstrap-table2-filter/src/wrapper.js
index 0c8fab7..0602ee8 100644
--- a/packages/react-bootstrap-table2-filter/src/wrapper.js
+++ b/packages/react-bootstrap-table2-filter/src/wrapper.js
@@ -53,8 +53,10 @@ export default (Base, {
const currFilters = Object.assign({}, store.filters);
const { dataField, filter } = column;
- const needClearFilters = !_.isDefined(filterVal) || filterVal === '' ||
- filterVal.length === 0 || (filterVal.length === 1 && filterVal[0] === '');
+ const needClearFilters =
+ !_.isDefined(filterVal) ||
+ filterVal === '' ||
+ filterVal.length === 0;
if (needClearFilters) {
delete currFilters[dataField];
diff --git a/packages/react-bootstrap-table2-filter/test/filter.test.js b/packages/react-bootstrap-table2-filter/test/filter.test.js
index 25f629d..79d0407 100644
--- a/packages/react-bootstrap-table2-filter/test/filter.test.js
+++ b/packages/react-bootstrap-table2-filter/test/filter.test.js
@@ -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', () => {
beforeEach(() => {
filterFn = filters(store, columns, _);
diff --git a/packages/react-bootstrap-table2-filter/test/wrapper.test.js b/packages/react-bootstrap-table2-filter/test/wrapper.test.js
index 1442658..751fc2e 100644
--- a/packages/react-bootstrap-table2-filter/test/wrapper.test.js
+++ b/packages/react-bootstrap-table2-filter/test/wrapper.test.js
@@ -163,7 +163,7 @@ describe('Wrapper', () => {
});
describe('when filterVal is empty or undefined', () => {
- const filterVals = ['', undefined];
+ const filterVals = ['', undefined, []];
it('should setting store object correctly', () => {
filterVals.forEach((filterVal) => {