diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-number-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-number-filter.js new file mode 100644 index 0000000..12a5ebc --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-number-filter.js @@ -0,0 +1,85 @@ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { numberFilter, Comparator } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const products = productsGenerator(8); + +let priceFilter; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price', + filter: numberFilter({ + getFilter: (filter) => { + // pricerFilter was assigned once the component has been mounted. + priceFilter = filter; + } + }) +}]; + +const handleClick = () => { + priceFilter({ + number: 2103, + comparator: Comparator.GT + }); +}; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { numberFilter } from 'react-bootstrap-table2-filter'; + +let priceFilter; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price', + filter: numberFilter({ + getFilter: (filter) => { + // pricerFilter was assigned once the component has been mounted. + priceFilter = filter; + } + }) +}]; + +const handleClick = () => { + priceFilter({ + number: 2103, + comparator: Comparator.GT + }); +}; + +export default () => ( +
+ + + +
+); +`; + +export default () => ( +
+ + + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-select-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-select-filter.js new file mode 100644 index 0000000..d368af5 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-select-filter.js @@ -0,0 +1,96 @@ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { productsQualityGenerator } from 'utils/common'; + +const products = productsQualityGenerator(6); + +let qualityFilter; + +const selectOptions = { + 0: 'good', + 1: 'Bad', + 2: 'unknown' +}; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'quality', + text: 'Product Quality', + formatter: cell => selectOptions[cell], + filter: selectFilter({ + options: selectOptions, + getFilter: (filter) => { + // qualityFilter was assigned once the component has been mounted. + qualityFilter = filter; + } + }) +}]; + +const handleClick = () => { + qualityFilter(0); +}; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter'; + +let qualityFilter; + +const selectOptions = { + 0: 'good', + 1: 'Bad', + 2: 'unknown' +}; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'quality', + text: 'Product Quality', + formatter: cell => selectOptions[cell], + filter: selectFilter({ + options: selectOptions, + getFilter: (filter) => { + // qualityFilter was assigned once the component has been mounted. + qualityFilter = filter; + } + }) +}]; + +const handleClick = () => { + qualityFilter(0); +}; + +export default () => ( +
+ + + +
+); +`; + +export default () => ( +
+ + + + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-text-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-text-filter.js new file mode 100644 index 0000000..f1080c0 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-text-filter.js @@ -0,0 +1,81 @@ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const products = productsGenerator(8); + +let nameFilter; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + filter: textFilter({ + getFilter: (filter) => { + // nameFilter was assigned once the component has been mounted. + nameFilter = filter; + } + }) +}, { + dataField: 'price', + text: 'Product Price', + filter: textFilter() +}]; + +const handleClick = () => { + nameFilter(0); +}; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; + +let nameFilter; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + filter: textFilter({ + getFilter: (filter) => { + // nameFilter was assigned once the component has been mounted. + nameFilter = filter; + } + }) +}, { + dataField: 'price', + text: 'Product Price', + filter: textFilter() +}]; + +const handleClick = () => { + nameFilter(0); +}; + +export default () => ( +
+ + + +
+); +`; + +export default () => ( +
+ + + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 25d8538..7e3ba5e 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -48,6 +48,9 @@ import CustomSelectFilter from 'examples/column-filter/custom-select-filter'; import NumberFilter from 'examples/column-filter/number-filter'; import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value'; import CustomNumberFilter from 'examples/column-filter/custom-number-filter'; +import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-text-filter'; +import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter'; +import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter'; // work on rows import RowStyleTable from 'examples/rows/row-style'; @@ -166,7 +169,10 @@ storiesOf('Column Filter', module) .add('Custom Text Filter', () => ) .add('Custom Select Filter', () => ) .add('Custom Number Filter', () => ) - .add('Custom Filter Value', () => ); + .add('Custom Filter Value', () => ) + .add('Programmatically Text Filter ', () => ) + .add('Programmatically Select Filter ', () => ) + .add('Programmatically Number Filter ', () => ); storiesOf('Work on Rows', module) .add('Customize Row Style', () => ) diff --git a/packages/react-bootstrap-table2-filter/src/components/number.js b/packages/react-bootstrap-table2-filter/src/components/number.js index 3c2b612..ebf55e8 100644 --- a/packages/react-bootstrap-table2-filter/src/components/number.js +++ b/packages/react-bootstrap-table2-filter/src/components/number.js @@ -1,3 +1,4 @@ +/* eslint react/require-default-props: 0 */ /* eslint no-return-assign: 0 */ import React, { Component } from 'react'; @@ -30,11 +31,25 @@ class NumberFilter extends Component { } componentDidMount() { - const { column, onFilter } = this.props; + const { column, onFilter, getFilter } = this.props; const comparator = this.numberFilterComparator.value; const number = this.numberFilter.value; if (comparator && number) { - onFilter(column, { number, comparator }, FILTER_TYPE.NUMBER); + onFilter(column, FILTER_TYPE.NUMBER)({ number, comparator }); + } + + // export onFilter function to allow users to access + if (getFilter) { + getFilter((filterVal) => { + this.setState(() => ({ isSelected: (filterVal !== '') })); + this.numberFilterComparator.value = filterVal.comparator; + this.numberFilter.value = filterVal.number; + + onFilter(column, FILTER_TYPE.NUMBER)({ + number: filterVal.number, + comparator: filterVal.comparator + }); + }); } } @@ -53,7 +68,7 @@ class NumberFilter extends Component { } const filterValue = e.target.value; this.timeout = setTimeout(() => { - onFilter(column, { number: filterValue, comparator }, FILTER_TYPE.NUMBER); + onFilter(column, FILTER_TYPE.NUMBER)({ number: filterValue, comparator }); }, delay); } @@ -65,7 +80,7 @@ class NumberFilter extends Component { // if (comparator === '') { // return; // } - onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER); + onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator }); } onChangeComparator(e) { @@ -75,7 +90,7 @@ class NumberFilter extends Component { // if (value === '') { // return; // } - onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER); + onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator }); } getComparatorOptions() { @@ -116,7 +131,7 @@ class NumberFilter extends Component { this.setState(() => ({ isSelected: (number !== '') })); this.numberFilterComparator.value = comparator; this.numberFilter.value = number; - onFilter(column, { number, comparator }, FILTER_TYPE.NUMBER); + onFilter(column, FILTER_TYPE.NUMBER)({ number, comparator }); } cleanFiltered() { @@ -126,7 +141,7 @@ class NumberFilter extends Component { this.setState(() => ({ isSelected: (value !== '') })); this.numberFilterComparator.value = comparator; this.numberFilter.value = value; - onFilter(column, { number: value, comparator }, FILTER_TYPE.NUMBER); + onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator }); } render() { @@ -224,7 +239,8 @@ NumberFilter.propTypes = { comparatorStyle: PropTypes.object, comparatorClassName: PropTypes.string, numberStyle: PropTypes.object, - numberClassName: PropTypes.string + numberClassName: PropTypes.string, + getFilter: PropTypes.func }; NumberFilter.defaultProps = { diff --git a/packages/react-bootstrap-table2-filter/src/components/select.js b/packages/react-bootstrap-table2-filter/src/components/select.js index 7e17afd..ba2d306 100644 --- a/packages/react-bootstrap-table2-filter/src/components/select.js +++ b/packages/react-bootstrap-table2-filter/src/components/select.js @@ -25,9 +25,21 @@ class SelectFilter extends Component { } componentDidMount() { + const { column, onFilter, getFilter } = this.props; + const value = this.selectInput.value; if (value && value !== '') { - this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); + onFilter(column, FILTER_TYPE.SELECT)(value); + } + + // export onFilter function to allow users to access + if (getFilter) { + getFilter((filterVal) => { + this.setState(() => ({ isSelected: filterVal !== '' })); + this.selectInput.value = filterVal; + + onFilter(column, FILTER_TYPE.SELECT)(filterVal); + }); } } @@ -41,7 +53,7 @@ class SelectFilter extends Component { if (needFilter) { const value = this.selectInput.value; if (value) { - this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); + this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value); } } } @@ -64,19 +76,19 @@ class SelectFilter extends Component { const value = (this.props.defaultValue !== undefined) ? this.props.defaultValue : ''; this.setState(() => ({ isSelected: value !== '' })); this.selectInput.value = value; - this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); + this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value); } applyFilter(value) { this.selectInput.value = value; this.setState(() => ({ isSelected: value !== '' })); - this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); + this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value); } filter(e) { const { value } = e.target; this.setState(() => ({ isSelected: value !== '' })); - this.props.onFilter(this.props.column, value, FILTER_TYPE.SELECT); + this.props.onFilter(this.props.column, FILTER_TYPE.SELECT)(value); } render() { @@ -90,6 +102,7 @@ class SelectFilter extends Component { comparator, withoutEmptyOption, caseSensitive, + getFilter, ...rest } = this.props; @@ -121,7 +134,8 @@ SelectFilter.propTypes = { className: PropTypes.string, withoutEmptyOption: PropTypes.bool, defaultValue: PropTypes.any, - caseSensitive: PropTypes.bool + caseSensitive: PropTypes.bool, + getFilter: PropTypes.func }; SelectFilter.defaultProps = { diff --git a/packages/react-bootstrap-table2-filter/src/components/text.js b/packages/react-bootstrap-table2-filter/src/components/text.js index ffbe702..3af8ae2 100644 --- a/packages/react-bootstrap-table2-filter/src/components/text.js +++ b/packages/react-bootstrap-table2-filter/src/components/text.js @@ -17,10 +17,21 @@ class TextFilter extends Component { value: props.defaultValue }; } + componentDidMount() { + const { onFilter, getFilter, column } = this.props; const defaultValue = this.input.value; + if (defaultValue) { - this.props.onFilter(this.props.column, defaultValue, FILTER_TYPE.TEXT); + onFilter(this.props.column, FILTER_TYPE.TEXT)(defaultValue); + } + + // export onFilter function to allow users to access + if (getFilter) { + getFilter((filterVal) => { + this.setState(() => ({ value: filterVal })); + onFilter(column, FILTER_TYPE.TEXT)(filterVal); + }); } } @@ -40,7 +51,7 @@ class TextFilter extends Component { const filterValue = e.target.value; this.setState(() => ({ value: filterValue })); this.timeout = setTimeout(() => { - this.props.onFilter(this.props.column, filterValue, FILTER_TYPE.TEXT); + this.props.onFilter(this.props.column, FILTER_TYPE.TEXT)(filterValue); }, this.props.delay); } @@ -53,12 +64,12 @@ class TextFilter extends Component { cleanFiltered() { const value = this.props.defaultValue; this.setState(() => ({ value })); - this.props.onFilter(this.props.column, value, FILTER_TYPE.TEXT); + this.props.onFilter(this.props.column, FILTER_TYPE.TEXT)(value); } applyFilter(filterText) { this.setState(() => ({ value: filterText })); - this.props.onFilter(this.props.column, filterText, FILTER_TYPE.TEXT); + this.props.onFilter(this.props.column, FILTER_TYPE.TEXT)(filterText); } handleClick(e) { @@ -77,8 +88,10 @@ class TextFilter extends Component { onFilter, caseSensitive, defaultValue, + getFilter, ...rest } = this.props; + // stopPropagation for onClick event is try to prevent sort was triggered. return ( ( data, dataField, - { filterVal = '', comparator = LIKE, caseSensitive }, + { filterVal: userInput = '', comparator = LIKE, caseSensitive }, customFilterValue -) => - data.filter((row) => { - let cell = _.get(row, dataField); - if (customFilterValue) { - cell = customFilterValue(cell, row); - } - const cellStr = _.isDefined(cell) ? cell.toString() : ''; - if (comparator === EQ) { - return cellStr === filterVal; - } - if (caseSensitive) { - return cellStr.includes(filterVal); - } - return cellStr.toLocaleUpperCase().indexOf(filterVal.toLocaleUpperCase()) !== -1; - }); +) => { + // make sure filter value to be a string + const filterVal = userInput.toString(); + + return ( + data.filter((row) => { + let cell = _.get(row, dataField); + if (customFilterValue) { + cell = customFilterValue(cell, row); + } + const cellStr = _.isDefined(cell) ? cell.toString() : ''; + if (comparator === EQ) { + return cellStr === filterVal; + } + if (caseSensitive) { + return cellStr.includes(filterVal); + } + + return cellStr.toLocaleUpperCase().indexOf(filterVal.toLocaleUpperCase()) !== -1; + }) + ); +}; export const filterByNumber = _ => ( data, dataField, { filterVal: { comparator, number } }, customFilterValue -) => +) => ( data.filter((row) => { if (number === '' || !comparator) return true; let valid = true; @@ -81,7 +88,8 @@ export const filterByNumber = _ => ( } } return valid; - }); + }) +); 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 f48e031..f13e615 100644 --- a/packages/react-bootstrap-table2-filter/src/wrapper.js +++ b/packages/react-bootstrap-table2-filter/src/wrapper.js @@ -40,33 +40,43 @@ export default (Base, { } } - onFilter(column, filterVal, filterType) { - const { store, columns } = this.props; - const currFilters = Object.assign({}, this.state.currFilters); - const { dataField, filter } = column; + /** + * filter the table like below: + * onFilter(column, filterType)(filterVal) + * @param {Object} column + * @param {String} filterType + * @param {String} filterVal - user input for filtering. + */ + onFilter(column, filterType) { + return (filterVal) => { + const { store, columns } = this.props; + const currFilters = Object.assign({}, this.state.currFilters); + const { dataField, filter } = column; - if (!_.isDefined(filterVal) || filterVal === '') { - delete currFilters[dataField]; - } else { - // select default comparator is EQ, others are LIKE - const { - comparator = (filterType === FILTER_TYPE.SELECT ? EQ : LIKE), - caseSensitive = false - } = filter.props; - currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive }; - } - store.filters = currFilters; + if (!_.isDefined(filterVal) || filterVal === '') { + delete currFilters[dataField]; + } else { + // select default comparator is EQ, others are LIKE + const { + comparator = (filterType === FILTER_TYPE.SELECT ? EQ : LIKE), + caseSensitive = false + } = filter.props; + currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive }; + } - if (this.isRemoteFiltering() || this.isRemotePagination()) { - this.handleRemoteFilterChange(); - // when remote filtering is enable, dont set currFilters state - // in the componentWillReceiveProps, - // it's the key point that we can know the filter is changed - return; - } + store.filters = currFilters; - store.filteredData = filters(store, columns, _)(currFilters); - this.setState(() => ({ currFilters, isDataChanged: true })); + if (this.isRemoteFiltering() || this.isRemotePagination()) { + this.handleRemoteFilterChange(); + // when remote filtering is enable, dont set currFilters state + // in the componentWillReceiveProps, + // it's the key point that we can know the filter is changed + return; + } + + store.filteredData = filters(store, columns, _)(currFilters); + this.setState(() => ({ currFilters, isDataChanged: true })); + }; } render() { diff --git a/packages/react-bootstrap-table2-filter/test/components/number.test.js b/packages/react-bootstrap-table2-filter/test/components/number.test.js index 6ba66f7..3586ec4 100644 --- a/packages/react-bootstrap-table2-filter/test/components/number.test.js +++ b/packages/react-bootstrap-table2-filter/test/components/number.test.js @@ -9,7 +9,11 @@ import * as Comparator from '../../src/comparison'; describe('Number Filter', () => { let wrapper; + + // onFilter(x)(y) = filter result const onFilter = sinon.stub(); + const onFilterFirstReturn = sinon.stub(); + const column = { dataField: 'price', text: 'Product Price' @@ -17,6 +21,9 @@ describe('Number Filter', () => { afterEach(() => { onFilter.reset(); + onFilterFirstReturn.reset(); + + onFilter.returns(onFilterFirstReturn); }); describe('initialization', () => { @@ -90,6 +97,36 @@ describe('Number Filter', () => { }); }); + describe('when props.getFilter is defined', () => { + let programmaticallyFilter; + + const comparator = Comparator.EQ; + const number = 123; + + const getFilter = (filter) => { + programmaticallyFilter = filter; + }; + + beforeEach(() => { + wrapper = mount( + + ); + + programmaticallyFilter({ comparator, number }); + }); + + it('should do onFilter correctly when exported function was executed', () => { + expect(onFilter.calledOnce).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.NUMBER)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith({ comparator, number })).toBeTruthy(); + }); + + it('should setState correctly when exported function was executed', () => { + expect(wrapper.state().isSelected).toBeTruthy(); + }); + }); + describe('when defaultValue.number and defaultValue.comparator props is defined', () => { const number = 203; const comparator = Comparator.EQ; @@ -110,8 +147,9 @@ describe('Number Filter', () => { it('should calling onFilter on componentDidMount', () => { expect(onFilter.calledOnce).toBeTruthy(); - expect(onFilter.calledWith( - column, { number: `${number}`, comparator }, FILTER_TYPE.NUMBER)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.NUMBER)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith({ number: `${number}`, comparator })).toBeTruthy(); }); }); diff --git a/packages/react-bootstrap-table2-filter/test/components/select.test.js b/packages/react-bootstrap-table2-filter/test/components/select.test.js index 2ed39a6..85f9686 100644 --- a/packages/react-bootstrap-table2-filter/test/components/select.test.js +++ b/packages/react-bootstrap-table2-filter/test/components/select.test.js @@ -9,19 +9,27 @@ import { FILTER_TYPE } from '../../src/const'; describe('Select Filter', () => { let wrapper; let instance; + + // onFilter(x)(y) = filter result const onFilter = sinon.stub(); + const onFilterFirstReturn = sinon.stub(); + const column = { dataField: 'quality', text: 'Product Quality' }; + const options = { 0: 'Bad', 1: 'Good', - 2: 'Unknow' + 2: 'Unknown' }; afterEach(() => { onFilter.reset(); + onFilterFirstReturn.reset(); + + onFilter.returns(onFilterFirstReturn); }); describe('initialization', () => { @@ -83,11 +91,48 @@ describe('Select Filter', () => { it('should calling onFilter on componentDidMount', () => { expect(onFilter.calledOnce).toBeTruthy(); - expect(onFilter.calledWith(column, defaultValue, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).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( + + ); + instance = wrapper.instance(); + + programmaticallyFilter(filterValue); + }); + + it('should do onFilter correctly when exported function was executed', () => { + expect(onFilter.calledOnce).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith(filterValue)).toBeTruthy(); + }); + + it('should setState correctly when exported function was executed', () => { + expect(instance.state.isSelected).toBeTruthy(); + }); + }); + describe('when placeholder is defined', () => { const placeholder = 'test'; beforeEach(() => { @@ -170,8 +215,9 @@ describe('Select Filter', () => { it('should update', () => { expect(onFilter.callCount).toBe(2); - expect(onFilter.calledWith( - column, instance.props.defaultValue, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilterFirstReturn.callCount).toBe(2); + expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy(); }); }); @@ -198,8 +244,9 @@ describe('Select Filter', () => { it('should update', () => { expect(onFilter.callCount).toBe(2); - expect(onFilter.calledWith( - column, instance.props.defaultValue, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilterFirstReturn.callCount).toBe(2); + expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy(); }); }); }); @@ -226,7 +273,9 @@ describe('Select Filter', () => { it('should calling onFilter correctly', () => { expect(onFilter.callCount).toBe(2); - expect(onFilter.calledWith(column, defaultValue, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilterFirstReturn.callCount).toBe(2); + expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy(); }); }); @@ -249,6 +298,7 @@ describe('Select Filter', () => { it('should calling onFilter correctly', () => { expect(onFilter.callCount).toBe(1); + expect(onFilterFirstReturn.callCount).toBe(1); }); }); }); @@ -268,8 +318,10 @@ describe('Select Filter', () => { }); it('should calling onFilter correctly', () => { - expect(onFilter.callCount).toBe(1); - expect(onFilter.calledWith(column, value, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilter.calledOnce).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith(value)).toBeTruthy(); }); }); @@ -289,8 +341,10 @@ describe('Select Filter', () => { }); it('should calling onFilter correctly', () => { - expect(onFilter.callCount).toBe(1); - expect(onFilter.calledWith(column, event.target.value, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilter.calledOnce).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.SELECT)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith(event.target.value)).toBeTruthy(); }); }); }); diff --git a/packages/react-bootstrap-table2-filter/test/components/text.test.js b/packages/react-bootstrap-table2-filter/test/components/text.test.js index 4a9c282..5a8d297 100644 --- a/packages/react-bootstrap-table2-filter/test/components/text.test.js +++ b/packages/react-bootstrap-table2-filter/test/components/text.test.js @@ -9,7 +9,11 @@ jest.useFakeTimers(); describe('Text Filter', () => { let wrapper; let instance; + + // onFilter(x)(y) = filter result const onFilter = sinon.stub(); + const onFilterFirstReturn = sinon.stub(); + const column = { dataField: 'price', text: 'Price' @@ -17,6 +21,9 @@ describe('Text Filter', () => { afterEach(() => { onFilter.reset(); + onFilterFirstReturn.reset(); + + onFilter.returns(onFilterFirstReturn); }); describe('initialization', () => { @@ -58,7 +65,39 @@ describe('Text Filter', () => { it('should calling onFilter on componentDidMount', () => { expect(onFilter.calledOnce).toBeTruthy(); - expect(onFilter.calledWith(column, defaultValue, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).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( + + ); + instance = wrapper.instance(); + + programmaticallyFilter(filterValue); + }); + + it('should do onFilter correctly when exported function was executed', () => { + expect(onFilter.calledOnce).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith(filterValue)).toBeTruthy(); + }); + + it('should setState correctly when exported function was executed', () => { + expect(instance.state.value).toEqual(filterValue); }); }); @@ -114,7 +153,9 @@ describe('Text Filter', () => { it('should calling onFilter correctly when props.defaultValue is changed', () => { expect(onFilter.calledOnce).toBeTruthy(); - expect(onFilter.calledWith(column, nextDefaultValue, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith(nextDefaultValue)).toBeTruthy(); }); }); @@ -133,8 +174,9 @@ describe('Text Filter', () => { it('should calling onFilter correctly', () => { expect(onFilter.calledOnce).toBeTruthy(); - expect(onFilter.calledWith( - column, instance.props.defaultValue, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy(); }); }); @@ -154,7 +196,9 @@ describe('Text Filter', () => { it('should calling onFilter correctly', () => { expect(onFilter.calledOnce).toBeTruthy(); - expect(onFilter.calledWith(column, filterText, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilter.calledWith(column, FILTER_TYPE.TEXT)).toBeTruthy(); + expect(onFilterFirstReturn.calledOnce).toBeTruthy(); + expect(onFilterFirstReturn.calledWith(filterText)).toBeTruthy(); }); }); diff --git a/packages/react-bootstrap-table2-filter/test/filter.test.js b/packages/react-bootstrap-table2-filter/test/filter.test.js index ae66a38..2ffd36d 100644 --- a/packages/react-bootstrap-table2-filter/test/filter.test.js +++ b/packages/react-bootstrap-table2-filter/test/filter.test.js @@ -42,6 +42,19 @@ describe('filter', () => { filterFn = filters(store, columns, _); }); + describe('when filter value is not a String', () => { + it('should transform to string and do the filter', () => { + currFilters.name = { + filterVal: 3, + filterType: FILTER_TYPE.TEXT + }; + + const result = filterFn(currFilters); + expect(result).toBeDefined(); + expect(result).toHaveLength(2); + }); + }); + describe(`when default comparator is ${LIKE}`, () => { it('should returning correct result', () => { currFilters.name = { diff --git a/packages/react-bootstrap-table2-filter/test/wrapper.test.js b/packages/react-bootstrap-table2-filter/test/wrapper.test.js index 3d86285..1442658 100644 --- a/packages/react-bootstrap-table2-filter/test/wrapper.test.js +++ b/packages/react-bootstrap-table2-filter/test/wrapper.test.js @@ -167,14 +167,14 @@ describe('Wrapper', () => { it('should setting store object correctly', () => { filterVals.forEach((filterVal) => { - instance.onFilter(props.columns[1], filterVal, FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal); expect(props.store.filtering).toBeFalsy(); }); }); it('should setting state correctly', () => { filterVals.forEach((filterVal) => { - instance.onFilter(props.columns[1], filterVal, FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal); expect(instance.state.isDataChanged).toBeTruthy(); expect(Object.keys(instance.state.currFilters)).toHaveLength(0); }); @@ -185,12 +185,12 @@ describe('Wrapper', () => { const filterVal = '3'; it('should setting store object correctly', () => { - instance.onFilter(props.columns[1], filterVal, FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal); expect(props.store.filters).toEqual(instance.state.currFilters); }); it('should setting state correctly', () => { - instance.onFilter(props.columns[1], filterVal, FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal); expect(instance.state.isDataChanged).toBeTruthy(); expect(Object.keys(instance.state.currFilters)).toHaveLength(1); }); @@ -203,7 +203,7 @@ describe('Wrapper', () => { props = createTableProps(); props.remote = { filter: true }; createFilterWrapper(props); - instance.onFilter(props.columns[1], filterVal, FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(filterVal); }); it('should not setting store object correctly', () => { @@ -222,27 +222,27 @@ describe('Wrapper', () => { describe('combination', () => { it('should setting store object correctly', () => { - instance.onFilter(props.columns[1], '3', FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)('3'); expect(props.store.filters).toEqual(instance.state.currFilters); expect(instance.state.isDataChanged).toBeTruthy(); expect(Object.keys(instance.state.currFilters)).toHaveLength(1); - instance.onFilter(props.columns[1], '2', FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)('2'); expect(props.store.filters).toEqual(instance.state.currFilters); expect(instance.state.isDataChanged).toBeTruthy(); expect(Object.keys(instance.state.currFilters)).toHaveLength(1); - instance.onFilter(props.columns[2], '2', FILTER_TYPE.TEXT); + instance.onFilter(props.columns[2], FILTER_TYPE.TEXT)('2'); expect(props.store.filters).toEqual(instance.state.currFilters); expect(instance.state.isDataChanged).toBeTruthy(); expect(Object.keys(instance.state.currFilters)).toHaveLength(2); - instance.onFilter(props.columns[2], '', FILTER_TYPE.TEXT); + instance.onFilter(props.columns[2], FILTER_TYPE.TEXT)(''); expect(props.store.filters).toEqual(instance.state.currFilters); expect(instance.state.isDataChanged).toBeTruthy(); expect(Object.keys(instance.state.currFilters)).toHaveLength(1); - instance.onFilter(props.columns[1], '', FILTER_TYPE.TEXT); + instance.onFilter(props.columns[1], FILTER_TYPE.TEXT)(''); expect(props.store.filters).toEqual(instance.state.currFilters); expect(instance.state.isDataChanged).toBeTruthy(); expect(Object.keys(instance.state.currFilters)).toHaveLength(0);