Compare commits

..

28 Commits

Author SHA1 Message Date
AllenFang
64df3e1fae Publish
- react-bootstrap-table2-example@0.1.9
 - react-bootstrap-table2-filter@0.2.0
 - react-bootstrap-table2-overlay@0.1.2
 - react-bootstrap-table2-paginator@0.1.4
 - react-bootstrap-table-next@0.1.13
2018-06-04 13:53:02 +08:00
Allen
5cdd1ad093 20180604 release #366 from react-bootstrap-table/develop
20180604 release
2018-06-04 13:49:03 +08:00
AllenFang
36e754b6bc patch docs 2018-06-03 22:58:03 +08:00
AllenFang
6730dcf60d fix overlay test bugs 2018-06-03 22:52:15 +08:00
AllenFang
fb54809dc9 fix docs 2018-06-03 21:36:09 +08:00
AllenFang
d43c622fdb fix docs 2018-06-03 21:31:13 +08:00
AllenFang
7253d7a1d7 fix overlay tests 2018-06-03 20:40:31 +08:00
AllenFang
a6daa50417 improve overlay wrapping 2018-06-03 15:50:10 +08:00
AllenFang
b11019ce20 fix #358 2018-06-03 14:49:58 +08:00
Allen
dda47f7b7d Merge pull request #365 from react-bootstrap-table/feat/349
Feat/349
2018-06-03 14:39:22 +08:00
AllenFang
4da8ba7ecc patch docs for date filter 2018-06-03 14:24:20 +08:00
Allen
2ff0b27747 Merge pull request #359 from sean-ww/feature/pagination-total
Adding custom pagination total
2018-06-03 14:18:50 +08:00
AllenFang
c3f279fb0c patch tests for date filter 2018-06-03 14:04:06 +08:00
AllenFang
06bcf1edca add stories for date filter 2018-06-03 14:03:38 +08:00
AllenFang
fc1f75cfac implement date filter 2018-06-03 14:02:39 +08:00
sean
1cf12ab707 paginationTotal renamed to paginationTotalRenderer 2018-06-02 10:43:56 +02:00
Allen
288ccc1049 Merge pull request #364 from react-bootstrap-table/feat/351
Fix #351
2018-06-02 15:37:51 +08:00
AllenFang
f13c139f63 patch docs for custom selection 2018-06-02 15:27:10 +08:00
AllenFang
e72ad0586e add story for custom selection column 2018-06-02 15:20:57 +08:00
AllenFang
c2044fe8b5 patch test for selection box 2018-06-02 15:20:40 +08:00
AllenFang
a7b3690a7c custom selection box 2018-06-02 15:20:19 +08:00
Amol Udage
68afc348db fixes sorting issue (#354)
+ when remote sort is true then disable client side sorting
2018-06-02 13:21:25 +08:00
sean-ww88
5404124a78 Added missing commas on the custom pagination example 2018-05-30 17:35:27 +02:00
sean-ww88
d592871c0d Adding custom pagination total 2018-05-30 17:29:58 +02:00
AllenFang
6019e550fd Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2018-05-28 18:07:31 +08:00
AllenFang
765a49fb07 Publish
- react-bootstrap-table-next@0.1.12
2018-05-23 22:44:40 +08:00
Allen
fe2fd93c20 fix the bool rendering issues in React (#340) (#348) 2018-05-23 22:42:46 +08:00
Allen
19ba336e32 gix the bool rendering issues in React (#340) 2018-05-19 13:07:57 +08:00
39 changed files with 1338 additions and 89 deletions

View File

@@ -202,6 +202,7 @@ paginator({
totalSize, // Total data size. It's necessary when remote is enabled
pageStartIndex: 0, // first page will be 0, default is 1
paginationSize: 3, // the pagination bar size, default is 5
showTotal: true, // display pagination information
sizePerPageList: [ {
text: '5', value: 5
}, {
@@ -223,6 +224,7 @@ paginator({
hidePageListOnlyOnePage: true, // hide pagination bar when only one page, default is false
onPageChange: (page, sizePerPage) => {}, // callback function when page was changing
onSizePerPageChange: (sizePerPage, page) => {}, // callback function when page size was changing
paginationTotalRenderer: (from, to, size) => { ... } // custom the pagination total
})
```

View File

@@ -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`:

View File

@@ -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.

View File

@@ -16,6 +16,8 @@
* [onSelect](#onSelect)
* [onSelectAll](#onSelectAll)
* [hideSelectColumn](#hideSelectColumn)
* [selectionRenderer](#selectionRenderer)
* [selectionHeaderRenderer](#selectionHeaderRenderer)
### <a name="mode">selectRow.mode - [String]</a>
@@ -156,6 +158,34 @@ const selectRow = {
};
```
### <a name='selectionRenderer'>selectRow.selectionRenderer - [Function]</a>
Provide a callback function which allow you to custom the checkbox/radio box. This callback only have one argument which is an object and contain following properties:
```js
const selectRow = {
mode: 'checkbox',
selectionRenderer: ({ mode, checked, disabled }) => (
// ....
)
};
```
> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer.
### <a name='selectionHeaderRenderer'>selectRow.selectionHeaderRenderer - [Function]</a>
Provide a callback function which allow you to custom the checkbox/radio box in the selection header column. This callback only have one argument which is an object and contain following properties:
```js
const selectRow = {
mode: 'checkbox',
selectionHeaderRenderer: ({ mode, checked, indeterminate }) => (
// ....
)
};
```
> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer.
### <a name='onSelect'>selectRow.onSelect - [Function]</a>
This callback function will be called when a row is select/unselect and pass following three arguments:
`row`, `isSelect`, `rowIndex` and `e`.

View 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>
);

View 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>
);

View 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>
);

View 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>
);

View File

@@ -24,6 +24,12 @@ import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// ...
const customTotal = (from, to, size) => (
<span className="react-bootstrap-table-pagination-total">
Showing { from } to { to } of { size } Results
</span>
);
const options = {
paginationSize: 4,
pageStartIndex: 0,
@@ -39,6 +45,8 @@ const options = {
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
paginationTotalRenderer: customTotal,
sizePerPageList: [{
text: '5', value: 5
}, {
@@ -50,11 +58,18 @@ const options = {
<BootstrapTable keyField='id' data={ products } columns={ columns } pagination={ paginationFactory(options) } />
`;
const customTotal = (from, to, size) => (
<span className="react-bootstrap-table-pagination-total">
Showing { from } to { to } of { size } Results
</span>
);
const options = {
paginationSize: 4,
pageStartIndex: 0,
// alwaysShowAllBtns: true // Always show next and previous button
// withFirstAndLast: false // Hide the going to First and Last page button
// alwaysShowAllBtns: true, // Always show next and previous button
// withFirstAndLast: false, // Hide the going to First and Last page button
// hideSizePerPage: true, // Hide the sizePerPage dropdown always
// hidePageListOnlyOnePage: true, // Hide the pagination list when only one page
firstPageText: 'First',
@@ -66,6 +81,7 @@ const options = {
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
showTotal: true,
paginationTotalRenderer: customTotal,
sizePerPageList: [{
text: '5', value: 5
}, {

View File

@@ -0,0 +1,107 @@
/* eslint react/prop-types: 0 */
/* eslint no-param-reassign: 0 */
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 selectRow1 = {
mode: 'radio',
clickToSelect: true,
selectionHeaderRenderer: () => 'X',
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
const selectRow2 = {
mode: 'checkbox',
clickToSelect: true,
selectionHeaderRenderer: ({ indeterminate, ...rest }) => (
<input
type="checkbox"
ref={ (input) => {
if (input) input.indeterminate = indeterminate;
} }
{ ...rest }
/>
),
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
const sourceCode1 = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = ....;
const selectRow = {
mode: 'radio',
clickToSelect: true,
selectionHeaderRenderer: () => 'X',
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
const sourceCode2 = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = ....;
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
selectionHeaderRenderer: ({ indeterminate, ...rest }) => (
<input
type="checkbox"
ref={ (input) => {
if (input) input.indeterminate = indeterminate;
} }
{ ...rest }
/>
),
selectionRenderer: ({ mode, ...rest }) => (
<input type={ mode } { ...rest } />
)
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow1 } />
<Code>{ sourceCode1 }</Code>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow2 } />
<Code>{ sourceCode2 }</Code>
</div>
);

View File

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

View File

@@ -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';
@@ -95,6 +99,7 @@ import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-sele
import SelectionNoDataTable from 'examples/row-selection/selection-no-data';
import SelectionStyleTable from 'examples/row-selection/selection-style';
import SelectionClassTable from 'examples/row-selection/selection-class';
import CustomSelectionTable from 'examples/row-selection/custom-selection';
import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows';
import SelectionBgColorTable from 'examples/row-selection/selection-bgcolor';
import SelectionHooks from 'examples/row-selection/selection-hooks';
@@ -171,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 />)
@@ -222,6 +231,7 @@ storiesOf('Row Selection', module)
.add('Selection without Data', () => <SelectionNoDataTable />)
.add('Selection Style', () => <SelectionStyleTable />)
.add('Selection Class', () => <SelectionClassTable />)
.add('Custom Selection', () => <CustomSelectionTable />)
.add('Selection Background Color', () => <SelectionBgColorTable />)
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />)
.add('Selection Hooks', () => <SelectionHooks />)

View File

@@ -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
@@ -150,3 +151,42 @@ const numberFilter = numberFilter({
// 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 date 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...
```

View File

@@ -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
});

View File

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

View 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;

View File

@@ -1,7 +1,8 @@
export const FILTER_TYPE = {
TEXT: 'TEXT',
SELECT: 'SELECT',
NUMBER: 'NUMBER'
NUMBER: 'NUMBER',
DATE: 'DATE'
};
export const FILTER_DELAY = 500;

View File

@@ -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(_);
}

View File

@@ -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;
}

View File

@@ -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();
});
});
});

View File

@@ -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....
});
});

View File

@@ -1,16 +1,29 @@
/* eslint no-return-assign: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import LoadingOverlay from 'react-loading-overlay';
export default options => (element, loading) =>
export default options => loading =>
class TableLoadingOverlayWrapper extends React.Component {
static propTypes = {
children: PropTypes.element.isRequired
}
componentDidMount() {
if (loading) {
const { wrapper } = this.overlay;
const masker = wrapper.firstChild;
const headerDOM = wrapper.parentElement.querySelector('thead');
const bodyDOM = wrapper.parentElement.querySelector('tbody');
masker.style.marginTop = window.getComputedStyle(headerDOM).height;
const captionDOM = wrapper.parentElement.querySelector('caption');
let marginTop = window.getComputedStyle(headerDOM).height;
if (captionDOM) {
marginTop = parseFloat(marginTop.replace('px', ''));
marginTop += parseFloat(window.getComputedStyle(captionDOM).height.replace('px', ''));
marginTop = `${marginTop}px`;
}
masker.style.marginTop = marginTop;
masker.style.height = window.getComputedStyle(bodyDOM).height;
}
}
@@ -22,7 +35,7 @@ export default options => (element, loading) =>
{ ...options }
active={ loading }
>
{ element }
{ this.props.children }
</LoadingOverlay>
);
}

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-overlay",
"version": "0.1.1",
"version": "0.1.2",
"description": "it's a loading overlay addons for react-bootstrap-table2",
"main": "./lib/index.js",
"repository": {

View File

@@ -1,12 +1,11 @@
import React from 'react';
import { shallow, render } from 'enzyme';
import { render, shallow } from 'enzyme';
import LoadingOverlay from 'react-loading-overlay';
import overlayFactory from '..';
import overlayFactory from '../index.js';
describe('overlayFactory', () => {
let wrapper;
// let instance;
const createTable = () => (
<table>
@@ -27,8 +26,8 @@ describe('overlayFactory', () => {
describe('when loading is false', () => {
beforeEach(() => {
const tableElm = createTable();
const Overlay = overlayFactory()(tableElm, false);
wrapper = shallow(<Overlay />);
const Overlay = overlayFactory()(false);
wrapper = shallow(<Overlay>{ tableElm }</Overlay>);
});
it('should rendering Overlay component correctly', () => {
@@ -42,14 +41,12 @@ describe('overlayFactory', () => {
describe('when loading is true', () => {
beforeEach(() => {
const tableElm = createTable();
const Overlay = overlayFactory()(tableElm, true);
wrapper = render(<Overlay />);
const Overlay = overlayFactory()(true);
wrapper = render(<Overlay>{ tableElm }</Overlay>);
});
it('should rendering Overlay component correctly', () => {
const overlay = wrapper.find(LoadingOverlay);
expect(wrapper.length).toBe(1);
expect(overlay.length).toBe(0);
});
});
@@ -60,8 +57,8 @@ describe('overlayFactory', () => {
};
beforeEach(() => {
const tableElm = createTable();
const Overlay = overlayFactory(options)(tableElm, false);
wrapper = shallow(<Overlay />);
const Overlay = overlayFactory(options)(false);
wrapper = shallow(<Overlay>{ tableElm }</Overlay>);
});
it('should rendering Overlay component with options correctly', () => {

View File

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

View File

@@ -3,6 +3,8 @@ export default {
PAGE_START_INDEX: 1,
With_FIRST_AND_LAST: true,
SHOW_ALL_PAGE_BTNS: false,
SHOW_TOTAL: false,
PAGINATION_TOTAL: null,
FIRST_PAGE_TEXT: '<<',
PRE_PAGE_TEXT: '<',
NEXT_PAGE_TEXT: '>',

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
const PaginationTotal = props => (
<span>
<span className="react-bootstrap-table-pagination-total">
&nbsp;Showing rows { props.from } to&nbsp;{ props.to + 1 } of&nbsp;{ props.dataSize }
</span>
);

View File

@@ -87,10 +87,28 @@ class Pagination extends pageResolver(Component) {
}
}
defaultTotal = (from, to, size) => (
<PaginationTotal
from={ from }
to={ to }
dataSize={ size }
/>
);
setTotal = (from, to, size, total) => {
if (total && (typeof total === 'function')) {
return total(from, to, size);
}
return this.defaultTotal(from, to, size);
};
render() {
const { totalPages, lastPage, dropdownOpen: open } = this.state;
const {
showTotal,
dataSize,
paginationTotalRenderer,
sizePerPageList,
currSizePerPage,
hideSizePerPage,
@@ -121,11 +139,12 @@ class Pagination extends pageResolver(Component) {
}
{
showTotal ?
<PaginationTotal
from={ from }
to={ to }
dataSize={ this.props.dataSize }
/> : null
this.setTotal(
from,
to,
dataSize,
paginationTotalRenderer
) : null
}
</div>
<div className={ pageListClass }>
@@ -145,6 +164,8 @@ Pagination.propTypes = {
onSizePerPageChange: PropTypes.func.isRequired,
pageStartIndex: PropTypes.number,
paginationSize: PropTypes.number,
showTotal: PropTypes.bool,
paginationTotalRenderer: PropTypes.func,
firstPageText: PropTypes.string,
prePageText: PropTypes.string,
nextPageText: PropTypes.string,
@@ -164,6 +185,8 @@ Pagination.defaultProps = {
paginationSize: Const.PAGINATION_SIZE,
withFirstAndLast: Const.With_FIRST_AND_LAST,
alwaysShowAllBtns: Const.SHOW_ALL_PAGE_BTNS,
showTotal: Const.SHOW_TOTAL,
paginationTotalRenderer: Const.PAGINATION_TOTAL,
firstPageText: Const.FIRST_PAGE_TEXT,
prePageText: Const.PRE_PAGE_TEXT,
nextPageText: Const.NEXT_PAGE_TEXT,

View File

@@ -146,6 +146,7 @@ export default (Base, {
hideSizePerPage={ hideSizePerPage }
hidePageListOnlyOnePage={ hidePageListOnlyOnePage }
showTotal={ options.showTotal }
paginationTotalRenderer={ options.paginationTotalRenderer }
firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT }
prePageText={ options.prePageText || Const.PRE_PAGE_TEXT }
nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT }

View File

@@ -15,7 +15,7 @@ const data = [];
for (let i = 0; i < 100; i += 1) {
data.push({
id: i,
name: `itme name ${i}`
name: `item name ${i}`
});
}
@@ -67,29 +67,29 @@ describe('Wrapper', () => {
createPaginationWrapper(props);
});
it('should rendering correctly', () => {
it('should render correctly', () => {
expect(wrapper.length).toBe(1);
});
it('should initializing state correctly', () => {
it('should initialize state correctly', () => {
expect(instance.state.currPage).toBeDefined();
expect(instance.state.currPage).toEqual(Const.PAGE_START_INDEX);
expect(instance.state.currSizePerPage).toBeDefined();
expect(instance.state.currSizePerPage).toEqual(Const.SIZE_PER_PAGE_LIST[0]);
});
it('should saving page and sizePerPage to store correctly', () => {
it('should save page and sizePerPage to the store correctly', () => {
expect(props.store.page).toBe(instance.state.currPage);
expect(props.store.sizePerPage).toBe(instance.state.currSizePerPage);
});
it('should rendering BootstraTable correctly', () => {
it('should render BootstrapTable correctly', () => {
const table = wrapper.find(BootstrapTable);
expect(table.length).toBe(1);
expect(table.prop('data').length).toEqual(instance.state.currSizePerPage);
});
it('should rendering Pagination correctly', () => {
it('should render Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(pagination.length).toBe(1);
expect(pagination.prop('dataSize')).toEqual(props.store.data.length);
@@ -111,7 +111,7 @@ describe('Wrapper', () => {
expect(pagination.prop('nextPageTitle')).toEqual(Const.NEXT_PAGE_TITLE);
expect(pagination.prop('lastPageTitle')).toEqual(Const.LAST_PAGE_TITLE);
expect(pagination.prop('hideSizePerPage')).toEqual(Const.HIDE_SIZE_PER_PAGE);
expect(pagination.prop('showTotal')).toEqual(undefined);
expect(pagination.prop('showTotal')).toBeFalsy();
});
describe('componentWillReceiveProps', () => {
@@ -254,7 +254,7 @@ describe('Wrapper', () => {
createPaginationWrapper(props);
});
it('should rendering Pagination correctly', () => {
it('should render Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(wrapper.length).toBe(1);
expect(pagination.length).toBe(1);

View File

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

View File

@@ -29,12 +29,15 @@ class BootstrapTable extends PropsBaseResolver(Component) {
render() {
const { loading, overlay } = this.props;
const table = this.renderTable();
if (loading && overlay) {
const LoadingOverlay = overlay(table, loading);
return <LoadingOverlay />;
if (overlay) {
const LoadingOverlay = overlay(loading);
return (
<LoadingOverlay>
{ this.renderTable() }
</LoadingOverlay>
);
}
return table;
return this.renderTable();
}
renderTable() {
@@ -142,7 +145,9 @@ BootstrapTable.propTypes = {
classes: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
nonSelectable: PropTypes.array,
bgColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
hideSelectColumn: PropTypes.bool
hideSelectColumn: PropTypes.bool,
selectionRenderer: PropTypes.func,
selectionHeaderRenderer: PropTypes.func
}),
onRowSelect: PropTypes.func,
onAllRowsSelect: PropTypes.func,

View File

@@ -88,7 +88,9 @@ class Cell extends Component {
cellAttrs.onDoubleClick = this.handleEditingCell;
}
return (
<td { ...cellAttrs }>{ content }</td>
<td { ...cellAttrs }>
{ typeof content === 'boolean' ? `${content}` : content }
</td>
);
}
}

View File

@@ -14,7 +14,8 @@ export default class SelectionCell extends Component {
onRowSelect: PropTypes.func,
disabled: PropTypes.bool,
rowIndex: PropTypes.number,
clickToSelect: PropTypes.bool
clickToSelect: PropTypes.bool,
selectionRenderer: PropTypes.func
}
constructor() {
@@ -53,16 +54,25 @@ export default class SelectionCell extends Component {
const {
mode: inputType,
selected,
disabled
disabled,
selectionRenderer
} = this.props;
return (
<td onClick={ this.handleClick }>
{
selectionRenderer ? selectionRenderer({
mode: inputType,
checked: selected,
disabled
}) : (
<input
type={ inputType }
checked={ selected }
disabled={ disabled }
/>
)
}
</td>
);
}

View File

@@ -22,7 +22,8 @@ export default class SelectionHeaderCell extends Component {
static propTypes = {
mode: PropTypes.string.isRequired,
checkedStatus: PropTypes.string,
onAllRowsSelect: PropTypes.func
onAllRowsSelect: PropTypes.func,
selectionHeaderRenderer: PropTypes.func
}
constructor() {
@@ -52,25 +53,37 @@ export default class SelectionHeaderCell extends Component {
render() {
const {
CHECKBOX_STATUS_CHECKED, CHECKBOX_STATUS_INDETERMINATE, ROW_SELECT_SINGLE
CHECKBOX_STATUS_CHECKED, CHECKBOX_STATUS_INDETERMINATE, ROW_SELECT_MULTIPLE
} = Const;
const { mode, checkedStatus } = this.props;
const { mode, checkedStatus, selectionHeaderRenderer } = this.props;
const checked = checkedStatus === CHECKBOX_STATUS_CHECKED;
const indeterminate = checkedStatus === CHECKBOX_STATUS_INDETERMINATE;
return mode === ROW_SELECT_SINGLE
? <th data-row-selection />
: (
<th data-row-selection onClick={ this.handleCheckBoxClick }>
const attrs = {};
let content;
if (selectionHeaderRenderer) {
content = selectionHeaderRenderer({
mode,
checked,
indeterminate
});
attrs.onClick = this.handleCheckBoxClick;
} else if (mode === ROW_SELECT_MULTIPLE) {
content = (
<CheckBox
{ ...this.props }
checked={ checked }
indeterminate={ indeterminate }
/>
</th>
);
attrs.onClick = this.handleCheckBoxClick;
}
return (
<th data-row-selection { ...attrs }>{ content }</th>
);
}
}

View File

@@ -39,6 +39,7 @@ export default Base =>
}
componentWillReceiveProps(nextProps) {
if (!this.isRemoteSort() && !this.isRemotePagination()) {
let sortedColumn;
for (let i = 0; i < nextProps.columns.length; i += 1) {
if (nextProps.columns[i].dataField === nextProps.store.sortField) {
@@ -50,6 +51,7 @@ export default Base =>
nextProps.store.sortBy(sortedColumn);
}
}
}
handleSort(column) {
const { store } = this.props;

View File

@@ -27,6 +27,25 @@ describe('Cell', () => {
});
});
describe('when content is bool value', () => {
const column = {
dataField: 'col1',
text: 'column 1'
};
const aRowWithBoolValue = { col1: true };
beforeEach(() => {
wrapper = shallow(
<Cell row={ aRowWithBoolValue } columnIndex={ 1 } rowIndex={ 1 } column={ column } />
);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.text()).toEqual(aRowWithBoolValue[column.dataField].toString());
});
});
describe('when column.formatter prop is defined', () => {
const rowIndex = 1;
const column = {

View File

@@ -193,5 +193,36 @@ describe('<SelectionCell />', () => {
expect(wrapper.find('input').get(0).props.disabled).toBeTruthy();
});
});
describe('when selectionRenderer prop is defined', () => {
const DummySelection = () => <div className="dummy" />;
const selectionRenderer = jest.fn().mockReturnValue(<DummySelection />);
beforeEach(() => {
selectionRenderer.mockClear();
wrapper = shallow(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
selectionRenderer={ selectionRenderer }
/>
);
});
it('should render component correctly', () => {
expect(wrapper.find(DummySelection)).toHaveLength(1);
});
it('should call props.selectionRenderer correctly', () => {
expect(selectionRenderer).toHaveBeenCalledTimes(1);
expect(selectionRenderer).toHaveBeenCalledWith({
mode,
checked: selected,
disabled: wrapper.prop('disabled')
});
});
});
});
});

View File

@@ -126,6 +126,36 @@ describe('<SelectionHeaderCell />', () => {
expect(wrapper.find(CheckBox).get(0).props.indeterminate).toBe(indeterminate);
});
});
describe('when props.selectionHeaderRenderer is defined', () => {
const checkedStatus = Const.CHECKBOX_STATUS_CHECKED;
const DummySelection = () => <div className="dummy" />;
const selectionHeaderRenderer = jest.fn().mockReturnValue(<DummySelection />);
beforeEach(() => {
selectionHeaderRenderer.mockClear();
wrapper = shallow(
<SelectionHeaderCell
mode="checkbox"
checkedStatus={ checkedStatus }
selectionHeaderRenderer={ selectionHeaderRenderer }
/>
);
});
it('should render correctly', () => {
expect(wrapper.find(DummySelection)).toHaveLength(1);
});
it('should call props.selectionHeaderRenderer correctly', () => {
expect(selectionHeaderRenderer).toHaveBeenCalledTimes(1);
expect(selectionHeaderRenderer).toHaveBeenCalledWith({
mode: 'checkbox',
checked: checkedStatus === Const.CHECKBOX_STATUS_CHECKED,
indeterminate: checkedStatus === Const.CHECKBOX_STATUS_INDETERMINATE
});
});
});
});
});