mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2025-10-16 11:55:39 +00:00
commit
dda47f7b7d
@ -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`:
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
77
packages/react-bootstrap-table2-example/examples/column-filter/custom-date-filter.js
vendored
Normal file
77
packages/react-bootstrap-table2-example/examples/column-filter/custom-date-filter.js
vendored
Normal file
@ -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'
|
||||
})
|
||||
}];
|
||||
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ stocks }
|
||||
columns={ columns }
|
||||
filter={ filterFactory() }
|
||||
/>
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ stocks }
|
||||
columns={ columns }
|
||||
filter={ filterFactory() }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
59
packages/react-bootstrap-table2-example/examples/column-filter/date-filter-default-value.js
vendored
Normal file
59
packages/react-bootstrap-table2-example/examples/column-filter/date-filter-default-value.js
vendored
Normal file
@ -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 }
|
||||
})
|
||||
}];
|
||||
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ stocks }
|
||||
columns={ columns }
|
||||
filter={ filterFactory() }
|
||||
/>
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ stocks }
|
||||
columns={ columns }
|
||||
filter={ filterFactory() }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
55
packages/react-bootstrap-table2-example/examples/column-filter/date-filter.js
vendored
Normal file
55
packages/react-bootstrap-table2-example/examples/column-filter/date-filter.js
vendored
Normal file
@ -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()
|
||||
}];
|
||||
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ stocks }
|
||||
columns={ columns }
|
||||
filter={ filterFactory() }
|
||||
/>
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ stocks }
|
||||
columns={ columns }
|
||||
filter={ filterFactory() }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
85
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-date-filter.js
vendored
Normal file
85
packages/react-bootstrap-table2-example/examples/column-filter/programmatically-date-filter.js
vendored
Normal file
@ -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 () => (
|
||||
<div>
|
||||
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter InStock Date columns which is greater than 2018.01.01 </button>
|
||||
|
||||
<BootstrapTable keyField='id' data={ stocks } columns={ columns } filter={ filterFactory() } />
|
||||
</div>
|
||||
);
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<button className="btn btn-lg btn-primary" onClick={ handleClick }> filter InStock Date columns which is greater than 2018.01.01 </button>
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ stocks }
|
||||
columns={ columns }
|
||||
filter={ filterFactory() }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
@ -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', () => <SelectFilterComparator />)
|
||||
.add('Number Filter', () => <NumberFilter />)
|
||||
.add('Number Filter with Default Value', () => <NumberFilterWithDefaultValue />)
|
||||
.add('Date Filter', () => <DateFilter />)
|
||||
.add('Date Filter with Default Value', () => <DateFilterWithDefaultValue />)
|
||||
.add('Custom Text Filter', () => <CustomTextFilter />)
|
||||
.add('Custom Select Filter', () => <CustomSelectFilter />)
|
||||
.add('Custom Number Filter', () => <CustomNumberFilter />)
|
||||
.add('Custom Date Filter', () => <CustomDateFilter />)
|
||||
.add('Custom Filter Value', () => <CustomFilterValue />)
|
||||
.add('Programmatically Text Filter ', () => <ProgrammaticallyTextFilter />)
|
||||
.add('Programmatically Select Filter ', () => <ProgrammaticallySelectFilter />)
|
||||
.add('Programmatically Number Filter ', () => <ProgrammaticallyNumberFilter />);
|
||||
.add('Programmatically Text Filter', () => <ProgrammaticallyTextFilter />)
|
||||
.add('Programmatically Select Filter', () => <ProgrammaticallySelectFilter />)
|
||||
.add('Programmatically Number Filter', () => <ProgrammaticallyNumberFilter />)
|
||||
.add('Programmatically Date Filter', () => <ProgrammaticallyDateFilter />);
|
||||
|
||||
storiesOf('Work on Rows', module)
|
||||
.add('Customize Row Style', () => <RowStyleTable />)
|
||||
|
||||
@ -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()
|
||||
}];
|
||||
|
||||
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
|
||||
```
|
||||
|
||||
> **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...
|
||||
```
|
||||
@ -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
|
||||
});
|
||||
|
||||
204
packages/react-bootstrap-table2-filter/src/components/date.js
vendored
Normal file
204
packages/react-bootstrap-table2-filter/src/components/date.js
vendored
Normal file
@ -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(<option key="-1" />);
|
||||
}
|
||||
for (let i = 0; i < this.comparators.length; i += 1) {
|
||||
optionTags.push(
|
||||
<option key={ i } value={ this.comparators[i] }>
|
||||
{ this.comparators[i] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
return optionTags;
|
||||
}
|
||||
|
||||
getDefaultDate() {
|
||||
let defaultDate = '';
|
||||
const { defaultValue } = this.props;
|
||||
if (defaultValue && defaultValue.date) {
|
||||
// Set the appropriate format for the input type=date, i.e. "YYYY-MM-DD"
|
||||
defaultDate = dateParser(new Date(defaultValue.date));
|
||||
}
|
||||
return defaultDate;
|
||||
}
|
||||
|
||||
applyFilter(value, comparator) {
|
||||
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 (
|
||||
<div className={ `filter date-filter ${className}` } style={ style }>
|
||||
<select
|
||||
ref={ n => this.dateFilterComparator = n }
|
||||
style={ comparatorStyle }
|
||||
className={ `date-filter-comparator form-control ${comparatorClassName}` }
|
||||
onChange={ this.onChangeComparator }
|
||||
defaultValue={ defaultValue ? defaultValue.comparator : '' }
|
||||
>
|
||||
{ this.getComparatorOptions() }
|
||||
</select>
|
||||
<input
|
||||
ref={ n => this.inputDate = n }
|
||||
className={ `filter date-filter-input form-control ${dateClassName}` }
|
||||
style={ dateStyle }
|
||||
type="date"
|
||||
onChange={ this.onChangeDate }
|
||||
placeholder={ placeholder || `Enter ${text}...` }
|
||||
defaultValue={ this.getDefaultDate() }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DateFilter.propTypes = {
|
||||
onFilter: PropTypes.func.isRequired,
|
||||
column: PropTypes.object.isRequired,
|
||||
delay: PropTypes.number,
|
||||
defaultValue: PropTypes.shape({
|
||||
date: PropTypes.oneOfType([PropTypes.object]),
|
||||
comparator: PropTypes.oneOf([...legalComparators, ''])
|
||||
}),
|
||||
/* eslint consistent-return: 0 */
|
||||
comparators: (props, propName) => {
|
||||
if (!props[propName]) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < props[propName].length; i += 1) {
|
||||
let comparatorIsValid = false;
|
||||
for (let j = 0; j < legalComparators.length; j += 1) {
|
||||
if (legalComparators[j] === props[propName][i] || props[propName][i] === '') {
|
||||
comparatorIsValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!comparatorIsValid) {
|
||||
return new Error(`Date comparator provided is not supported.
|
||||
Use only ${legalComparators}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
placeholder: PropTypes.string,
|
||||
withoutEmptyComparatorOption: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
comparatorStyle: PropTypes.object,
|
||||
dateStyle: PropTypes.object,
|
||||
className: PropTypes.string,
|
||||
comparatorClassName: PropTypes.string,
|
||||
dateClassName: PropTypes.string,
|
||||
getFilter: PropTypes.func
|
||||
};
|
||||
|
||||
DateFilter.defaultProps = {
|
||||
delay: 0,
|
||||
defaultValue: {
|
||||
date: undefined,
|
||||
comparator: ''
|
||||
},
|
||||
withoutEmptyComparatorOption: false,
|
||||
comparators: legalComparators,
|
||||
placeholder: undefined,
|
||||
style: undefined,
|
||||
className: '',
|
||||
comparatorStyle: undefined,
|
||||
comparatorClassName: '',
|
||||
dateStyle: undefined,
|
||||
dateClassName: ''
|
||||
};
|
||||
|
||||
|
||||
export default DateFilter;
|
||||
@ -1,7 +1,8 @@
|
||||
export const FILTER_TYPE = {
|
||||
TEXT: 'TEXT',
|
||||
SELECT: 'SELECT',
|
||||
NUMBER: 'NUMBER'
|
||||
NUMBER: 'NUMBER',
|
||||
DATE: 'DATE'
|
||||
};
|
||||
|
||||
export const FILTER_DELAY = 500;
|
||||
|
||||
@ -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(_);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -0,0 +1,264 @@
|
||||
import 'jsdom-global/register';
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import DateFilter from '../../src/components/date';
|
||||
import { FILTER_TYPE } from '../../src/const';
|
||||
import * as Comparator from '../../src/comparison';
|
||||
|
||||
|
||||
describe('Date Filter', () => {
|
||||
let wrapper;
|
||||
|
||||
|
||||
const onFilterFirstReturn = jest.fn();
|
||||
const onFilter = jest.fn().mockReturnValue(onFilterFirstReturn);
|
||||
|
||||
const column = {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
onFilter.mockClear();
|
||||
onFilterFirstReturn.mockClear();
|
||||
|
||||
// onFilter.returns(onFilterFirstReturn);
|
||||
});
|
||||
|
||||
describe('initialization', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter onFilter={ onFilter } column={ column } />
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-input')).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-comparator')).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should rendering comparator options correctly', () => {
|
||||
const select = wrapper.find('select');
|
||||
expect(select.find('option')).toHaveLength(wrapper.prop('comparators').length + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when withoutEmptyComparatorOption prop is true', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
withoutEmptyComparatorOption
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering comparator options correctly', () => {
|
||||
const select = wrapper.find('.date-filter-comparator');
|
||||
expect(select.find('option')).toHaveLength(wrapper.prop('comparators').length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue.date props is defined', () => {
|
||||
const date = new Date(2018, 0, 1);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
defaultValue={ { date } }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering input successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
const input = wrapper.find('.date-filter-input');
|
||||
expect(input).toHaveLength(1);
|
||||
expect(input.props().defaultValue).toEqual(wrapper.instance().getDefaultDate());
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue.comparator props is defined', () => {
|
||||
const comparator = Comparator.EQ;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
defaultValue={ { comparator } }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering comparator select successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
const select = wrapper.find('.date-filter-comparator');
|
||||
expect(select).toHaveLength(1);
|
||||
expect(select.props().defaultValue).toEqual(comparator);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.getFilter is defined', () => {
|
||||
let programmaticallyFilter;
|
||||
|
||||
const comparator = Comparator.EQ;
|
||||
const date = new Date(2018, 0, 1);
|
||||
|
||||
const getFilter = (filter) => {
|
||||
programmaticallyFilter = filter;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter onFilter={ onFilter } column={ column } getFilter={ getFilter } />
|
||||
);
|
||||
|
||||
programmaticallyFilter({ comparator, date });
|
||||
});
|
||||
|
||||
it('should do onFilter correctly when exported function was executed', () => {
|
||||
expect(onFilter).toHaveBeenCalledTimes(1);
|
||||
expect(onFilter).toHaveBeenCalledWith(column, FILTER_TYPE.DATE);
|
||||
expect(onFilterFirstReturn).toHaveBeenCalledTimes(1);
|
||||
expect(onFilterFirstReturn).toHaveBeenCalledWith({ comparator, date });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when defaultValue.number and defaultValue.comparator props are defined', () => {
|
||||
let date;
|
||||
let comparator;
|
||||
|
||||
beforeEach(() => {
|
||||
date = new Date();
|
||||
comparator = Comparator.EQ;
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
defaultValue={ { date, comparator } }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should calling onFilter on componentDidMount', () => {
|
||||
expect(onFilter).toHaveBeenCalledTimes(1);
|
||||
expect(onFilter).toHaveBeenCalledWith(column, FILTER_TYPE.DATE);
|
||||
expect(onFilterFirstReturn).toHaveBeenCalledTimes(1);
|
||||
// expect(onFilterFirstReturn).toHaveBeenCalledWith({ comparator, date });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when style props is defined', () => {
|
||||
const style = { backgroundColor: 'red' };
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
style={ style }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter').prop('style')).toEqual(style);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when dateStyle props is defined', () => {
|
||||
const dateStyle = { backgroundColor: 'red' };
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
dateStyle={ dateStyle }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-input').prop('style')).toEqual(dateStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when comparatorStyle props is defined', () => {
|
||||
const comparatorStyle = { backgroundColor: 'red' };
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
comparatorStyle={ comparatorStyle }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-comparator').prop('style')).toEqual(comparatorStyle);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when className props is defined', () => {
|
||||
const className = 'test';
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
className={ className }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.hasClass(className)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when dateClassName props is defined', () => {
|
||||
const className = 'test';
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
dateClassName={ className }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-input').prop('className').indexOf(className) > -1).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when comparatorClassName props is defined', () => {
|
||||
const className = 'test';
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<DateFilter
|
||||
onFilter={ onFilter }
|
||||
column={ column }
|
||||
comparatorClassName={ className }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should rendering component successfully', () => {
|
||||
expect(wrapper).toHaveLength(1);
|
||||
expect(wrapper.find('.date-filter-comparator').prop('className').indexOf(className) > -1).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -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....
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user