Compare commits

...

21 Commits

Author SHA1 Message Date
AllenFang
37e79a654b Publish
- react-bootstrap-table2-example@0.1.12
 - react-bootstrap-table2-filter@0.3.2
2018-08-01 19:54:44 +08:00
Allen
4e7cfdf5ea Merge pull request #443 from react-bootstrap-table/develop
20180801 release
2018-08-01 19:53:18 +08:00
Allen
6f4e779a3e Merge pull request #442 from react-bootstrap-table/enhance/multi-select
Enhance/multi select
2018-07-31 17:03:26 +08:00
AllenFang
38d3e2df05 patch docs for multiselect filter 2018-07-31 16:57:34 +08:00
AllenFang
4e204f1ccd refine multiselect 2018-07-31 16:55:17 +08:00
AllenFang
7e29999b40 patch multiselect filter's stories 2018-07-31 16:54:48 +08:00
Allen
01cf69392f Merge pull request #438 from Ignalion/multiselectv2
Multiselectv2
2018-07-31 16:29:19 +08:00
AllenFang
7d7688582b Publish
- react-bootstrap-table2-filter@0.3.1
2018-07-30 23:27:23 +08:00
Allen
e26065b116 Merge pull request #440 from react-bootstrap-table/develop
20180730 release
2018-07-30 23:16:26 +08:00
ignalion
485503c54d Added correct handling of empty selection (first line) 2018-07-29 15:45:58 +03:00
ignalion
3c37716dd2 little fix for curly brace 2018-07-29 15:45:58 +03:00
ignalion
1a7f86a321 Added tests for multiselect filter 2018-07-29 15:45:58 +03:00
ignalion
475f8c67b0 Added some examples for multi-select filter 2018-07-29 15:45:58 +03:00
ignalion
26314254be Added multiselect filter (mostly copied from default select one) 2018-07-29 15:45:58 +03:00
Benny Johnson
6522f6d964 Commented out a check which caused the date filter to not update the (#425)
table when the date or date comparator were cleared

Modified applyFilter in date.js so that it doesn't try to parse an
Invalid Date. It was parsing an empty string, which caused it to pass
through an invalid date to onFilter, and this wasn't being checked for
properly by the onFilter function. It now checks for the empty string
and passes in null, which is what the onFilter function was actually
checking for
2018-07-25 11:20:35 +08:00
Allen
ecaf439e66 Merge pull request #420 from nthgol/patch-1
import numberFilter in numberFilter example
2018-07-19 10:15:57 +08:00
Nathan
90d03676ad import numberFilter in numberFilter example 2018-07-16 18:06:51 -04:00
AllenFang
2585a62697 Publish
- react-bootstrap-table2-example@0.1.11
 - react-bootstrap-table2-paginator@0.1.6
 - react-bootstrap-table-next@0.1.15
2018-07-15 16:08:59 +08:00
Allen
6afe58a081 Merge pull request #415 from react-bootstrap-table/develop
20180715 release
2018-07-15 16:02:47 +08:00
Allen
6f5bd1a13d fix #394 (#414) 2018-07-15 14:43:29 +08:00
Allen
85a9ab72af fix #402 (#412) 2018-07-15 14:18:21 +08:00
30 changed files with 1143 additions and 44 deletions

View File

@@ -18,6 +18,7 @@
* [id](#id) * [id](#id)
* [classes](#classes) * [classes](#classes)
* [wrapperClasses](#wrapperClasses) * [wrapperClasses](#wrapperClasses)
* [headerClasses](#headerClasses)
* [cellEdit](#cellEdit) * [cellEdit](#cellEdit)
* [selectRow](#selectRow) * [selectRow](#selectRow)
* [rowStyle](#rowStyle) * [rowStyle](#rowStyle)
@@ -111,6 +112,10 @@ Customize class on `table` element.
### <a name='wrapperClasses'>wrapperClasses - [String]</a> ### <a name='wrapperClasses'>wrapperClasses - [String]</a>
Customize class on the outer element which wrap up the `table` element. Customize class on the outer element which wrap up the `table` element.
### <a name='headerClasses'>headerClasses - [String]</a>
Customize class on the header row(`tr`).
### <a name='cellEdit'>cellEdit - [Object]</a> ### <a name='cellEdit'>cellEdit - [Object]</a>
Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail. Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail.

View File

@@ -88,7 +88,7 @@ Please see [available filter configuration](https://react-bootstrap-table.github
- [x] Custom Select Filter - [x] Custom Select Filter
- [X] Number Filter - [X] Number Filter
- [X] Date Filter - [X] Date Filter
- [ ] Array Filter - [X] Array Filter
- [X] Programmatically Filter - [X] Programmatically Filter
Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly. Remember to install [`react-bootstrap-table2-filter`](https://www.npmjs.com/package/react-bootstrap-table2-filter) firstly.

View File

@@ -0,0 +1,80 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
withoutEmptyOption: true,
style: {
backgroundColor: 'pink'
},
className: 'test-classname',
datamycustomattr: 'datamycustomattr'
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
withoutEmptyOption: true,
style: {
backgroundColor: 'pink'
},
className: 'test-classname',
datamycustomattr: 'datamycustomattr'
})
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,69 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
defaultValue: [0, 2]
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
defaultValue: [0, 2]
})
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,67 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions
})
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,95 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator(6);
let qualityFilter;
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
getFilter: (filter) => {
// qualityFilter was assigned once the component has been mounted.
qualityFilter = filter;
}
})
}];
const handleClick = () => {
qualityFilter([0, 2]);
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
let qualityFilter;
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions,
getFilter: (filter) => {
// qualityFilter was assigned once the component has been mounted.
qualityFilter = filter;
}
})
}];
const handleClick = () => {
qualityFilter([0, 2]);
};
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }>{' filter columns by option "good" and "unknow" '}</button>
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
</div>
);
`;
export default () => (
<div>
<button className="btn btn-lg btn-primary" onClick={ handleClick }>{' filter columns by option "good" and "unknow" '}</button>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,52 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
headerClasses="header-class"
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
headerClasses="header-class"
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-example", "name": "react-bootstrap-table2-example",
"version": "0.1.10", "version": "0.1.12",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"private": true, "private": true,

View File

@@ -33,6 +33,7 @@ import HeaderColumnEventTable from 'examples/header-columns/column-event-table';
import HeaderColumnClassTable from 'examples/header-columns/column-class-table'; import HeaderColumnClassTable from 'examples/header-columns/column-class-table';
import HeaderColumnStyleTable from 'examples/header-columns/column-style-table'; import HeaderColumnStyleTable from 'examples/header-columns/column-style-table';
import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table'; import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
import HeaderClassTable from 'examples/header-columns/header-class-table';
// column filter // column filter
import TextFilter from 'examples/column-filter/text-filter'; import TextFilter from 'examples/column-filter/text-filter';
@@ -45,6 +46,9 @@ import SelectFilter from 'examples/column-filter/select-filter';
import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value'; import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value';
import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator'; import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator';
import CustomSelectFilter from 'examples/column-filter/custom-select-filter'; import CustomSelectFilter from 'examples/column-filter/custom-select-filter';
import MultiSelectFilter from 'examples/column-filter/multi-select-filter';
import MultiSelectFilterDefaultValue from 'examples/column-filter/multi-select-filter-default-value';
import CustomMultiSelectFilter from 'examples/column-filter/custom-multi-select-filter';
import NumberFilter from 'examples/column-filter/number-filter'; import NumberFilter from 'examples/column-filter/number-filter';
import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value'; import NumberFilterWithDefaultValue from 'examples/column-filter/number-filter-default-value';
import CustomNumberFilter from 'examples/column-filter/custom-number-filter'; import CustomNumberFilter from 'examples/column-filter/custom-number-filter';
@@ -55,6 +59,7 @@ import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-
import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter'; import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter';
import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter'; import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter';
import ProgrammaticallyDateFilter from 'examples/column-filter/programmatically-date-filter'; import ProgrammaticallyDateFilter from 'examples/column-filter/programmatically-date-filter';
import ProgrammaticallyMultiSelectFilter from 'examples/column-filter/programmatically-multi-select-filter';
import CustomFilter from 'examples/column-filter/custom-filter'; import CustomFilter from 'examples/column-filter/custom-filter';
import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter'; import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter';
@@ -165,7 +170,8 @@ storiesOf('Work on Header Columns', module)
.add('Column Event', () => <HeaderColumnEventTable />) .add('Column Event', () => <HeaderColumnEventTable />)
.add('Customize Column Class', () => <HeaderColumnClassTable />) .add('Customize Column Class', () => <HeaderColumnClassTable />)
.add('Customize Column Style', () => <HeaderColumnStyleTable />) .add('Customize Column Style', () => <HeaderColumnStyleTable />)
.add('Customize Column HTML attribute', () => <HeaderColumnAttrsTable />); .add('Customize Column HTML attribute', () => <HeaderColumnAttrsTable />)
.add('Header Class', () => <HeaderClassTable />);
storiesOf('Column Filter', module) storiesOf('Column Filter', module)
.add('Text Filter', () => <TextFilter />) .add('Text Filter', () => <TextFilter />)
@@ -176,6 +182,8 @@ storiesOf('Column Filter', module)
.add('Select Filter', () => <SelectFilter />) .add('Select Filter', () => <SelectFilter />)
.add('Select Filter with Default Value', () => <SelectFilterWithDefaultValue />) .add('Select Filter with Default Value', () => <SelectFilterWithDefaultValue />)
.add('Select Filter with Comparator', () => <SelectFilterComparator />) .add('Select Filter with Comparator', () => <SelectFilterComparator />)
.add('MultiSelect Filter', () => <MultiSelectFilter />)
.add('MultiSelect Filter with Default Value', () => <MultiSelectFilterDefaultValue />)
.add('Number Filter', () => <NumberFilter />) .add('Number Filter', () => <NumberFilter />)
.add('Number Filter with Default Value', () => <NumberFilterWithDefaultValue />) .add('Number Filter with Default Value', () => <NumberFilterWithDefaultValue />)
.add('Date Filter', () => <DateFilter />) .add('Date Filter', () => <DateFilter />)
@@ -184,11 +192,13 @@ storiesOf('Column Filter', module)
.add('Custom Select Filter', () => <CustomSelectFilter />) .add('Custom Select Filter', () => <CustomSelectFilter />)
.add('Custom Number Filter', () => <CustomNumberFilter />) .add('Custom Number Filter', () => <CustomNumberFilter />)
.add('Custom Date Filter', () => <CustomDateFilter />) .add('Custom Date Filter', () => <CustomDateFilter />)
.add('Custom MultiSelect Filter', () => <CustomMultiSelectFilter />)
.add('Custom Filter Value', () => <CustomFilterValue />) .add('Custom Filter Value', () => <CustomFilterValue />)
.add('Programmatically Text Filter', () => <ProgrammaticallyTextFilter />) .add('Programmatically Text Filter', () => <ProgrammaticallyTextFilter />)
.add('Programmatically Select Filter', () => <ProgrammaticallySelectFilter />) .add('Programmatically Select Filter', () => <ProgrammaticallySelectFilter />)
.add('Programmatically Number Filter', () => <ProgrammaticallyNumberFilter />) .add('Programmatically Number Filter', () => <ProgrammaticallyNumberFilter />)
.add('Programmatically Date Filter', () => <ProgrammaticallyDateFilter />) .add('Programmatically Date Filter', () => <ProgrammaticallyDateFilter />)
.add('Programmatically Multi Select Filter', () => <ProgrammaticallyMultiSelectFilter />)
.add('Custom Filter', () => <CustomFilter />) .add('Custom Filter', () => <CustomFilter />)
.add('Advance Custom Filter', () => <AdvanceCustomFilter />); .add('Advance Custom Filter', () => <AdvanceCustomFilter />);

View File

@@ -10,3 +10,7 @@
.demo-row-odd { .demo-row-odd {
background-color: $green-lighten-4; background-color: $green-lighten-4;
} }
.header-class {
background-color: $green-lighten-4;
}

View File

@@ -18,6 +18,7 @@ You can get all types of filters via import and these filters are a factory func
* TextFilter * TextFilter
* SelectFilter * SelectFilter
* MultiSelectFilter
* NumberFilter * NumberFilter
* DateFilter * DateFilter
* CustomFilter * CustomFilter
@@ -114,6 +115,52 @@ const qualityFilter = selectFilter({
// omit... // omit...
``` ```
## MultiSelect Filter
A quick example:
```js
import filterFactory, { multiSelectFilter } from 'react-bootstrap-table2-filter';
// omit...
const selectOptions = {
0: 'good',
1: 'Bad',
2: 'unknown'
};
const columns = [
..., {
dataField: 'quality',
text: 'Product Quailty',
formatter: cell => selectOptions[cell],
filter: multiSelectFilter({
options: selectOptions
})
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
```
Following is an example for custom select filter:
```js
import filterFactory, { multiSelectFilter, Comparator } from 'react-bootstrap-table2-filter';
// omit...
const qualityFilter = multiSelectFilter({
options: selectOptions,
placeholder: 'My Custom PlaceHolder', // custom the input placeholder
className: 'my-custom-text-filter', // custom classname on input
defaultValue: '2', // default filtering value
comparator: Comparator.LIKE, // default is Comparator.EQ
style: { ... }, // your custom styles on input
withoutEmptyOption: true // hide the default select option
});
// omit...
```
## Number Filter ## Number Filter
```js ```js
@@ -131,7 +178,7 @@ const columns = [..., {
Numner filter is same as other filter, you can custom the number filter via `numberFilter` factory function: Numner filter is same as other filter, you can custom the number filter via `numberFilter` factory function:
```js ```js
import filterFactory, { selectFilter, Comparator } from 'react-bootstrap-table2-filter'; import filterFactory, { selectFilter, Comparator, numberFilter } from 'react-bootstrap-table2-filter';
// omit... // omit...
const numberFilter = numberFilter({ const numberFilter = numberFilter({

View File

@@ -1,5 +1,6 @@
import TextFilter from './src/components/text'; import TextFilter from './src/components/text';
import SelectFilter from './src/components/select'; import SelectFilter from './src/components/select';
import MultiSelectFilter from './src/components/multiselect';
import NumberFilter from './src/components/number'; import NumberFilter from './src/components/number';
import DateFilter from './src/components/date'; import DateFilter from './src/components/date';
import wrapperFactory from './src/wrapper'; import wrapperFactory from './src/wrapper';
@@ -25,6 +26,11 @@ export const selectFilter = (props = {}) => ({
props props
}); });
export const multiSelectFilter = (props = {}) => ({
Filter: MultiSelectFilter,
props
});
export const numberFilter = (props = {}) => ({ export const numberFilter = (props = {}) => ({
Filter: NumberFilter, Filter: NumberFilter,
props props

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-filter", "name": "react-bootstrap-table2-filter",
"version": "0.3.0", "version": "0.3.2",
"description": "it's a column filter addon for react-bootstrap-table2", "description": "it's a column filter addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -93,12 +93,16 @@ class DateFilter extends Component {
} }
applyFilter(value, comparator) { applyFilter(value, comparator) {
if (!comparator || !value) { // if (!comparator || !value) {
return; // return;
} // }
const { column, onFilter, delay } = this.props; const { column, onFilter, delay } = this.props;
const execute = () => { const execute = () => {
const date = typeof value !== 'object' ? new Date(value) : value; // Incoming value should always be a string, and the defaultDate
// above is implemented as an empty string, so we can just check for that.
// instead of parsing an invalid Date. The filter function will interpret
// null as an empty date field
const date = value === '' ? null : new Date(value);
onFilter(column, FILTER_TYPE.DATE)({ date, comparator }); onFilter(column, FILTER_TYPE.DATE)({ date, comparator });
}; };
if (delay) { if (delay) {

View File

@@ -0,0 +1,152 @@
/* eslint react/require-default-props: 0 */
/* eslint no-return-assign: 0 */
/* eslint no-param-reassign: 0 */
/* eslint react/no-unused-prop-types: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { LIKE, EQ } from '../comparison';
import { FILTER_TYPE } from '../const';
function optionsEquals(currOpts, prevOpts) {
const keys = Object.keys(currOpts);
for (let i = 0; i < keys.length; i += 1) {
if (currOpts[keys[i]] !== prevOpts[keys[i]]) {
return false;
}
}
return Object.keys(currOpts).length === Object.keys(prevOpts).length;
}
const getSelections = container =>
Array.from(container.selectedOptions).map(item => item.value);
class MultiSelectFilter extends Component {
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.applyFilter = this.applyFilter.bind(this);
const isSelected = props.defaultValue.map(item => props.options[item]).length > 0;
this.state = { isSelected };
}
componentDidMount() {
const { getFilter } = this.props;
const value = getSelections(this.selectInput);
if (value && value.length > 0) {
this.applyFilter(value);
}
// export onFilter function to allow users to access
if (getFilter) {
getFilter((filterVal) => {
this.selectInput.value = filterVal;
this.applyFilter(filterVal);
});
}
}
componentDidUpdate(prevProps) {
let needFilter = false;
if (this.props.defaultValue !== prevProps.defaultValue) {
needFilter = true;
} else if (!optionsEquals(this.props.options, prevProps.options)) {
needFilter = true;
}
if (needFilter) {
this.applyFilter(this.selectInput.value);
}
}
getOptions() {
const optionTags = [];
const { options, placeholder, column, withoutEmptyOption } = this.props;
if (!withoutEmptyOption) {
optionTags.push((
<option key="-1" value="">{ placeholder || `Select ${column.text}...` }</option>
));
}
Object.keys(options).forEach(key =>
optionTags.push(<option key={ key } value={ key }>{ options[key] }</option>)
);
return optionTags;
}
cleanFiltered() {
const value = (this.props.defaultValue !== undefined) ? this.props.defaultValue : [];
this.selectInput.value = value;
this.applyFilter(value);
}
applyFilter(value) {
if (value.length === 1 && value[0] === '') {
value = [];
}
this.setState(() => ({ isSelected: value.length > 0 }));
this.props.onFilter(this.props.column, FILTER_TYPE.MULTISELECT)(value);
}
filter(e) {
const value = getSelections(e.target);
this.applyFilter(value);
}
render() {
const {
style,
className,
defaultValue,
onFilter,
column,
options,
comparator,
withoutEmptyOption,
caseSensitive,
getFilter,
...rest
} = this.props;
const selectClass =
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return (
<select
{ ...rest }
ref={ n => this.selectInput = n }
style={ style }
multiple
className={ selectClass }
onChange={ this.filter }
onClick={ e => e.stopPropagation() }
defaultValue={ defaultValue !== undefined ? defaultValue : '' }
>
{ this.getOptions() }
</select>
);
}
}
MultiSelectFilter.propTypes = {
onFilter: PropTypes.func.isRequired,
column: PropTypes.object.isRequired,
options: PropTypes.object.isRequired,
comparator: PropTypes.oneOf([LIKE, EQ]),
placeholder: PropTypes.string,
style: PropTypes.object,
className: PropTypes.string,
withoutEmptyOption: PropTypes.bool,
defaultValue: PropTypes.array,
caseSensitive: PropTypes.bool,
getFilter: PropTypes.func
};
MultiSelectFilter.defaultProps = {
defaultValue: [],
className: '',
withoutEmptyOption: false,
comparator: EQ,
caseSensitive: true
};
export default MultiSelectFilter;

View File

@@ -1,6 +1,7 @@
export const FILTER_TYPE = { export const FILTER_TYPE = {
TEXT: 'TEXT', TEXT: 'TEXT',
SELECT: 'SELECT', SELECT: 'SELECT',
MULTISELECT: 'MULTISELECT',
NUMBER: 'NUMBER', NUMBER: 'NUMBER',
DATE: 'DATE' DATE: 'DATE'
}; };

View File

@@ -187,6 +187,26 @@ export const filterByDate = _ => (
}); });
}; };
export const filterByArray = _ => (
data,
dataField,
{ filterVal, comparator }
) => {
if (filterVal.length === 0) return data;
const refinedFilterVal = filterVal
.filter(x => _.isDefined(x))
.map(x => x.toString());
return data.filter((row) => {
const cell = _.get(row, dataField);
let cellStr = _.isDefined(cell) ? cell.toString() : '';
if (comparator === EQ) {
return refinedFilterVal.indexOf(cellStr) !== -1;
}
cellStr = cellStr.toLocaleUpperCase();
return refinedFilterVal.some(item => cellStr.indexOf(item.toLocaleUpperCase()) !== -1);
});
};
export const filterFactory = _ => (filterType) => { export const filterFactory = _ => (filterType) => {
let filterFn; let filterFn;
switch (filterType) { switch (filterType) {
@@ -194,6 +214,9 @@ export const filterFactory = _ => (filterType) => {
case FILTER_TYPE.SELECT: case FILTER_TYPE.SELECT:
filterFn = filterByText(_); filterFn = filterByText(_);
break; break;
case FILTER_TYPE.MULTISELECT:
filterFn = filterByArray(_);
break;
case FILTER_TYPE.NUMBER: case FILTER_TYPE.NUMBER:
filterFn = filterByNumber(_); filterFn = filterByNumber(_);
break; break;

View File

@@ -53,12 +53,20 @@ export default (Base, {
const currFilters = Object.assign({}, store.filters); const currFilters = Object.assign({}, store.filters);
const { dataField, filter } = column; const { dataField, filter } = column;
if (!_.isDefined(filterVal) || filterVal === '') { const needClearFilters =
!_.isDefined(filterVal) ||
filterVal === '' ||
filterVal.length === 0;
if (needClearFilters) {
delete currFilters[dataField]; delete currFilters[dataField];
} else { } else {
// select default comparator is EQ, others are LIKE // select default comparator is EQ, others are LIKE
const { const {
comparator = (filterType === FILTER_TYPE.SELECT ? EQ : LIKE), comparator = (
(filterType === FILTER_TYPE.SELECT) || (
filterType === FILTER_TYPE.MULTISELECT) ? EQ : LIKE
),
caseSensitive = false caseSensitive = false
} = filter.props; } = filter.props;
currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive }; currFilters[dataField] = { filterVal, filterType, comparator, caseSensitive };

View File

@@ -0,0 +1,354 @@
import 'jsdom-global/register';
import React from 'react';
import sinon from 'sinon';
import { mount } from 'enzyme';
import MultiSelectFilter from '../../src/components/multiselect';
import { FILTER_TYPE } from '../../src/const';
describe('Multi Select Filter', () => {
let wrapper;
let instance;
// onFilter(x)(y) = filter result
const onFilter = sinon.stub();
const onFilterFirstReturn = sinon.stub();
const column = {
dataField: 'quality',
text: 'Product Quality'
};
const options = {
0: 'Bad',
1: 'Good',
2: 'Unknown'
};
afterEach(() => {
onFilter.reset();
onFilterFirstReturn.reset();
onFilter.returns(onFilterFirstReturn);
});
describe('initialization', () => {
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter onFilter={ onFilter } column={ column } options={ options } />
);
instance = wrapper.instance();
});
it('should have correct state', () => {
expect(instance.state.isSelected).toBeFalsy();
});
it('should rendering component successfully', () => {
expect(wrapper).toHaveLength(1);
expect(wrapper.find('select')).toHaveLength(1);
expect(wrapper.find('.select-filter')).toHaveLength(1);
expect(wrapper.find('.placeholder-selected')).toHaveLength(1);
});
it('should rendering select options correctly', () => {
const select = wrapper.find('select');
expect(select.find('option')).toHaveLength(Object.keys(options).length + 1);
expect(select.childAt(0).text()).toEqual(`Select ${column.text}...`);
Object.keys(options).forEach((key, i) => {
expect(select.childAt(i + 1).prop('value')).toEqual(key);
expect(select.childAt(i + 1).text()).toEqual(options[key]);
});
});
});
describe('when defaultValue is defined', () => {
let defaultValue;
describe('and it is valid', () => {
beforeEach(() => {
defaultValue = ['0'];
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
defaultValue={ defaultValue }
/>
);
instance = wrapper.instance();
});
it('should have correct state', () => {
expect(instance.state.isSelected).toBeTruthy();
});
it('should rendering component successfully', () => {
expect(wrapper).toHaveLength(1);
expect(wrapper.find('.placeholder-selected')).toHaveLength(0);
});
it('should calling onFilter on componentDidMount', () => {
expect(onFilter.calledOnce).toBeTruthy();
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
});
});
});
describe('when props.getFilter is defined', () => {
let programmaticallyFilter;
const filterValue = ['foo'];
const getFilter = (filter) => {
programmaticallyFilter = filter;
};
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
getFilter={ getFilter }
/>
);
instance = wrapper.instance();
programmaticallyFilter(filterValue);
});
it('should do onFilter correctly when exported function was executed', () => {
expect(onFilter.calledOnce).toBeTruthy();
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
expect(onFilterFirstReturn.calledWith(filterValue)).toBeTruthy();
});
it('should setState correctly when exported function was executed', () => {
expect(instance.state.isSelected).toBeTruthy();
});
});
describe('when placeholder is defined', () => {
const placeholder = 'test';
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
placeholder={ placeholder }
/>
);
instance = wrapper.instance();
});
it('should rendering component successfully', () => {
expect(wrapper).toHaveLength(1);
const select = wrapper.find('select');
expect(select.childAt(0).text()).toEqual(placeholder);
});
});
describe('when style is defined', () => {
const style = { backgroundColor: 'red' };
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
style={ style }
/>
);
});
it('should rendering component successfully', () => {
expect(wrapper).toHaveLength(1);
expect(wrapper.find('select').prop('style')).toEqual(style);
});
});
describe('when withoutEmptyOption is defined', () => {
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
withoutEmptyOption
/>
);
});
it('should rendering select without default empty option', () => {
const select = wrapper.find('select');
expect(select.find('option')).toHaveLength(Object.keys(options).length);
});
});
describe('componentDidUpdate', () => {
let prevProps;
describe('when props.defaultValue is diff from prevProps.defaultValue', () => {
const defaultValue = ['0'];
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
defaultValue={ defaultValue }
/>
);
prevProps = {
column,
options,
defaultValue: ['1']
};
instance = wrapper.instance();
instance.componentDidUpdate(prevProps);
});
it('should update', () => {
expect(onFilter.callCount).toBe(2);
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
expect(onFilterFirstReturn.callCount).toBe(2);
expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy();
});
});
describe('when props.options is diff from prevProps.options', () => {
const defaultValue = ['0'];
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ {
...options,
3: 'Best'
} }
defaultValue={ defaultValue }
/>
);
prevProps = {
column,
options
};
instance = wrapper.instance();
instance.componentDidUpdate(prevProps);
});
it('should update', () => {
expect(onFilter.callCount).toBe(2);
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
expect(onFilterFirstReturn.callCount).toBe(2);
expect(onFilterFirstReturn.calledWith(instance.props.defaultValue)).toBeTruthy();
});
});
});
describe('cleanFiltered', () => {
describe('when props.defaultValue is defined', () => {
const defaultValue = ['0'];
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
defaultValue={ defaultValue }
/>
);
instance = wrapper.instance();
instance.cleanFiltered();
});
it('should setting state correctly', () => {
expect(instance.state.isSelected).toBeTruthy();
});
it('should calling onFilter correctly', () => {
expect(onFilter.callCount).toBe(2);
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
expect(onFilterFirstReturn.callCount).toBe(2);
expect(onFilterFirstReturn.calledWith(defaultValue)).toBeTruthy();
});
});
describe('when props.defaultValue is not defined', () => {
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter
onFilter={ onFilter }
column={ column }
options={ options }
/>
);
instance = wrapper.instance();
instance.cleanFiltered();
});
it('should setting state correctly', () => {
expect(instance.state.isSelected).toBeFalsy();
});
it('should calling onFilter correctly', () => {
expect(onFilter.callCount).toBe(1);
expect(onFilterFirstReturn.callCount).toBe(1);
});
});
});
describe('applyFilter', () => {
const values = ['2'];
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter onFilter={ onFilter } column={ column } options={ options } />
);
instance = wrapper.instance();
instance.applyFilter(values);
});
it('should setting state correctly', () => {
expect(instance.state.isSelected).toBeTruthy();
});
it('should calling onFilter correctly', () => {
expect(onFilter.calledOnce).toBeTruthy();
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
expect(onFilterFirstReturn.calledWith(values)).toBeTruthy();
});
});
describe('filter', () => {
const event = { target: { selectedOptions: [{ value: 'tester' }] } };
beforeEach(() => {
wrapper = mount(
<MultiSelectFilter onFilter={ onFilter } column={ column } options={ options } />
);
instance = wrapper.instance();
instance.filter(event);
});
it('should setting state correctly', () => {
expect(instance.state.isSelected).toBeTruthy();
});
it('should calling onFilter correctly', () => {
expect(onFilter.calledOnce).toBeTruthy();
expect(onFilter.calledWith(column, FILTER_TYPE.MULTISELECT)).toBeTruthy();
expect(onFilterFirstReturn.calledOnce).toBeTruthy();
expect(onFilterFirstReturn.calledWith(
event.target.selectedOptions.map(item => item.value))).toBeTruthy();
});
});
});

View File

@@ -123,6 +123,55 @@ describe('filter', () => {
}); });
}); });
describe('filterByArray', () => {
beforeEach(() => {
filterFn = filters(store, columns, _);
});
describe('when filter value is empty array', () => {
it('should return original data', () => {
currFilters.name = {
filterVal: [],
filterType: FILTER_TYPE.MULTISELECT
};
const result = filterFn(currFilters);
expect(result).toBeDefined();
expect(result).toHaveLength(store.data.length);
});
});
describe('when filter value is not an empty array', () => {
describe(`and comparator is ${EQ}`, () => {
it('should return data correctly', () => {
currFilters.price = {
filterVal: [201, 203],
filterType: FILTER_TYPE.MULTISELECT,
comparator: EQ
};
const result = filterFn(currFilters);
expect(result).toBeDefined();
expect(result).toHaveLength(2);
});
});
describe(`and comparator is ${LIKE}`, () => {
it('should return data correctly', () => {
currFilters.name = {
filterVal: ['name 3', '5'],
filterType: FILTER_TYPE.MULTISELECT,
comparator: LIKE
};
const result = filterFn(currFilters);
expect(result).toBeDefined();
expect(result).toHaveLength(3);
});
});
});
});
describe('filterByNumber', () => { describe('filterByNumber', () => {
beforeEach(() => { beforeEach(() => {
filterFn = filters(store, columns, _); filterFn = filters(store, columns, _);

View File

@@ -163,7 +163,7 @@ describe('Wrapper', () => {
}); });
describe('when filterVal is empty or undefined', () => { describe('when filterVal is empty or undefined', () => {
const filterVals = ['', undefined]; const filterVals = ['', undefined, []];
it('should setting store object correctly', () => { it('should setting store object correctly', () => {
filterVals.forEach((filterVal) => { filterVals.forEach((filterVal) => {

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-paginator", "name": "react-bootstrap-table2-paginator",
"version": "0.1.5", "version": "0.1.6",
"description": "it's the pagination addon for react-bootstrap-table2", "description": "it's the pagination addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -1,12 +1,39 @@
/* eslint no-param-reassign: 0 */
const getNormalizedPage = (
page,
pageStartIndex
) => {
const offset = Math.abs(1 - pageStartIndex);
return page + offset;
};
const endIndex = (
page,
sizePerPage,
pageStartIndex
) => (getNormalizedPage(page, pageStartIndex) * sizePerPage) - 1;
const startIndex = (
end,
sizePerPage,
) => end - (sizePerPage - 1);
export const alignPage = (store, pageStartIndex, sizePerPage) => {
const end = endIndex(store.page, sizePerPage, pageStartIndex);
const dataSize = store.data.length;
if (end - 1 > dataSize) {
return pageStartIndex;
}
return store.page;
};
export const getByCurrPage = (store, pageStartIndex) => { export const getByCurrPage = (store, pageStartIndex) => {
const dataSize = store.data.length; const dataSize = store.data.length;
if (!dataSize) return []; if (!dataSize) return [];
const getNormalizedPage = () => { const end = endIndex(store.page, store.sizePerPage, pageStartIndex);
const offset = Math.abs(1 - pageStartIndex); const start = startIndex(end, store.sizePerPage);
return store.page + offset;
};
const end = (getNormalizedPage() * store.sizePerPage) - 1;
const start = end - (store.sizePerPage - 1);
const result = []; const result = [];
for (let i = start; i <= end; i += 1) { for (let i = start; i <= end; i += 1) {

View File

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import Const from './const'; import Const from './const';
import Pagination from './pagination'; import Pagination from './pagination';
import { getByCurrPage } from './page'; import { getByCurrPage, alignPage } from './page';
export default (Base, { export default (Base, {
remoteResolver remoteResolver
@@ -49,17 +49,21 @@ export default (Base, {
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
let needNewState = false; let needNewState = false;
let { currPage, currSizePerPage } = this.state; let { currPage, currSizePerPage } = this.state;
const { page, sizePerPage, pageStartIndex, onPageChange } = nextProps.pagination.options; const { page, sizePerPage, onPageChange } = nextProps.pagination.options;
const pageStartIndex = typeof nextProps.pagination.options.pageStartIndex !== 'undefined' ?
nextProps.pagination.options.pageStartIndex : Const.PAGE_START_INDEX;
if (typeof page !== 'undefined' && currPage !== page) { // user defined page if (typeof page !== 'undefined' && currPage !== page) { // user defined page
currPage = page; currPage = page;
needNewState = true; needNewState = true;
} else if (nextProps.isDataChanged) { } else if (nextProps.isDataChanged) {
currPage = alignPage(this.props.store, pageStartIndex, currSizePerPage);
needNewState = true; needNewState = true;
} }
if (typeof currPage === 'undefined') { if (typeof currPage === 'undefined') {
currPage = typeof pageStartIndex !== 'undefined' ? pageStartIndex : Const.PAGE_START_INDEX; currPage = pageStartIndex;
} }
if (typeof sizePerPage !== 'undefined') { if (typeof sizePerPage !== 'undefined') {

View File

@@ -1,5 +1,5 @@
import Store from 'react-bootstrap-table-next/src/store'; import Store from 'react-bootstrap-table-next/src/store';
import { getByCurrPage } from '../src/page'; import { getByCurrPage, alignPage } from '../src/page';
describe('Page Functions', () => { describe('Page Functions', () => {
let data; let data;
@@ -48,4 +48,40 @@ describe('Page Functions', () => {
}); });
}); });
}); });
describe('alignPage', () => {
const pageStartIndex = 1;
const sizePerPage = 10;
describe('if the length of store.data is less than the end page index', () => {
beforeEach(() => {
data = [];
for (let i = 0; i < 15; i += 1) {
data.push({ id: i, name: `test_name${i}` });
}
store = new Store('id');
store.data = data;
store.page = 2;
});
it('should return pageStartIndex argument', () => {
expect(alignPage(store, pageStartIndex, sizePerPage)).toEqual(pageStartIndex);
});
});
describe('if the length of store.data is large than the end page index', () => {
beforeEach(() => {
data = [];
for (let i = 0; i < 30; i += 1) {
data.push({ id: i, name: `test_name${i}` });
}
store = new Store('id');
store.data = data;
store.page = 2;
});
it('should return current page', () => {
expect(alignPage(store, pageStartIndex, sizePerPage)).toEqual(store.page);
});
});
});
}); });

View File

@@ -176,23 +176,6 @@ describe('Wrapper', () => {
expect(props.store.page).toEqual(instance.state.currPage); expect(props.store.page).toEqual(instance.state.currPage);
}); });
}); });
describe('when nextProps.isDataChanged is true, currPage is undefined and options.pageStartIndex exists', () => {
beforeEach(() => {
nextProps.isDataChanged = true;
nextProps.pagination.options.pageStartIndex = 0;
instance.state.currPage = undefined;
instance.componentWillReceiveProps(nextProps);
});
it('should setting currPage state correctly', () => {
expect(instance.state.currPage).toBe(nextProps.pagination.options.pageStartIndex);
});
it('should saving store.page correctly', () => {
expect(props.store.page).toEqual(instance.state.currPage);
});
});
}); });
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table-next", "name": "react-bootstrap-table-next",
"version": "0.1.14", "version": "0.1.15",
"description": "Next generation of react-bootstrap-table", "description": "Next generation of react-bootstrap-table",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -86,6 +86,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
{ tableCaption } { tableCaption }
<Header <Header
columns={ columns } columns={ columns }
className={ this.props.headerClasses }
sortField={ store.sortField } sortField={ store.sortField }
sortOrder={ store.sortOrder } sortOrder={ store.sortOrder }
onSort={ this.props.onSort } onSort={ this.props.onSort }
@@ -155,6 +156,7 @@ BootstrapTable.propTypes = {
rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
rowEvents: PropTypes.object, rowEvents: PropTypes.object,
rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
headerClasses: PropTypes.string,
defaultSorted: PropTypes.arrayOf(PropTypes.shape({ defaultSorted: PropTypes.arrayOf(PropTypes.shape({
dataField: PropTypes.string.isRequired, dataField: PropTypes.string.isRequired,
order: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]).isRequired order: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]).isRequired

View File

@@ -10,6 +10,7 @@ const Header = (props) => {
const { ROW_SELECT_DISABLED } = Const; const { ROW_SELECT_DISABLED } = Const;
const { const {
className,
columns, columns,
onSort, onSort,
onFilter, onFilter,
@@ -21,7 +22,7 @@ const Header = (props) => {
return ( return (
<thead> <thead>
<tr> <tr className={ className }>
{ {
(selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn) (selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn)
? <SelectionHeaderCell { ...selectRow } /> : null ? <SelectionHeaderCell { ...selectRow } /> : null
@@ -60,7 +61,8 @@ Header.propTypes = {
sortField: PropTypes.string, sortField: PropTypes.string,
sortOrder: PropTypes.string, sortOrder: PropTypes.string,
selectRow: PropTypes.object, selectRow: PropTypes.object,
onExternalFilter: PropTypes.func onExternalFilter: PropTypes.func,
className: PropTypes.string
}; };
export default Header; export default Header;

View File

@@ -29,6 +29,25 @@ describe('Header', () => {
}); });
}); });
describe('className prop is exists', () => {
const className = 'test-class';
beforeEach(() => {
wrapper = shallow(
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
className={ className }
/>
);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(`.${className}`).length).toBe(1);
});
});
describe('header with columns enable sort', () => { describe('header with columns enable sort', () => {
const sortField = columns[1].dataField; const sortField = columns[1].dataField;