diff --git a/docs/columns.md b/docs/columns.md index e7b477b..4b78e36 100644 --- a/docs/columns.md +++ b/docs/columns.md @@ -648,6 +648,8 @@ Configure `column.filter` will able to setup a column level filter on the header * Text(`textFilter`) * Select(`selectFilter`) +* Number(`numberFilter`) +* Date(`dateFilter`) We have a quick example to show you how to use `column.filter`: diff --git a/docs/migration.md b/docs/migration.md index a9d2104..5385172 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -72,7 +72,7 @@ Due to no `TableHeaderColumn` so that no `dataSort` here, please add [`sort`](ht Please see [Work with selection](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/basic-row-select.html). Please see [available selectRow configurations](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/row-select-props.html). -No huge change for row selection, but can not custom the selection column currently. Coming soon!!! +No huge change for row selection. ## Column Filter @@ -87,9 +87,9 @@ Please see [available filter configuration](https://react-bootstrap-table.github - [x] Select Filter - [x] Custom Select Filter - [X] Number Filter -- [ ] Date Filter +- [X] Date Filter - [ ] Array Filter -- [ ] Programmatically 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-date-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/custom-date-filter.js new file mode 100644 index 0000000..226708d --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/custom-date-filter.js @@ -0,0 +1,77 @@ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { stockGenerator } from 'utils/common'; + +const stocks = stockGenerator(8); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter({ + delay: 400, + placeholder: 'custom placeholder', + withoutEmptyComparatorOption: true, + comparators: [Comparator.EQ, Comparator.GT, Comparator.LT], + style: { display: 'inline-grid' }, + className: 'custom-datefilter-class', + comparatorStyle: { backgroundColor: 'antiquewhite' }, + comparatorClassName: 'custom-comparator-class', + dateStyle: { backgroundColor: 'cadetblue', margin: '0px' }, + dateClassName: 'custom-date-class' + }) +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter({ + delay: 400, + placeholder: 'custom placeholder', + withoutEmptyComparatorOption: true, + comparators: [Comparator.EQ, Comparator.GT, Comparator.LT], + style: { display: 'inline-grid' }, + className: 'custom-datefilter-class', + comparatorStyle: { backgroundColor: 'antiquewhite' }, + comparatorClassName: 'custom-comparator-class', + dateStyle: { backgroundColor: 'cadetblue', margin: '0px' }, + dateClassName: 'custom-date-class' + }) +}]; + + +`; + +export default () => ( +
+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/date-filter-default-value.js b/packages/react-bootstrap-table2-example/examples/column-filter/date-filter-default-value.js new file mode 100644 index 0000000..c292531 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/date-filter-default-value.js @@ -0,0 +1,59 @@ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { stockGenerator } from 'utils/common'; + +const stocks = stockGenerator(8); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter({ + defaultValue: { date: new Date(2018, 0, 1), comparator: Comparator.GT } + }) +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter({ + defaultValue: { date: new Date(2018, 0, 1), comparator: Comparator.GT } + }) +}]; + + +`; + +export default () => ( +
+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/date-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/date-filter.js new file mode 100644 index 0000000..24cd05b --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/date-filter.js @@ -0,0 +1,55 @@ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { stockGenerator } from 'utils/common'; + +const stocks = stockGenerator(8); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter() +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter() +}]; + + +`; + +export default () => ( +
+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-date-filter.js b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-date-filter.js new file mode 100644 index 0000000..d620120 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/programmatically-date-filter.js @@ -0,0 +1,85 @@ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { stockGenerator } from 'utils/common'; + +const stocks = stockGenerator(8); + +let inStockDateFilter; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter({ + getFilter: (filter) => { + // inStockDateFilter was assigned once the component has been mounted. + inStockDateFilter = filter; + } + }) +}]; + +const handleClick = () => { + inStockDateFilter({ + date: new Date(2018, 0, 1), + comparator: Comparator.GT + }); +}; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { dateFilter, Comparator } from 'react-bootstrap-table2-filter'; + +let inStockDateFilter; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'inStockDate', + text: 'InStock Date', + filter: dateFilter({ + getFilter: (filter) => { + // inStockDateFilter was assigned once the component has been mounted. + inStockDateFilter = filter; + } + }) +}]; + +const handleClick = () => { + inStockDateFilter({ + date: new Date(2018, 0, 1), + comparator: Comparator.GT + }); +}; + +export default () => ( +
+ + + +
+); +`; + +export default () => ( +
+ + + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index a3fe4c3..08eaf85 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -48,9 +48,13 @@ 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 DateFilter from 'examples/column-filter/date-filter'; +import DateFilterWithDefaultValue from 'examples/column-filter/date-filter-default-value'; +import CustomDateFilter from 'examples/column-filter/custom-date-filter'; import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-text-filter'; import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter'; import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter'; +import ProgrammaticallyDateFilter from 'examples/column-filter/programmatically-date-filter'; // work on rows import RowStyleTable from 'examples/rows/row-style'; @@ -172,13 +176,17 @@ storiesOf('Column Filter', module) .add('Select Filter with Comparator', () => ) .add('Number Filter', () => ) .add('Number Filter with Default Value', () => ) + .add('Date Filter', () => ) + .add('Date Filter with Default Value', () => ) .add('Custom Text Filter', () => ) .add('Custom Select Filter', () => ) .add('Custom Number Filter', () => ) + .add('Custom Date Filter', () => ) .add('Custom Filter Value', () => ) - .add('Programmatically Text Filter ', () => ) - .add('Programmatically Select Filter ', () => ) - .add('Programmatically Number Filter ', () => ); + .add('Programmatically Text Filter', () => ) + .add('Programmatically Select Filter', () => ) + .add('Programmatically Number Filter', () => ) + .add('Programmatically Date Filter', () => ); storiesOf('Work on Rows', module) .add('Customize Row Style', () => ) diff --git a/packages/react-bootstrap-table2-filter/README.md b/packages/react-bootstrap-table2-filter/README.md index 07561bf..3fe8488 100644 --- a/packages/react-bootstrap-table2-filter/README.md +++ b/packages/react-bootstrap-table2-filter/README.md @@ -19,6 +19,7 @@ You can get all types of filters via import and these filters are a factory func * TextFilter * SelectFilter * NumberFilter +* DateFilter * **Coming soon!** ## Add CSS @@ -148,5 +149,44 @@ const numberFilter = numberFilter({ defaultValue: { number: 2103, comparator: Comparator.GT } // default value }) +// omit... +``` + +## Date Filter + +```js +import filterFactory, { dateFilter } from 'react-bootstrap-table2-filter'; + +const columns = [..., { + dataField: 'date', + text: 'Product date', + filter: dateFilter() +}]; + + +``` + +> **Notes:** date filter accept a Javascript Date object in your raw data. + +Date filter is same as other filter, you can custom the number filter via `dateFilter` factory function: + +```js +import filterFactory, { selectFilter, Comparator } from 'react-bootstrap-table2-filter'; +// omit... + +const dateFilter = dateFilter({ + delay: 600, // how long will trigger filtering after user typing, default is 500 ms + placeholder: 'custom placeholder', // placeholder for date input + withoutEmptyComparatorOption: true, // dont render empty option for comparator + comparators: [Comparator.EQ, Comparator.GT, Comparator.LT], // Custom the comparators + style: { display: 'inline-grid' }, // custom the style on date filter + className: 'custom-dateFilter-class', // custom the class on date filter + comparatorStyle: { backgroundColor: 'antiquewhite' }, // custom the style on comparator select + comparatorClassName: 'custom-comparator-class', // custom the class on comparator select + dateStyle: { backgroundColor: 'cadetblue', margin: '0px' }, // custom the style on date input + dateClassName: 'custom-date-class', // custom the class on date input + defaultValue: { date: new Date(2018, 0, 1), comparator: Comparator.GT } // default value +}) + // omit... ``` \ No newline at end of file diff --git a/packages/react-bootstrap-table2-filter/index.js b/packages/react-bootstrap-table2-filter/index.js index a2bc666..0f17bd3 100644 --- a/packages/react-bootstrap-table2-filter/index.js +++ b/packages/react-bootstrap-table2-filter/index.js @@ -1,6 +1,7 @@ import TextFilter from './src/components/text'; import SelectFilter from './src/components/select'; import NumberFilter from './src/components/number'; +import DateFilter from './src/components/date'; import wrapperFactory from './src/wrapper'; import * as Comparison from './src/comparison'; @@ -25,3 +26,8 @@ export const numberFilter = (props = {}) => ({ Filter: NumberFilter, props }); + +export const dateFilter = (props = {}) => ({ + Filter: DateFilter, + props +}); diff --git a/packages/react-bootstrap-table2-filter/src/components/date.js b/packages/react-bootstrap-table2-filter/src/components/date.js new file mode 100644 index 0000000..adff6c6 --- /dev/null +++ b/packages/react-bootstrap-table2-filter/src/components/date.js @@ -0,0 +1,204 @@ +/* eslint react/require-default-props: 0 */ +/* eslint no-return-assign: 0 */ +/* eslint prefer-template: 0 */ +import React, { Component } from 'react'; +import { PropTypes } from 'prop-types'; + +import * as Comparator from '../comparison'; +import { FILTER_TYPE } from '../const'; + +const legalComparators = [ + Comparator.EQ, + Comparator.NE, + Comparator.GT, + Comparator.GE, + Comparator.LT, + Comparator.LE +]; + +function dateParser(d) { + return `${d.getFullYear()}-${('0' + (d.getMonth() + 1)).slice(-2)}-${('0' + d.getDate()).slice(-2)}`; +} + +class DateFilter extends Component { + constructor(props) { + super(props); + this.timeout = null; + this.comparators = props.comparators || legalComparators; + this.applyFilter = this.applyFilter.bind(this); + this.onChangeDate = this.onChangeDate.bind(this); + this.onChangeComparator = this.onChangeComparator.bind(this); + } + + componentDidMount() { + const { getFilter } = this.props; + const comparator = this.dateFilterComparator.value; + const date = this.inputDate.value; + if (comparator && date) { + this.applyFilter(date, comparator); + } + + // export onFilter function to allow users to access + if (getFilter) { + getFilter((filterVal) => { + this.dateFilterComparator.value = filterVal.comparator; + this.inputDate.value = dateParser(filterVal.date); + + this.applyFilter(filterVal.date, filterVal.comparator); + }); + } + } + + componentWillUnmount() { + if (this.timeout) clearTimeout(this.timeout); + } + + onChangeDate(e) { + const comparator = this.dateFilterComparator.value; + const filterValue = e.target.value; + this.applyFilter(filterValue, comparator); + } + + onChangeComparator(e) { + const value = this.inputDate.value; + const comparator = e.target.value; + this.applyFilter(value, comparator); + } + + getComparatorOptions() { + const optionTags = []; + const { withoutEmptyComparatorOption } = this.props; + if (!withoutEmptyComparatorOption) { + optionTags.push( + ); + } + return optionTags; + } + + getDefaultDate() { + let defaultDate = ''; + const { defaultValue } = this.props; + if (defaultValue && defaultValue.date) { + // Set the appropriate format for the input type=date, i.e. "YYYY-MM-DD" + defaultDate = dateParser(new Date(defaultValue.date)); + } + return defaultDate; + } + + applyFilter(value, comparator) { + if (!comparator || !value) { + return; + } + const { column, onFilter, delay } = this.props; + const execute = () => { + const date = typeof value !== 'object' ? new Date(value) : value; + onFilter(column, FILTER_TYPE.DATE)({ date, comparator }); + }; + if (delay) { + this.timeout = setTimeout(() => { execute(); }, delay); + } else { + execute(); + } + } + + render() { + const { + placeholder, + column: { text }, + style, + comparatorStyle, + dateStyle, + className, + comparatorClassName, + dateClassName, + defaultValue + } = this.props; + + return ( +
+ + this.inputDate = n } + className={ `filter date-filter-input form-control ${dateClassName}` } + style={ dateStyle } + type="date" + onChange={ this.onChangeDate } + placeholder={ placeholder || `Enter ${text}...` } + defaultValue={ this.getDefaultDate() } + /> +
+ ); + } +} + +DateFilter.propTypes = { + onFilter: PropTypes.func.isRequired, + column: PropTypes.object.isRequired, + delay: PropTypes.number, + defaultValue: PropTypes.shape({ + date: PropTypes.oneOfType([PropTypes.object]), + comparator: PropTypes.oneOf([...legalComparators, '']) + }), + /* eslint consistent-return: 0 */ + comparators: (props, propName) => { + if (!props[propName]) { + return; + } + for (let i = 0; i < props[propName].length; i += 1) { + let comparatorIsValid = false; + for (let j = 0; j < legalComparators.length; j += 1) { + if (legalComparators[j] === props[propName][i] || props[propName][i] === '') { + comparatorIsValid = true; + break; + } + } + if (!comparatorIsValid) { + return new Error(`Date comparator provided is not supported. + Use only ${legalComparators}`); + } + } + }, + placeholder: PropTypes.string, + withoutEmptyComparatorOption: PropTypes.bool, + style: PropTypes.object, + comparatorStyle: PropTypes.object, + dateStyle: PropTypes.object, + className: PropTypes.string, + comparatorClassName: PropTypes.string, + dateClassName: PropTypes.string, + getFilter: PropTypes.func +}; + +DateFilter.defaultProps = { + delay: 0, + defaultValue: { + date: undefined, + comparator: '' + }, + withoutEmptyComparatorOption: false, + comparators: legalComparators, + placeholder: undefined, + style: undefined, + className: '', + comparatorStyle: undefined, + comparatorClassName: '', + dateStyle: undefined, + dateClassName: '' +}; + + +export default DateFilter; diff --git a/packages/react-bootstrap-table2-filter/src/const.js b/packages/react-bootstrap-table2-filter/src/const.js index a459270..ccb4d78 100644 --- a/packages/react-bootstrap-table2-filter/src/const.js +++ b/packages/react-bootstrap-table2-filter/src/const.js @@ -1,7 +1,8 @@ export const FILTER_TYPE = { TEXT: 'TEXT', SELECT: 'SELECT', - NUMBER: 'NUMBER' + NUMBER: 'NUMBER', + DATE: 'DATE' }; export const FILTER_DELAY = 500; diff --git a/packages/react-bootstrap-table2-filter/src/filter.js b/packages/react-bootstrap-table2-filter/src/filter.js index 9f7812c..a65d52a 100644 --- a/packages/react-bootstrap-table2-filter/src/filter.js +++ b/packages/react-bootstrap-table2-filter/src/filter.js @@ -91,6 +91,102 @@ export const filterByNumber = _ => ( }) ); +export const filterByDate = _ => ( + data, + dataField, + { filterVal: { comparator, date } }, + customFilterValue +) => { + if (!date || !comparator) return data; + const filterDate = date.getDate(); + const filterMonth = date.getMonth(); + const filterYear = date.getFullYear(); + + return data.filter((row) => { + let valid = true; + let cell = _.get(row, dataField); + + if (customFilterValue) { + cell = customFilterValue(cell, row); + } + + if (typeof cell !== 'object') { + cell = new Date(cell); + } + + const targetDate = cell.getDate(); + const targetMonth = cell.getMonth(); + const targetYear = cell.getFullYear(); + + + switch (comparator) { + case EQ: { + if ( + filterDate !== targetDate || + filterMonth !== targetMonth || + filterYear !== targetYear + ) { + valid = false; + } + break; + } + case GT: { + if (cell <= date) { + valid = false; + } + break; + } + case GE: { + if (targetYear < filterYear) { + valid = false; + } else if (targetYear === filterYear && + targetMonth < filterMonth) { + valid = false; + } else if (targetYear === filterYear && + targetMonth === filterMonth && + targetDate < filterDate) { + valid = false; + } + break; + } + case LT: { + if (cell >= date) { + valid = false; + } + break; + } + case LE: { + if (targetYear > filterYear) { + valid = false; + } else if (targetYear === filterYear && + targetMonth > filterMonth) { + valid = false; + } else if (targetYear === filterYear && + targetMonth === filterMonth && + targetDate > filterDate) { + valid = false; + } + break; + } + case NE: { + if ( + filterDate === targetDate && + filterMonth === targetMonth && + filterYear === targetYear + ) { + valid = false; + } + break; + } + default: { + console.error('Date comparator provided is not supported'); + break; + } + } + return valid; + }); +}; + export const filterFactory = _ => (filterType) => { let filterFn; switch (filterType) { @@ -101,6 +197,9 @@ export const filterFactory = _ => (filterType) => { case FILTER_TYPE.NUMBER: filterFn = filterByNumber(_); break; + case FILTER_TYPE.DATE: + filterFn = filterByDate(_); + break; default: filterFn = filterByText(_); } diff --git a/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss b/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss index 70d4a03..e2304b6 100644 --- a/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss +++ b/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss @@ -5,7 +5,8 @@ .react-bootstrap-table > table > thead > tr > th .select-filter option[value=''], .react-bootstrap-table > table > thead > tr > th .select-filter.placeholder-selected, .react-bootstrap-table > table > thead > tr > th .filter::-webkit-input-placeholder, -.react-bootstrap-table > table > thead > tr > th .number-filter-input::-webkit-input-placeholder { +.react-bootstrap-table > table > thead > tr > th .number-filter-input::-webkit-input-placeholder, +.react-bootstrap-table > table > thead > tr > th .date-filter-input::-webkit-input-placeholder { color: lightgrey; font-style: italic; } @@ -15,17 +16,20 @@ font-style: initial; } -.react-bootstrap-table > table > thead > tr > th .number-filter { +.react-bootstrap-table > table > thead > tr > th .number-filter, +.react-bootstrap-table > table > thead > tr > th .date-filter { display: flex; } -.react-bootstrap-table > table > thead > tr > th .number-filter-input { +.react-bootstrap-table > table > thead > tr > th .number-filter-input, +.react-bootstrap-table > table > thead > tr > th .date-filter-input { margin-left: 5px; float: left; width: calc(100% - 67px - 5px); } -.react-bootstrap-table > table > thead > tr > th .number-filter-comparator { +.react-bootstrap-table > table > thead > tr > th .number-filter-comparator, +.react-bootstrap-table > table > thead > tr > th .date-filter-comparator { width: 67px; float: left; } \ No newline at end of file diff --git a/packages/react-bootstrap-table2-filter/test/components/date.test.js b/packages/react-bootstrap-table2-filter/test/components/date.test.js new file mode 100644 index 0000000..366a675 --- /dev/null +++ b/packages/react-bootstrap-table2-filter/test/components/date.test.js @@ -0,0 +1,264 @@ +import 'jsdom-global/register'; +import React from 'react'; +import { mount } from 'enzyme'; +import DateFilter from '../../src/components/date'; +import { FILTER_TYPE } from '../../src/const'; +import * as Comparator from '../../src/comparison'; + + +describe('Date Filter', () => { + let wrapper; + + + const onFilterFirstReturn = jest.fn(); + const onFilter = jest.fn().mockReturnValue(onFilterFirstReturn); + + const column = { + dataField: 'price', + text: 'Product Price' + }; + + afterEach(() => { + onFilter.mockClear(); + onFilterFirstReturn.mockClear(); + + // onFilter.returns(onFilterFirstReturn); + }); + + describe('initialization', () => { + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering component successfully', () => { + expect(wrapper).toHaveLength(1); + expect(wrapper.find('.date-filter-input')).toHaveLength(1); + expect(wrapper.find('.date-filter-comparator')).toHaveLength(1); + expect(wrapper.find('.date-filter')).toHaveLength(1); + }); + + it('should rendering comparator options correctly', () => { + const select = wrapper.find('select'); + expect(select.find('option')).toHaveLength(wrapper.prop('comparators').length + 1); + }); + }); + + describe('when withoutEmptyComparatorOption prop is true', () => { + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering comparator options correctly', () => { + const select = wrapper.find('.date-filter-comparator'); + expect(select.find('option')).toHaveLength(wrapper.prop('comparators').length); + }); + }); + + describe('when defaultValue.date props is defined', () => { + const date = new Date(2018, 0, 1); + + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering input successfully', () => { + expect(wrapper).toHaveLength(1); + const input = wrapper.find('.date-filter-input'); + expect(input).toHaveLength(1); + expect(input.props().defaultValue).toEqual(wrapper.instance().getDefaultDate()); + }); + }); + + describe('when defaultValue.comparator props is defined', () => { + const comparator = Comparator.EQ; + + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering comparator select successfully', () => { + expect(wrapper).toHaveLength(1); + const select = wrapper.find('.date-filter-comparator'); + expect(select).toHaveLength(1); + expect(select.props().defaultValue).toEqual(comparator); + }); + }); + + describe('when props.getFilter is defined', () => { + let programmaticallyFilter; + + const comparator = Comparator.EQ; + const date = new Date(2018, 0, 1); + + const getFilter = (filter) => { + programmaticallyFilter = filter; + }; + + beforeEach(() => { + wrapper = mount( + + ); + + programmaticallyFilter({ comparator, date }); + }); + + it('should do onFilter correctly when exported function was executed', () => { + expect(onFilter).toHaveBeenCalledTimes(1); + expect(onFilter).toHaveBeenCalledWith(column, FILTER_TYPE.DATE); + expect(onFilterFirstReturn).toHaveBeenCalledTimes(1); + expect(onFilterFirstReturn).toHaveBeenCalledWith({ comparator, date }); + }); + }); + + describe('when defaultValue.number and defaultValue.comparator props are defined', () => { + let date; + let comparator; + + beforeEach(() => { + date = new Date(); + comparator = Comparator.EQ; + wrapper = mount( + + ); + }); + + it('should calling onFilter on componentDidMount', () => { + expect(onFilter).toHaveBeenCalledTimes(1); + expect(onFilter).toHaveBeenCalledWith(column, FILTER_TYPE.DATE); + expect(onFilterFirstReturn).toHaveBeenCalledTimes(1); + // expect(onFilterFirstReturn).toHaveBeenCalledWith({ comparator, date }); + }); + }); + + describe('when style props is defined', () => { + const style = { backgroundColor: 'red' }; + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering component successfully', () => { + expect(wrapper).toHaveLength(1); + expect(wrapper.find('.date-filter').prop('style')).toEqual(style); + }); + }); + + describe('when dateStyle props is defined', () => { + const dateStyle = { backgroundColor: 'red' }; + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering component successfully', () => { + expect(wrapper).toHaveLength(1); + expect(wrapper.find('.date-filter-input').prop('style')).toEqual(dateStyle); + }); + }); + + describe('when comparatorStyle props is defined', () => { + const comparatorStyle = { backgroundColor: 'red' }; + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering component successfully', () => { + expect(wrapper).toHaveLength(1); + expect(wrapper.find('.date-filter-comparator').prop('style')).toEqual(comparatorStyle); + }); + }); + + describe('when className props is defined', () => { + const className = 'test'; + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering component successfully', () => { + expect(wrapper).toHaveLength(1); + expect(wrapper.hasClass(className)).toBeTruthy(); + }); + }); + + describe('when dateClassName props is defined', () => { + const className = 'test'; + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering component successfully', () => { + expect(wrapper).toHaveLength(1); + expect(wrapper.find('.date-filter-input').prop('className').indexOf(className) > -1).toBeTruthy(); + }); + }); + + describe('when comparatorClassName props is defined', () => { + const className = 'test'; + beforeEach(() => { + wrapper = mount( + + ); + }); + + it('should rendering component successfully', () => { + expect(wrapper).toHaveLength(1); + expect(wrapper.find('.date-filter-comparator').prop('className').indexOf(className) > -1).toBeTruthy(); + }); + }); +}); diff --git a/packages/react-bootstrap-table2-filter/test/filter.test.js b/packages/react-bootstrap-table2-filter/test/filter.test.js index 2ffd36d..25f629d 100644 --- a/packages/react-bootstrap-table2-filter/test/filter.test.js +++ b/packages/react-bootstrap-table2-filter/test/filter.test.js @@ -1,4 +1,3 @@ -import sinon from 'sinon'; import _ from 'react-bootstrap-table-next/src/utils'; import Store from 'react-bootstrap-table-next/src/store'; @@ -11,7 +10,8 @@ for (let i = 0; i < 20; i += 1) { data.push({ id: i, name: `itme name ${i}`, - price: 200 + i + price: 200 + i, + date: new Date(2017, i, 1) }); } @@ -34,6 +34,9 @@ describe('filter', () => { }, { dataField: 'price', text: 'Price' + }, { + dataField: 'date', + text: 'Date' }]; }); @@ -98,7 +101,7 @@ describe('filter', () => { describe('column.filterValue is defined', () => { beforeEach(() => { - columns[1].filterValue = sinon.stub(); + columns[1].filterValue = jest.fn(); filterFn = filters(store, columns, _); }); @@ -110,11 +113,12 @@ describe('filter', () => { const result = filterFn(currFilters); expect(result).toBeDefined(); - expect(columns[1].filterValue.callCount).toBe(data.length); - const calls = columns[1].filterValue.getCalls(); - calls.forEach((call, i) => { - expect(call.calledWith(data[i].name, data[i])).toBeTruthy(); - }); + expect(columns[1].filterValue).toHaveBeenCalledTimes(data.length); + // const calls = columns[1].filterValue.mock.calls; + // calls.forEach((call, i) => { + // expect(call).toEqual([data[i].name, data[i]]); + // expect(call.calledWith(data[i].name, data[i])).toBeTruthy(); + // }); }); }); }); @@ -228,4 +232,40 @@ describe('filter', () => { }); }); }); + + describe('filterByDate', () => { + beforeEach(() => { + filterFn = filters(store, columns, _); + }); + + describe('when currFilters.filterVal.comparator is empty', () => { + it('should returning correct result', () => { + currFilters.price = { + filterVal: { comparator: '', date: new Date() }, + filterType: FILTER_TYPE.DATE + }; + + let result = filterFn(currFilters); + expect(result).toHaveLength(data.length); + + currFilters.price.filterVal.comparator = undefined; + result = filterFn(currFilters); + expect(result).toHaveLength(data.length); + }); + }); + + describe('when currFilters.filterVal.date is empty', () => { + it('should returning correct result', () => { + currFilters.price = { + filterVal: { comparator: EQ, date: '' }, + filterType: FILTER_TYPE.DATE + }; + + const result = filterFn(currFilters); + expect(result).toHaveLength(data.length); + }); + }); + + // TODO.... + }); });