Compare commits

...

16 Commits

Author SHA1 Message Date
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
AllenFang
258ea43225 Publish
- react-bootstrap-table2-example@0.1.10
 - react-bootstrap-table2-filter@0.3.0
 - react-bootstrap-table2-paginator@0.1.5
 - react-bootstrap-table-next@0.1.14
2018-06-24 22:42:50 +08:00
Allen
7a7b708029 Merge pull request #390 from react-bootstrap-table/develop
20180624 release
2018-06-24 22:40:15 +08:00
Allen
0cf89861af Merge pull request #389 from react-bootstrap-table/feat/custom-filter
Implement custom filter
2018-06-24 15:33:22 +08:00
AllenFang
eb74625835 patch docs for custom filter 2018-06-24 15:15:57 +08:00
AllenFang
cbaec4c655 add stories for custom filter 2018-06-24 15:04:59 +08:00
AllenFang
04c21cb63d implement custom filter 2018-06-24 15:03:46 +08:00
AllenFang
7d72002b6e fix wrong eslint rule 2018-06-24 13:16:48 +08:00
AllenFang
279cc25da0 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2018-06-24 13:16:07 +08:00
AllenFang
1152bb8440 fix #380 2018-06-23 13:49:38 +08:00
AllenFang
88befb8136 fix #380 2018-06-23 13:47:53 +08:00
Allen
42c6bc0337 Merge pull request #371 from sean-ww/fix/remote-filtered-pagination
Prevent remote pagination from setting the page incorrectly
2018-06-23 12:59:02 +08:00
sean
6e753bb955 Prevent remote pagination from setting the page incorrectly 2018-06-06 15:11:54 +02:00
26 changed files with 660 additions and 44 deletions

View File

@@ -18,6 +18,7 @@
* [id](#id)
* [classes](#classes)
* [wrapperClasses](#wrapperClasses)
* [headerClasses](#headerClasses)
* [cellEdit](#cellEdit)
* [selectRow](#selectRow)
* [rowStyle](#rowStyle)
@@ -111,6 +112,10 @@ Customize class on `table` element.
### <a name='wrapperClasses'>wrapperClasses - [String]</a>
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>
Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail.

View File

@@ -82,7 +82,7 @@ Please see [available filter configuration](https://react-bootstrap-table.github
- [x] Text Filter
- [x] Custom Text Filter
- [x] Remote Filter
- [ ] Custom Filter Component
- [x] Custom Filter Component
- [ ] Regex Filter
- [x] Select Filter
- [x] Custom Select Filter

View File

@@ -0,0 +1,184 @@
/* eslint no-return-assign: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter, Comparator, FILTER_TYPES } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
this.onChange = this.onChange.bind(this);
this.state = { value: 2100 };
}
onChange(e) {
this.setState({ value: e.target.value });
}
getValue() {
return parseInt(this.range.value, 10);
}
filter() {
this.props.onFilter({
number: this.getValue(),
comparator: this.select.value
});
}
render() {
return [
<input
key="range"
ref={ node => this.range = node }
type="range"
min="2100"
max="2110"
onChange={ this.onChange }
/>,
<p
key="show"
ref={ node => this.showValue = node }
style={ { textAlign: 'center' } }
>
{ this.state.value }
</p>,
<select
key="select"
ref={ node => this.select = node }
className="form-control"
>
<option value={ Comparator.GT }>&gt;</option>
<option value={ Comparator.EQ }>=</option>
<option value={ Comparator.LT }>&lt;</option>
</select>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ `Filter ${this.props.column.text}` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter({
type: FILTER_TYPES.NUMBER // ask react-bootstrap-table to filter data as number
}),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter, Comparator, FILTER_TYPES } from 'react-bootstrap-table2-filter';
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
this.onChange = this.onChange.bind(this);
this.state = { value: 2100 };
}
onChange(e) {
this.setState({ value: e.target.value });
}
getValue() {
return parseInt(this.range.value, 10);
}
filter() {
this.props.onFilter({
number: this.getValue(),
comparator: this.select.value
});
}
render() {
return [
<input
key="range"
ref={ node => this.range = node }
type="range"
min="2100"
max="2110"
onChange={ this.onChange }
/>,
<p
key="show"
ref={ node => this.showValue = node }
style={ { textAlign: 'center' } }
>
{ this.state.value }
</p>,
<select
key="select"
ref={ node => this.select = node }
className="form-control"
>
<option value={ Comparator.GT }>&gt;</option>
<option value={ Comparator.EQ }>=</option>
<option value={ Comparator.LT }>&lt;</option>
</select>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ \`Filter $\{this.props.column.text}\` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter({
type: FILTER_TYPES.NUMBER // ask react-bootstrap-table to filter data as number
}),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
<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,128 @@
/* eslint no-return-assign: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
}
getValue() {
return this.input.value;
}
filter() {
this.props.onFilter(this.getValue());
}
render() {
return [
<input
key="input"
ref={ node => this.input = node }
type="text"
placeholder="Input price"
/>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ `Find ${this.props.column.text}` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter(),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter, customFilter } from 'react-bootstrap-table2-filter';
class PriceFilter extends React.Component {
static propTypes = {
column: PropTypes.object.isRequired,
onFilter: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.filter = this.filter.bind(this);
this.getValue = this.getValue.bind(this);
}
getValue() {
return this.input.value;
}
filter() {
this.props.onFilter(this.getValue());
}
render() {
return [
<input
key="input"
ref={ node => this.input = node }
type="text"
placeholder="Input price"
/>,
<button
key="submit"
className="btn btn-warning"
onClick={ this.filter }
>
{ \`Filter $\{this.props.column.text}\` }
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: customFilter(),
filterRenderer: (onFilter, column) =>
<PriceFilter onFilter={ onFilter } column={ column } />
}];
<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,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",
"version": "0.1.9",
"version": "0.1.11",
"description": "",
"main": "index.js",
"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 HeaderColumnStyleTable from 'examples/header-columns/column-style-table';
import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
import HeaderClassTable from 'examples/header-columns/header-class-table';
// column filter
import TextFilter from 'examples/column-filter/text-filter';
@@ -55,6 +56,8 @@ import ProgrammaticallyTextFilter from 'examples/column-filter/programmatically-
import ProgrammaticallySelectFilter from 'examples/column-filter/programmatically-select-filter';
import ProgrammaticallyNumberFilter from 'examples/column-filter/programmatically-number-filter';
import ProgrammaticallyDateFilter from 'examples/column-filter/programmatically-date-filter';
import CustomFilter from 'examples/column-filter/custom-filter';
import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter';
// work on rows
import RowStyleTable from 'examples/rows/row-style';
@@ -163,7 +166,8 @@ storiesOf('Work on Header Columns', module)
.add('Column Event', () => <HeaderColumnEventTable />)
.add('Customize Column Class', () => <HeaderColumnClassTable />)
.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)
.add('Text Filter', () => <TextFilter />)
@@ -186,7 +190,9 @@ storiesOf('Column Filter', module)
.add('Programmatically Text Filter', () => <ProgrammaticallyTextFilter />)
.add('Programmatically Select Filter', () => <ProgrammaticallySelectFilter />)
.add('Programmatically Number Filter', () => <ProgrammaticallyNumberFilter />)
.add('Programmatically Date Filter', () => <ProgrammaticallyDateFilter />);
.add('Programmatically Date Filter', () => <ProgrammaticallyDateFilter />)
.add('Custom Filter', () => <CustomFilter />)
.add('Advance Custom Filter', () => <AdvanceCustomFilter />);
storiesOf('Work on Rows', module)
.add('Customize Row Style', () => <RowStyleTable />)

View File

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

View File

@@ -20,6 +20,7 @@ You can get all types of filters via import and these filters are a factory func
* SelectFilter
* NumberFilter
* DateFilter
* CustomFilter
* **Coming soon!**
## Add CSS
@@ -190,3 +191,53 @@ const dateFilter = dateFilter({
// omit...
```
## Custom Filter
```js
import filterFactory, { customFilter } from 'react-bootstrap-table2-filter';
const columns = [..., {
dataField: 'date',
text: 'Product Name',
filter: customFilter(),
filterRenderer: (onFilter, column) => .....
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
```
In custom filter case, you are suppose to finish following two steps:
1. Call `customFilter` and pass to `column.filter`
2. Give `column.filterRenderer` as a callback function and return your custom filter element.
### column.filterRenderer
This function will pass two argument to you:
1. `onFilter`: call it to trigger filter when you need.
2. `column`: Just the column object!
In the end, please remember to return your custom filter element!
### customFilter
`customFilter` function just same as `textFilter`, `selectFilter` etc, it is for customization reason. However, in the custom filter case, there's only one props is valid: `type`
```js
import filterFactory, { FILTER_TYPES } from 'react-bootstrap-table2-filter';
const customFilter = customFilter({
type: FILTER_TYPES.NUMBER, // default is FILTER_TYPES.TEXT
})
```
`type` is a way to ask `react-bootstrap-table` to filter you data as number, select, date or normal text.
### FILTER_TYPES
Following properties is valid in `FILTER_TYPES`:
* TEXT
* SELECT
* NUMBER
* DATE

View File

@@ -4,12 +4,15 @@ import NumberFilter from './src/components/number';
import DateFilter from './src/components/date';
import wrapperFactory from './src/wrapper';
import * as Comparison from './src/comparison';
import { FILTER_TYPE } from './src/const';
export default (options = {}) => ({
wrapperFactory,
options
});
export const FILTER_TYPES = FILTER_TYPE;
export const Comparator = Comparison;
export const textFilter = (props = {}) => ({
@@ -31,3 +34,7 @@ export const dateFilter = (props = {}) => ({
Filter: DateFilter,
props
});
export const customFilter = (props = {}) => ({
props
});

View File

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

View File

@@ -1,4 +1,5 @@
/* eslint react/require-default-props: 0 */
/* eslint jsx-a11y/no-static-element-interactions: 0 */
/* eslint no-return-assign: 0 */
/* eslint prefer-template: 0 */
import React, { Component } from 'react';
@@ -121,7 +122,11 @@ class DateFilter extends Component {
} = this.props;
return (
<div className={ `filter date-filter ${className}` } style={ style }>
<div
onClick={ e => e.stopPropagation() }
className={ `filter date-filter ${className}` }
style={ style }
>
<select
ref={ n => this.dateFilterComparator = n }
style={ comparatorStyle }

View File

@@ -1,3 +1,4 @@
/* eslint jsx-a11y/no-static-element-interactions: 0 */
/* eslint react/require-default-props: 0 */
/* eslint no-return-assign: 0 */
@@ -167,7 +168,11 @@ class NumberFilter extends Component {
`;
return (
<div className={ `filter number-filter ${className}` } style={ style }>
<div
onClick={ e => e.stopPropagation() }
className={ `filter number-filter ${className}` }
style={ style }
>
<select
ref={ n => this.numberFilterComparator = n }
style={ comparatorStyle }

View File

@@ -116,6 +116,7 @@ class SelectFilter extends Component {
style={ style }
className={ selectClass }
onChange={ this.filter }
onClick={ e => e.stopPropagation() }
defaultValue={ defaultValue !== undefined ? defaultValue : '' }
>
{ this.getOptions() }

View File

@@ -20,6 +20,7 @@ export default (Base, {
super(props);
this.state = { currFilters: {}, isDataChanged: props.isDataChanged || false };
this.onFilter = this.onFilter.bind(this);
this.onExternalFilter = this.onExternalFilter.bind(this);
}
componentWillReceiveProps({ isDataChanged, store, columns }) {
@@ -78,12 +79,19 @@ export default (Base, {
};
}
onExternalFilter(column, filterType) {
return (value) => {
this.onFilter(column, filterType)(value);
};
}
render() {
return (
<Base
{ ...this.props }
data={ this.props.store.data }
onFilter={ this.onFilter }
onExternalFilter={ this.onExternalFilter }
isDataChanged={ this.state.isDataChanged }
/>
);

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-paginator",
"version": "0.1.4",
"version": "0.1.6",
"description": "it's the pagination addon for react-bootstrap-table2",
"main": "./lib/index.js",
"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) => {
const dataSize = store.data.length;
if (!dataSize) return [];
const getNormalizedPage = () => {
const offset = Math.abs(1 - pageStartIndex);
return store.page + offset;
};
const end = (getNormalizedPage() * store.sizePerPage) - 1;
const start = end - (store.sizePerPage - 1);
const end = endIndex(store.page, store.sizePerPage, pageStartIndex);
const start = startIndex(end, store.sizePerPage);
const result = [];
for (let i = start; i <= end; i += 1) {

View File

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import Const from './const';
import Pagination from './pagination';
import { getByCurrPage } from './page';
import { getByCurrPage, alignPage } from './page';
export default (Base, {
remoteResolver
@@ -49,16 +49,23 @@ export default (Base, {
componentWillReceiveProps(nextProps) {
let needNewState = false;
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
currPage = page;
needNewState = true;
} else if (nextProps.isDataChanged) { // user didn't defined page but data change
currPage = typeof pageStartIndex !== 'undefined' ? pageStartIndex : Const.PAGE_START_INDEX;
} else if (nextProps.isDataChanged) {
currPage = alignPage(this.props.store, pageStartIndex, currSizePerPage);
needNewState = true;
}
if (typeof currPage === 'undefined') {
currPage = pageStartIndex;
}
if (typeof sizePerPage !== 'undefined') {
currSizePerPage = sizePerPage;
needNewState = true;

View File

@@ -1,5 +1,5 @@
import Store from 'react-bootstrap-table-next/src/store';
import { getByCurrPage } from '../src/page';
import { getByCurrPage, alignPage } from '../src/page';
describe('Page Functions', () => {
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,22 +176,6 @@ describe('Wrapper', () => {
expect(props.store.page).toEqual(instance.state.currPage);
});
});
describe('when nextProps.isDataChanged is true and options.pageStartIndex is existing', () => {
beforeEach(() => {
nextProps.isDataChanged = true;
nextProps.pagination.options.pageStartIndex = 0;
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",
"version": "0.1.13",
"version": "0.1.15",
"description": "Next generation of react-bootstrap-table",
"main": "./lib/index.js",
"repository": {

View File

@@ -86,10 +86,12 @@ class BootstrapTable extends PropsBaseResolver(Component) {
{ tableCaption }
<Header
columns={ columns }
className={ this.props.headerClasses }
sortField={ store.sortField }
sortOrder={ store.sortOrder }
onSort={ this.props.onSort }
onFilter={ this.props.onFilter }
onExternalFilter={ this.props.onExternalFilter }
selectRow={ headerCellSelectionInfo }
/>
<Body
@@ -154,6 +156,7 @@ BootstrapTable.propTypes = {
rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
rowEvents: PropTypes.object,
rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
headerClasses: PropTypes.string,
defaultSorted: PropTypes.arrayOf(PropTypes.shape({
dataField: PropTypes.string.isRequired,
order: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]).isRequired
@@ -162,7 +165,8 @@ BootstrapTable.propTypes = {
overlay: PropTypes.func,
onTableChange: PropTypes.func,
onSort: PropTypes.func,
onFilter: PropTypes.func
onFilter: PropTypes.func,
onExternalFilter: PropTypes.func
};
BootstrapTable.defaultProps = {

View File

@@ -17,13 +17,15 @@ const HeaderCell = (props) => {
sorting,
sortOrder,
isLastSorting,
onFilter
onFilter,
onExternalFilter
} = props;
const {
text,
sort,
filter,
filterRenderer,
headerTitle,
headerAlign,
headerFormatter,
@@ -89,7 +91,11 @@ const HeaderCell = (props) => {
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
if (filter) {
if (filterRenderer) {
const onCustomFilter = onExternalFilter(column, filter.props.type);
filterElm = filterRenderer(onCustomFilter, column);
} else if (filter) {
filterElm = <filter.Filter { ...filter.props } onFilter={ onFilter } column={ column } />;
}
@@ -136,6 +142,7 @@ HeaderCell.propTypes = {
editorRenderer: PropTypes.func,
validator: PropTypes.func,
filter: PropTypes.object,
filterRenderer: PropTypes.func,
filterValue: PropTypes.func
}).isRequired,
index: PropTypes.number.isRequired,
@@ -143,7 +150,8 @@ HeaderCell.propTypes = {
sorting: PropTypes.bool,
sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]),
isLastSorting: PropTypes.bool,
onFilter: PropTypes.func
onFilter: PropTypes.func,
onExternalFilter: PropTypes.func
};
export default HeaderCell;

View File

@@ -10,17 +10,19 @@ const Header = (props) => {
const { ROW_SELECT_DISABLED } = Const;
const {
className,
columns,
onSort,
onFilter,
sortField,
sortOrder,
selectRow
selectRow,
onExternalFilter
} = props;
return (
<thead>
<tr>
<tr className={ className }>
{
(selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn)
? <SelectionHeaderCell { ...selectRow } /> : null
@@ -39,6 +41,7 @@ const Header = (props) => {
onSort={ onSort }
sorting={ currSort }
onFilter={ onFilter }
onExternalFilter={ onExternalFilter }
sortOrder={ sortOrder }
isLastSorting={ isLastSorting }
/>);
@@ -57,7 +60,9 @@ Header.propTypes = {
onFilter: PropTypes.func,
sortField: PropTypes.string,
sortOrder: PropTypes.string,
selectRow: PropTypes.object
selectRow: PropTypes.object,
onExternalFilter: PropTypes.func,
className: PropTypes.string
};
export default Header;

View File

@@ -669,4 +669,74 @@ describe('HeaderCell', () => {
});
});
});
describe('when column.filter is defined', () => {
const onFilter = jest.fn();
const filterProps = { a: 123 };
const Filter = () => <div>test</div>;
let column;
beforeEach(() => {
onFilter.mockClear();
column = {
dataField: 'id',
text: 'ID',
filter: {
props: filterProps,
Filter
}
};
wrapper = shallow(<HeaderCell column={ column } index={ index } onFilter={ onFilter } />);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('th').length).toBe(1);
});
it('should render filter correctly', () => {
expect(wrapper.find(Filter).length).toBe(1);
expect(wrapper.find(Filter).props()).toEqual({
column,
onFilter,
...filterProps
});
});
});
describe('when column.filter and column.filterRenderer is defined', () => {
const onExternalFilter = jest.fn();
const filterProps = { a: 123 };
const Filter = () => <div>test</div>;
const filterRenderer = jest.fn().mockReturnValue(<Filter />);
let column;
beforeEach(() => {
onExternalFilter.mockClear();
filterRenderer.mockClear();
column = {
dataField: 'id',
text: 'ID',
filter: {
props: filterProps
},
filterRenderer
};
wrapper = shallow(
<HeaderCell column={ column } index={ index } onExternalFilter={ onExternalFilter } />);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('th').length).toBe(1);
});
it('should render filter correctly', () => {
expect(wrapper.find(Filter).length).toBe(1);
});
it('should call filterRenderer function correctly', () => {
expect(filterRenderer).toHaveBeenCalledTimes(1);
});
});
});

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', () => {
const sortField = columns[1].dataField;