Compare commits

...

12 Commits

Author SHA1 Message Date
AllenFang
cf5b24e9e8 Publish
- react-bootstrap-table2-editor@1.4.0
 - react-bootstrap-table2-example@1.0.31
 - react-bootstrap-table2-filter@1.2.0
 - react-bootstrap-table2-paginator@2.1.0
 - react-bootstrap-table2-toolkit@2.1.0
 - react-bootstrap-table-next@3.2.0
2019-09-08 15:31:51 +08:00
Allen
da86b4aa1a Merge pull request #1087 from react-bootstrap-table/develop
20190908 release
2019-09-08 15:30:23 +08:00
AllenFang
7d28d46185 enhance footer event 2019-09-07 16:27:18 +08:00
AllenFang
16128e77e6 fix #1077 2019-09-07 16:25:49 +08:00
AllenFang
ec1f96cd1f fix #1082 2019-09-07 15:56:13 +08:00
AllenFang
00b1558df0 fix #1078 2019-09-07 14:44:18 +08:00
AllenFang
4dc5e6099f Merge branch 'chimericdream-master' into develop 2019-08-31 14:19:15 +08:00
AllenFang
d9acbace67 refine #1073 2019-08-31 14:18:55 +08:00
Bill Parrott
7c8bf00cde execute react-codemod on entire codebase to remove warnings on React 16.9.x 2019-08-28 15:32:22 -05:00
Bill Parrott
63df43a1e0 Merge branch 'master' of github.com:react-bootstrap-table/react-bootstrap-table2 2019-08-28 14:32:12 -05:00
Bill Parrott
2b410fb8ac Merge branch 'master' of github.com:react-bootstrap-table/react-bootstrap-table2 2019-08-12 13:34:14 -05:00
Bill Parrott
e31b5eb691 set tabindex to 0 for table headers; fixes #955 2019-06-03 09:26:15 -05:00
39 changed files with 568 additions and 260 deletions

View File

@@ -13,6 +13,7 @@ Available properties in a column object:
* [formatExtraData](#formatExtraData)
* [type](#type)
* [sort](#sort)
* [sortValue](#sortValue)
* [sortFunc](#sortFunc)
* [sortCaret](#sortCaret)
* [onSort](#onSort)
@@ -141,8 +142,42 @@ Specify the data type on column. Available value so far is `string`, `number`, `
## <a name='sort'>column.sort - [Bool]</a>
Enable the column sort via a `true` value given.
## <a name='sortValue'>column.sortValue - [Function]</a>
`column.sortValue` only work when `column.sort` enabled. This prop allow you to replace the value when table sorting.
For example, consider following data:
```js
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
const data = [{id: 1, type: 2}, {id: 2, type: 1}, {id: 3, type:0}];
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'type',
text: 'Job Type'
sort: true,
formatter: (cell, row) => types[cell]
}]
```
In above case, when user try to sort Job Type column which will sort the original value: 0, 1, 2 but we display the type name via [`column.formatter`](#formatter), which will lead confuse because we are sorting by type value instead of type name. So `sortValue` is a way for you to decide what kind of value should be adopted when sorting on a specify column:
```js
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'type',
text: 'Job Type'
sort: true,
formatter: (cell, row) => types[cell],
sortValue: (cell, row) => types[cell] // we use type name to sort.
}]
```
## <a name='sortFunc'>column.sortFunc - [Function]</a>
`column.sortFunc` only work when `column.sort` is enable. `sortFunc` allow you to define your sorting algorithm. This callback function accept six arguments:
`column.sortFunc` only work when `column.sort` enabled. `sortFunc` allow you to define your sorting algorithm. This callback function accept six arguments:
```js
{
@@ -421,7 +456,7 @@ If the events is not listed above, the callback function will only pass the `eve
{
// omit...
headerEvents: {
onClick: e => { ... }
onClick: (e, column, columnIndex) => { ... }
}
}
```
@@ -631,7 +666,7 @@ It's also available to custom via a callback function:
{
// omit...
footerEvents: {
onClick: e => { ... }
onClick: (e, column, columnIndex) => { ... }
}
}
```

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-editor",
"version": "1.3.2",
"version": "1.4.0",
"description": "it's the editor addon for react-bootstrap-table2",
"main": "./lib/index.js",
"scripts": {

View File

@@ -1,5 +1,8 @@
/* eslint disable-next-line: 0 */
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
/* eslint camelcase: 0 */
/* eslint react/no-unused-prop-types: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
@@ -43,7 +46,7 @@ export default (
};
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.cellEdit && isRemoteCellEdit()) {
if (nextProps.cellEdit.options.errorMessage) {
this.setState(() => ({

View File

@@ -2,6 +2,7 @@
/* eslint no-return-assign: 0 */
/* eslint class-methods-use-this: 0 */
/* eslint jsx-a11y/no-noninteractive-element-interactions: 0 */
/* eslint camelcase: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
@@ -51,7 +52,11 @@ export default (_, onStartEdit) =>
};
}
componentWillReceiveProps({ message }) {
componentWillUnmount() {
this.clearTimer();
}
UNSAFE_componentWillReceiveProps({ message }) {
if (_.isDefined(message)) {
this.createTimer();
this.setState(() => ({
@@ -60,10 +65,6 @@ export default (_, onStartEdit) =>
}
}
componentWillUnmount() {
this.clearTimer();
}
clearTimer() {
if (this.indicatorTimer) {
clearTimeout(this.indicatorTimer);

View File

@@ -117,7 +117,7 @@ describe('CellEditContext', () => {
wrapper = shallow(shallowContext());
wrapper.setState(initialState);
wrapper.render();
wrapper.instance().componentWillReceiveProps({});
wrapper.instance().UNSAFE_componentWillReceiveProps({});
});
it('should not set state.message', () => {
@@ -138,7 +138,7 @@ describe('CellEditContext', () => {
wrapper = shallow(shallowContext());
wrapper.setState(initialState);
wrapper.render();
wrapper.instance().componentWillReceiveProps({
wrapper.instance().UNSAFE_componentWillReceiveProps({
cellEdit: cellEditFactory(defaultCellEdit)
});
});
@@ -164,7 +164,7 @@ describe('CellEditContext', () => {
wrapper = shallow(shallowContext(defaultCellEdit, true));
wrapper.setState(initialState);
wrapper.render();
wrapper.instance().componentWillReceiveProps({
wrapper.instance().UNSAFE_componentWillReceiveProps({
cellEdit: cellEditFactory({
...defaultCellEdit,
errorMessage: message
@@ -190,7 +190,7 @@ describe('CellEditContext', () => {
beforeEach(() => {
wrapper = shallow(shallowContext(defaultCellEdit, true));
wrapper.setState(initialState);
wrapper.instance().componentWillReceiveProps({
wrapper.instance().UNSAFE_componentWillReceiveProps({
cellEdit: cellEditFactory({ ...defaultCellEdit })
});
wrapper.update();

View File

@@ -12,7 +12,7 @@ const columns = [{
dataField: 'id',
text: 'Product ID',
footerEvents: {
onClick: () => alert('Click on Product ID footer column')
onClick: (e, column, columnIndex) => alert(`Click on Product ID header column, columnIndex: ${columnIndex}`)
},
footer: 'Footer 1'
}, {
@@ -32,7 +32,7 @@ const columns = [{
dataField: 'id',
text: 'Product ID',
footerEvents: {
onClick: () => alert('Click on Product ID footer column')
onClick: (e, column, columnIndex) => alert('Click on Product ID footer column')
},
footer: 'Footer 1'
}, {

View File

@@ -12,7 +12,7 @@ const columns = [{
dataField: 'id',
text: 'Product ID',
headerEvents: {
onClick: () => alert('Click on Product ID header column')
onClick: (e, column, columnIndex) => alert(`Click on Product ID header column, columnIndex: ${columnIndex}`)
}
}, {
dataField: 'name',
@@ -29,7 +29,7 @@ const columns = [{
dataField: 'id',
text: 'Product ID',
headerEvents: {
onClick: () => alert('Click on Product ID header column')
onClick: (e, column, columnIndex) => alert('Click on Product ID header column')
}
}, {
dataField: 'name',

View File

@@ -0,0 +1,116 @@
/* eslint react/prop-types: 0 */
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { SearchBar } = Search;
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';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
const { SearchBar } = Search;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
// Implement startWith instead of contain
function customMatchFunc({
searchText,
value,
column,
row
}) {
if (typeof value !== 'undefined') {
return value.startsWith(searchText);
}
return false;
}
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ { customMatchFunc } }
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);
`;
// Implement startWith instead of contain
function customMatchFunc({
searchText,
value,
column,
row
}) {
if (typeof value !== 'undefined') {
return `${value}`.toLowerCase().startsWith(searchText.toLowerCase());
}
return false;
}
export default () => (
<div>
<h1>Custom a search match function by startWith instead of contain</h1>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ { onColumnMatch: customMatchFunc } }
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,78 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { jobsGenerator1 } from 'utils/common';
const jobs = jobsGenerator1(8);
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
sort: true,
formatter: (cell, row) => types[cell],
sortValue: (cell, row) => types[cell]
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const types = ['Cloud Service', 'Message Service', 'Add Service', 'Edit Service', 'Money'];
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
sort: true,
formatter: (cell, row) => types[cell],
sortValue: (cell, row) => types[cell]
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = { data: jobs };
}
handleClick = () => {
this.setState(() => {
const newProducts = jobsGenerator1(21);
return {
data: newProducts
};
});
}
render() {
return (
<div>
<button className="btn btn-default" onClick={ this.handleClick }>Change Data</button>
<BootstrapTable keyField="id" data={ this.state.data } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

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

View File

@@ -29,6 +29,11 @@ export const withOnSale = rows => rows.map((row) => {
return row;
});
export const withRandomPrice = rows => rows.map((row) => {
row.price = Math.floor((Math.random() * 10) + 2000);
return row;
});
export const productsQualityGenerator = (quantity = 5, factor = 0) =>
Array.from({ length: quantity }, (value, index) => ({
id: index + factor,

View File

@@ -104,6 +104,7 @@ import EnableSortTable from 'examples/sort/enable-sort-table';
import DefaultSortTable from 'examples/sort/default-sort-table';
import DefaultSortDirectionTable from 'examples/sort/default-sort-direction';
import SortEvents from 'examples/sort/sort-events';
import CustomSortValue from 'examples/sort/custom-sort-value';
import CustomSortTable from 'examples/sort/custom-sort-table';
import CustomSortCaretTable from 'examples/sort/custom-sort-caret';
import HeaderSortingClassesTable from 'examples/sort/header-sorting-classes';
@@ -195,6 +196,7 @@ import FullyCustomSearch from 'examples/search/fully-custom-search';
import SearchFormattedData from 'examples/search/search-formatted';
import CustomSearchValue from 'examples/search/custom-search-value';
import SearchableColumn from 'examples/search/searchable-column';
import CustomMatchFunction from 'examples/search/custom-match-function';
// CSV
import ExportCSV from 'examples/csv';
@@ -356,6 +358,7 @@ storiesOf('Sort Table', module)
.add('Default Sort Table', () => <DefaultSortTable />)
.add('Default Sort Direction Table', () => <DefaultSortDirectionTable />)
.add('Sort Events', () => <SortEvents />)
.add('Custom Sort Value', () => <CustomSortValue />)
.add('Custom Sort Fuction', () => <CustomSortTable />)
.add('Custom Sort Caret', () => <CustomSortCaretTable />)
.add('Custom Classes on Sorting Header Column', () => <HeaderSortingClassesTable />)
@@ -451,7 +454,8 @@ storiesOf('Table Search', module)
.add('Searchable Column', () => <SearchableColumn />)
.add('Fully Custom Search', () => <FullyCustomSearch />)
.add('Search Formatted Value', () => <SearchFormattedData />)
.add('Custom Search Value', () => <CustomSearchValue />);
.add('Custom Search Value', () => <CustomSearchValue />)
.add('Custom match function', () => <CustomMatchFunction />);
storiesOf('Column Toggle', module)
.addDecorator(bootstrapStyle())

View File

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

View File

@@ -1,6 +1,7 @@
/* eslint react/require-default-props: 0 */
/* eslint react/prop-types: 0 */
/* eslint no-return-assign: 0 */
/* eslint camelcase: 0 */
import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
@@ -41,16 +42,16 @@ class TextFilter extends Component {
}
}
componentWillReceiveProps(nextProps) {
componentWillUnmount() {
this.cleanTimer();
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.defaultValue !== this.props.defaultValue) {
this.applyFilter(nextProps.defaultValue);
}
}
componentWillUnmount() {
this.cleanTimer();
}
filter(e) {
e.stopPropagation();
this.cleanTimer();

View File

@@ -1,5 +1,6 @@
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
/* eslint camelcase: 0 */
import React from 'react';
import PropTypes from 'prop-types';
@@ -37,15 +38,6 @@ export default (
}
}
componentWillReceiveProps(nextProps) {
// let nextData = nextProps.data;
if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.data)) {
this.doFilter(nextProps, this.isEmitDataChange);
} else {
this.data = nextProps.data;
}
}
onFilter(column, filterType, initialize = false) {
return (filterVal) => {
// watch out here if migration to context API, #334
@@ -90,6 +82,15 @@ export default (
return this.data;
}
UNSAFE_componentWillReceiveProps(nextProps) {
// let nextData = nextProps.data;
if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.data)) {
this.doFilter(nextProps, this.isEmitDataChange);
} else {
this.data = nextProps.data;
}
}
doFilter(props, ignoreEmitDataChange = false) {
const { dataChangeListener, data, columns } = props;
const result = filters(data, columns, _)(this.currFilters);

View File

@@ -144,7 +144,7 @@ describe('Text Filter', () => {
<TextFilter onFilter={ onFilter } column={ column } />
);
instance = wrapper.instance();
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
});
it('should setting state correctly when props.defaultValue is changed', () => {

View File

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

View File

@@ -21,8 +21,9 @@ class PaginationDataProvider extends Provider {
isRemotePagination: PropTypes.func.isRequired
}
componentWillReceiveProps(nextProps) {
super.componentWillReceiveProps(nextProps);
// eslint-disable-next-line camelcase, react/sort-comp
UNSAFE_componentWillReceiveProps(nextProps) {
super.UNSAFE_componentWillReceiveProps(nextProps);
const { currSizePerPage } = this;
const { custom, onPageChange } = nextProps.pagination.options;

View File

@@ -1,4 +1,5 @@
/* eslint react/prop-types: 0 */
/* eslint camelcase: 0 */
import React, { Component } from 'react';
import pageResolver from './page-resolver';
@@ -12,7 +13,7 @@ export default WrappedComponent =>
this.state = this.initialState();
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
const { dataSize, currSizePerPage } = nextProps;
if (currSizePerPage !== this.props.currSizePerPage || dataSize !== this.props.dataSize) {
const totalPages = this.calculateTotalPage(currSizePerPage, dataSize);

View File

@@ -1,6 +1,7 @@
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
/* eslint no-lonely-if: 0 */
/* eslint camelcase: 0 */
import React from 'react';
import EventEmitter from 'events';
import Const from './const';
@@ -45,23 +46,6 @@ class StateProvider extends React.Component {
this.dataChangeListener.on('filterChanged', this.handleDataSizeChange);
}
componentWillReceiveProps(nextProps) {
const { custom } = nextProps.pagination.options;
// user should align the page when the page is not fit to the data size when remote enable
if (this.isRemotePagination() || custom) {
if (typeof nextProps.pagination.options.page !== 'undefined') {
this.currPage = nextProps.pagination.options.page;
}
if (typeof nextProps.pagination.options.sizePerPage !== 'undefined') {
this.currSizePerPage = nextProps.pagination.options.sizePerPage;
}
if (typeof nextProps.pagination.options.totalSize !== 'undefined') {
this.dataSize = nextProps.pagination.options.totalSize;
}
}
}
getPaginationProps = () => {
const { pagination: { options }, bootstrap4 } = this.props;
const { currPage, currSizePerPage, dataSize } = this;
@@ -113,6 +97,23 @@ class StateProvider extends React.Component {
getPaginationRemoteEmitter = () => this.remoteEmitter || this.props.remoteEmitter;
UNSAFE_componentWillReceiveProps(nextProps) {
const { custom } = nextProps.pagination.options;
// user should align the page when the page is not fit to the data size when remote enable
if (this.isRemotePagination() || custom) {
if (typeof nextProps.pagination.options.page !== 'undefined') {
this.currPage = nextProps.pagination.options.page;
}
if (typeof nextProps.pagination.options.sizePerPage !== 'undefined') {
this.currSizePerPage = nextProps.pagination.options.sizePerPage;
}
if (typeof nextProps.pagination.options.totalSize !== 'undefined') {
this.dataSize = nextProps.pagination.options.totalSize;
}
}
}
isRemotePagination = () => {
const e = {};
this.remoteEmitter.emit('isRemotePagination', e);

View File

@@ -174,7 +174,7 @@ describe('PaginationDataContext', () => {
data: [],
pagination: { ...defaultPagination }
};
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
});
it('should reset currPage to first page', () => {
@@ -195,7 +195,7 @@ describe('PaginationDataContext', () => {
data: [],
pagination: { ...defaultPagination, options: { onPageChange } }
};
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
});
it('should call options.onPageChange correctly', () => {

View File

@@ -164,13 +164,13 @@ describe('paginationHandler', () => {
});
it('should setting correct state.totalPages', () => {
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
expect(instance.state.totalPages).toEqual(
instance.calculateTotalPage(nextProps.currSizePerPage));
});
it('should setting correct state.lastPage', () => {
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
const totalPages = instance.calculateTotalPage(nextProps.currSizePerPage);
expect(instance.state.lastPage).toEqual(
instance.calculateLastPage(totalPages));
@@ -186,13 +186,13 @@ describe('paginationHandler', () => {
});
it('should setting correct state.totalPages', () => {
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
expect(instance.state.totalPages).toEqual(
instance.calculateTotalPage(nextProps.currSizePerPage, nextProps.dataSize));
});
it('should setting correct state.lastPage', () => {
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
const totalPages = instance.calculateTotalPage(
nextProps.currSizePerPage, nextProps.dataSize);
expect(instance.state.lastPage).toEqual(

View File

@@ -156,7 +156,7 @@ describe('PaginationStateContext', () => {
data,
pagination: { ...defaultPagination, options: { page: 3, sizePerPage: 5, totalSize: 50 } }
};
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
});
it('should always reset currPage and currSizePerPage', () => {
@@ -181,7 +181,7 @@ describe('PaginationStateContext', () => {
options: { page: 3, sizePerPage: 5, custom: true, totalSize: 50 }
}
};
instance.componentWillReceiveProps(nextProps);
instance.UNSAFE_componentWillReceiveProps(nextProps);
});
it('should always reset currPage and currSizePerPage', () => {

View File

@@ -98,6 +98,33 @@ Accept a string that will be used for default searching when first time table re
</ToolkitProvider>
```
#### onColumnMatch - [function]
Acccpt a function which will be called when table try to match every cells when search happening. This function accept an object like below example:
```js
function onColumnMatch({
searchText,
value,
column,
row
}) {
// implement your custom match logic on every cell value
}
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ {
onColumnMatch
} }
>
// ...
</ToolkitProvider>
```
> Notes: You have to return `true` when your match logic is positive and vice versa.
#### searchFormatted - [bool]
If you want to search on the formatted data, you are supposed to enable this props. `react-bootstrap-table2` will check if you define the `column.formatter` when doing search.

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-toolkit",
"version": "2.0.1",
"version": "2.1.0",
"description": "The toolkit for react-bootstrap-table2",
"main": "./lib/index.js",
"repository": {

View File

@@ -1,3 +1,4 @@
/* eslint camelcase: 0 */
/* eslint no-return-assign: 0 */
import React from 'react';
import PropTypes from 'prop-types';
@@ -34,10 +35,6 @@ class SearchBar extends React.Component {
};
}
componentWillReceiveProps(nextProps) {
this.setState({ value: nextProps.searchText });
}
onChangeValue = (e) => {
this.setState({ value: e.target.value });
}
@@ -50,6 +47,10 @@ class SearchBar extends React.Component {
debounceCallback();
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({ value: nextProps.searchText });
}
render() {
const {
className,

View File

@@ -3,11 +3,13 @@
/* eslint no-continue: 0 */
/* eslint no-lonely-if: 0 */
/* eslint class-methods-use-this: 0 */
/* eslint camelcase: 0 */
import React from 'react';
import PropTypes from 'prop-types';
export default (options = {
searchFormatted: false
searchFormatted: false,
onColumnMatch: null
}) => (
_,
isRemoteSearch,
@@ -35,7 +37,17 @@ export default (options = {
this.state = { data: initialData };
}
componentWillReceiveProps(nextProps) {
getSearched() {
return this.state.data;
}
triggerListener(result) {
if (this.props.dataChangeListener) {
this.props.dataChangeListener.emit('filterChanged', result.length);
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.searchText !== this.props.searchText) {
if (isRemoteSearch()) {
handleRemoteSearchChange(nextProps.searchText);
@@ -59,16 +71,6 @@ export default (options = {
}
}
getSearched() {
return this.state.data;
}
triggerListener(result) {
if (this.props.dataChangeListener) {
this.props.dataChangeListener.emit('filterChanged', result.length);
}
}
search(props) {
const { data, columns } = props;
const searchText = props.searchText.toLowerCase();
@@ -82,11 +84,22 @@ export default (options = {
} else if (column.filterValue) {
targetValue = column.filterValue(targetValue, row);
}
if (targetValue !== null && typeof targetValue !== 'undefined') {
targetValue = targetValue.toString().toLowerCase();
if (targetValue.indexOf(searchText) > -1) {
if (options.onColumnMatch) {
if (options.onColumnMatch({
searchText,
value: targetValue,
column,
row
})) {
return true;
}
} else {
if (targetValue !== null && typeof targetValue !== 'undefined') {
targetValue = targetValue.toString().toLowerCase();
if (targetValue.indexOf(searchText) > -1) {
return true;
}
}
}
}
return false;

View File

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

View File

@@ -1,3 +1,4 @@
/* eslint camelcase: 0 */
/* eslint arrow-body-style: 0 */
import React, { Component } from 'react';
@@ -18,7 +19,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
this.validateProps();
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.onDataSizeChange && !nextProps.pagination) {
if (nextProps.data.length !== this.props.data.length) {
nextProps.onDataSizeChange({ dataSize: nextProps.data.length });

View File

@@ -17,8 +17,8 @@ export default ExtendBase =>
createDefaultEventHandler(cb) {
return (e) => {
const { column, columnIndex } = this.props;
cb(e, column, columnIndex);
const { column, columnIndex, index } = this.props;
cb(e, column, typeof columnIndex !== 'undefined' ? columnIndex : index);
};
}

View File

@@ -1,3 +1,4 @@
/* eslint camelcase: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
@@ -12,10 +13,6 @@ export default () => {
state = { data: this.props.data };
componentWillReceiveProps(nextProps) {
this.setState(() => ({ data: nextProps.data }));
}
getData = (filterProps, searchProps, sortProps, paginationProps) => {
if (paginationProps) return paginationProps.data;
else if (sortProps) return sortProps.data;
@@ -24,6 +21,10 @@ export default () => {
return this.props.data;
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState(() => ({ data: nextProps.data }));
}
render() {
return (
<DataContext.Provider

View File

@@ -1,3 +1,4 @@
/* eslint camelcase: 0 */
/* eslint no-return-assign: 0 */
/* eslint no-param-reassign: 0 */
/* eslint class-methods-use-this: 0 */
@@ -83,7 +84,7 @@ const withContext = Base =>
}
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (!nextProps.pagination && this.props.pagination) {
this.PaginationContext = null;
}

View File

@@ -1,3 +1,4 @@
/* eslint camelcase: 0 */
/* eslint react/prop-types: 0 */
import React from 'react';
import PropTypes from 'prop-types';
@@ -16,7 +17,11 @@ class RowExpandProvider extends React.Component {
state = { expanded: this.props.expandRow.expanded || [],
isClosing: this.props.expandRow.isClosing || [] };
componentWillReceiveProps(nextProps) {
onClosed = (closedRow) => {
this.setState({ isClosing: this.state.isClosing.filter(value => value !== closedRow) });
};
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.expandRow) {
const nextExpanded = nextProps.expandRow.expanded || this.state.expanded;
const isClosing = this.state.expanded.reduce((acc, cur) => {
@@ -36,10 +41,6 @@ class RowExpandProvider extends React.Component {
}
}
onClosed = (closedRow) => {
this.setState({ isClosing: this.state.isClosing.filter(value => value !== closedRow) });
};
handleRowExpand = (rowKey, expanded, rowIndex, e) => {
const { data, keyField, expandRow: { onExpand, onlyOneExpanding, nonExpandable } } = this.props;
if (nonExpandable && _.contains(nonExpandable, rowKey)) {

View File

@@ -1,3 +1,4 @@
/* eslint camelcase: 0 */
/* eslint react/prop-types: 0 */
import React from 'react';
import PropTypes from 'prop-types';
@@ -19,17 +20,17 @@ class SelectionProvider extends React.Component {
this.selected = props.selectRow.selected || [];
}
componentWillReceiveProps(nextProps) {
if (nextProps.selectRow) {
this.selected = nextProps.selectRow.selected || this.selected;
}
}
// exposed API
getSelected() {
return this.selected;
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.selectRow) {
this.selected = nextProps.selectRow.selected || this.selected;
}
}
handleRowSelect = (rowKey, checked, rowIndex, e) => {
const { data, keyField, selectRow: { mode, onSelect } } = this.props;
const { ROW_SELECT_SINGLE } = Const;

View File

@@ -4,56 +4,61 @@ import cs from 'classnames';
import PropTypes from 'prop-types';
import _ from './utils';
import eventDelegater from './cell-event-delegater';
const FooterCell = (props) => {
const { index, column, columnData } = props;
class FooterCell extends eventDelegater(React.Component) {
render() {
const { index, column, columnData } = this.props;
const {
footer,
footerTitle,
footerAlign,
footerFormatter,
footerEvents,
footerClasses,
footerStyle,
footerAttrs
} = column;
const {
footer,
footerTitle,
footerAlign,
footerFormatter,
footerEvents,
footerClasses,
footerStyle,
footerAttrs
} = column;
const cellAttrs = {
...(_.isFunction(footerAttrs) ? footerAttrs(column, index) : footerAttrs),
...footerEvents
};
const delegateEvents = this.delegate(footerEvents);
const cellAttrs = {
...(_.isFunction(footerAttrs) ? footerAttrs(column, index) : footerAttrs),
...delegateEvents
};
let text = '';
if (_.isString(footer)) {
text = footer;
} else if (_.isFunction(footer)) {
text = footer(columnData, column, index);
let text = '';
if (_.isString(footer)) {
text = footer;
} else if (_.isFunction(footer)) {
text = footer(columnData, column, index);
}
let cellStyle = {};
const cellClasses = _.isFunction(footerClasses) ? footerClasses(column, index) : footerClasses;
if (footerStyle) {
cellStyle = _.isFunction(footerStyle) ? footerStyle(column, index) : footerStyle;
cellStyle = cellStyle ? { ...cellStyle } : cellStyle;
}
if (footerTitle) {
cellAttrs.title = _.isFunction(footerTitle) ? footerTitle(column, index) : text;
}
if (footerAlign) {
cellStyle.textAlign = _.isFunction(footerAlign) ? footerAlign(column, index) : footerAlign;
}
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
const children = footerFormatter ? footerFormatter(column, index) : text;
return React.createElement('th', cellAttrs, children);
}
let cellStyle = {};
const cellClasses = _.isFunction(footerClasses) ? footerClasses(column, index) : footerClasses;
if (footerStyle) {
cellStyle = _.isFunction(footerStyle) ? footerStyle(column, index) : footerStyle;
cellStyle = cellStyle ? { ...cellStyle } : cellStyle;
}
if (footerTitle) {
cellAttrs.title = _.isFunction(footerTitle) ? footerTitle(column, index) : text;
}
if (footerAlign) {
cellStyle.textAlign = _.isFunction(footerAlign) ? footerAlign(column, index) : footerAlign;
}
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
const children = footerFormatter ? footerFormatter(column, index) : text;
return React.createElement('th', cellAttrs, children);
};
}
FooterCell.propTypes = {
columnData: PropTypes.array,

View File

@@ -7,119 +7,124 @@ import Const from './const';
import SortSymbol from './sort/symbol';
import SortCaret from './sort/caret';
import _ from './utils';
import eventDelegater from './cell-event-delegater';
const HeaderCell = (props) => {
const {
column,
index,
onSort,
sorting,
sortOrder,
isLastSorting,
onFilter,
currFilters,
onExternalFilter
} = props;
class HeaderCell extends eventDelegater(React.Component) {
render() {
const {
column,
index,
onSort,
sorting,
sortOrder,
isLastSorting,
onFilter,
currFilters,
onExternalFilter
} = this.props;
const {
text,
sort,
sortCaret,
filter,
filterRenderer,
headerTitle,
headerAlign,
headerFormatter,
headerEvents,
headerClasses,
headerStyle,
headerAttrs,
headerSortingClasses,
headerSortingStyle
} = column;
const {
text,
sort,
sortCaret,
filter,
filterRenderer,
headerTitle,
headerAlign,
headerFormatter,
headerEvents,
headerClasses,
headerStyle,
headerAttrs,
headerSortingClasses,
headerSortingStyle
} = column;
const cellAttrs = {
..._.isFunction(headerAttrs) ? headerAttrs(column, index) : headerAttrs,
...headerEvents,
tabIndex: 0
};
const delegateEvents = this.delegate(headerEvents);
let sortSymbol;
let filterElm;
let cellStyle = {};
let cellClasses = _.isFunction(headerClasses) ? headerClasses(column, index) : headerClasses;
if (headerStyle) {
cellStyle = _.isFunction(headerStyle) ? headerStyle(column, index) : headerStyle;
cellStyle = cellStyle ? { ...cellStyle } : cellStyle;
}
if (headerTitle) {
cellAttrs.title = _.isFunction(headerTitle) ? headerTitle(column, index) : text;
}
if (headerAlign) {
cellStyle.textAlign = _.isFunction(headerAlign) ? headerAlign(column, index) : headerAlign;
}
if (sort) {
const customClick = cellAttrs.onClick;
cellAttrs.onClick = (e) => {
onSort(column);
if (_.isFunction(customClick)) customClick(e);
const cellAttrs = {
..._.isFunction(headerAttrs) ? headerAttrs(column, index) : headerAttrs,
...delegateEvents,
tabIndex: 0
};
cellAttrs.className = cs(cellAttrs.className, 'sortable');
if (sorting) {
sortSymbol = sortCaret ? sortCaret(sortOrder, column) : <SortCaret order={ sortOrder } />;
let sortSymbol;
let filterElm;
let cellStyle = {};
let cellClasses = _.isFunction(headerClasses) ? headerClasses(column, index) : headerClasses;
// append customized classes or style if table was sorting based on the current column.
cellClasses = cs(
cellClasses,
_.isFunction(headerSortingClasses)
? headerSortingClasses(column, sortOrder, isLastSorting, index)
: headerSortingClasses
);
cellStyle = {
...cellStyle,
..._.isFunction(headerSortingStyle)
? headerSortingStyle(column, sortOrder, isLastSorting, index)
: headerSortingStyle
};
} else {
sortSymbol = sortCaret ? sortCaret(undefined, column) : <SortSymbol />;
if (headerStyle) {
cellStyle = _.isFunction(headerStyle) ? headerStyle(column, index) : headerStyle;
cellStyle = cellStyle ? { ...cellStyle } : cellStyle;
}
if (headerTitle) {
cellAttrs.title = _.isFunction(headerTitle) ? headerTitle(column, index) : text;
}
if (headerAlign) {
cellStyle.textAlign = _.isFunction(headerAlign) ? headerAlign(column, index) : headerAlign;
}
if (sort) {
const customClick = cellAttrs.onClick;
cellAttrs.onClick = (e) => {
onSort(column);
if (_.isFunction(customClick)) customClick(e);
};
cellAttrs.className = cs(cellAttrs.className, 'sortable');
if (sorting) {
sortSymbol = sortCaret ? sortCaret(sortOrder, column) : <SortCaret order={ sortOrder } />;
// append customized classes or style if table was sorting based on the current column.
cellClasses = cs(
cellClasses,
_.isFunction(headerSortingClasses)
? headerSortingClasses(column, sortOrder, isLastSorting, index)
: headerSortingClasses
);
cellStyle = {
...cellStyle,
..._.isFunction(headerSortingStyle)
? headerSortingStyle(column, sortOrder, isLastSorting, index)
: headerSortingStyle
};
} else {
sortSymbol = sortCaret ? sortCaret(undefined, column) : <SortSymbol />;
}
}
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
if (filterRenderer) {
const onCustomFilter = onExternalFilter(column, filter.props.type);
filterElm = filterRenderer(onCustomFilter, column);
} else if (filter) {
filterElm = (
<filter.Filter
{ ...filter.props }
filterState={ currFilters[column.dataField] }
onFilter={ onFilter }
column={ column }
/>
);
}
const children = headerFormatter ?
headerFormatter(column, index, { sortElement: sortSymbol, filterElement: filterElm }) :
text;
if (headerFormatter) {
return React.createElement('th', cellAttrs, children);
}
return React.createElement('th', cellAttrs, children, sortSymbol, filterElm);
}
if (cellClasses) cellAttrs.className = cs(cellAttrs.className, cellClasses);
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
if (filterRenderer) {
const onCustomFilter = onExternalFilter(column, filter.props.type);
filterElm = filterRenderer(onCustomFilter, column);
} else if (filter) {
filterElm = (
<filter.Filter
{ ...filter.props }
filterState={ currFilters[column.dataField] }
onFilter={ onFilter }
column={ column }
/>
);
}
const children = headerFormatter ?
headerFormatter(column, index, { sortElement: sortSymbol, filterElement: filterElm }) :
text;
if (headerFormatter) {
return React.createElement('th', cellAttrs, children);
}
return React.createElement('th', cellAttrs, children, sortSymbol, filterElm);
};
}
HeaderCell.propTypes = {
column: PropTypes.shape({

View File

@@ -14,14 +14,19 @@ function comparator(a, b) {
return result;
}
export const sort = (data, sortOrder, { dataField, sortFunc }) => {
export const sort = (data, sortOrder, { dataField, sortFunc, sortValue }) => {
const _data = [...data];
_data.sort((a, b) => {
let result;
let valueA = _.get(a, dataField);
let valueB = _.get(b, dataField);
valueA = _.isDefined(valueA) ? valueA : '';
valueB = _.isDefined(valueB) ? valueB : '';
if (sortValue) {
valueA = sortValue(valueA, a);
valueB = sortValue(valueB, b);
} else {
valueA = _.isDefined(valueA) ? valueA : '';
valueB = _.isDefined(valueB) ? valueB : '';
}
if (sortFunc) {
result = sortFunc(valueA, valueB, sortOrder, dataField, a, b);

View File

@@ -81,7 +81,7 @@ describe('DataContext', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.instance().componentWillReceiveProps({
wrapper.instance().UNSAFE_componentWillReceiveProps({
data: newData
});
});

View File

@@ -99,7 +99,7 @@ describe('DataContext', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.instance().componentWillReceiveProps({
wrapper.instance().UNSAFE_componentWillReceiveProps({
selectRow: newSelectRow
});
});
@@ -115,7 +115,7 @@ describe('DataContext', () => {
...defaultSelectRow,
selected: defaultSelected
}));
wrapper.instance().componentWillReceiveProps({
wrapper.instance().UNSAFE_componentWillReceiveProps({
selectRow: defaultSelectRow
});
});
@@ -128,7 +128,7 @@ describe('DataContext', () => {
describe('if nextProps.selectRow is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
wrapper.instance().componentWillReceiveProps({});
wrapper.instance().UNSAFE_componentWillReceiveProps({});
});
it('should not set this.selected', () => {