diff --git a/docs/README.md b/docs/README.md
index 3fc1c58..dc12d3a 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -33,6 +33,7 @@
* [pagination](#pagination)
* [filter](#filter)
* [onTableChange](#onTableChange)
+* [onDataSizeChange](#onDataSizeChange)
### keyField(**required**) - [String]
Tells `react-bootstrap-table2` which column is unique.
@@ -317,4 +318,20 @@ Following is a shape of `newState`
newValue
}
}
+```
+
+### onDataSizeChange - [Function]
+This callback function will be called only when data size change by search/filter etc. This function have one argument which is an object contains below props:
+
+* `dataSize`: The new data size
+
+```js
+handleDataChange = ({ dataSize }) => {
+ this.setState({ rowCount: dataSize });
+}
+
+
```
\ No newline at end of file
diff --git a/packages/react-bootstrap-table2-example/.storybook/webpack.config.js b/packages/react-bootstrap-table2-example/.storybook/webpack.config.js
index a3995c8..75e6df6 100644
--- a/packages/react-bootstrap-table2-example/.storybook/webpack.config.js
+++ b/packages/react-bootstrap-table2-example/.storybook/webpack.config.js
@@ -9,6 +9,7 @@ const sourceStylePath = path.join(__dirname, '../../react-bootstrap-table2/style
const paginationStylePath = path.join(__dirname, '../../react-bootstrap-table2-paginator/style');
const filterStylePath = path.join(__dirname, '../../react-bootstrap-table2-filter/style');
const toolkitSourcePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/index.js');
+const toolkitStylePath = path.join(__dirname, '../../react-bootstrap-table2-toolkit/style');
const storyPath = path.join(__dirname, '../stories');
const examplesPath = path.join(__dirname, '../examples');
const srcPath = path.join(__dirname, '../src');
@@ -43,7 +44,13 @@ const loaders = [{
}, {
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
- include: [storyPath, sourceStylePath, paginationStylePath, filterStylePath],
+ include: [
+ storyPath,
+ sourceStylePath,
+ paginationStylePath,
+ filterStylePath,
+ toolkitStylePath
+ ],
}, {
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000',
diff --git a/packages/react-bootstrap-table2-example/examples/data/data-change-listener.js b/packages/react-bootstrap-table2-example/examples/data/data-change-listener.js
new file mode 100644
index 0000000..3b9f737
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/data/data-change-listener.js
@@ -0,0 +1,151 @@
+/* eslint react/no-multi-comp: 0 */
+import React from 'react';
+import BootstrapTable from 'react-bootstrap-table-next';
+import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
+import paginationFactory from 'react-bootstrap-table2-paginator';
+import Code from 'components/common/code-block';
+import { productsGenerator } from 'utils/common';
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name',
+ filter: textFilter()
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ filter: textFilter()
+}];
+
+const sourceCode1 = `\
+import BootstrapTable from 'react-bootstrap-table-next';
+import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
+
+class Case1 extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { rowCount: products.length };
+ }
+
+ handleDataChange = ({ dataSize }) => {
+ this.setState({ rowCount: dataSize });
+ }
+
+ render() {
+ return (
+
+
Row Count:{ this.state.rowCount }
+
+ { sourceCode }
+
+ );
+ }
+`;
+
+const sourceCode2 = `\
+import BootstrapTable from 'react-bootstrap-table-next';
+import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
+import paginationFactory from 'react-bootstrap-table2-paginator';
+
+class Case2 extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { rowCount: products.length };
+ }
+
+ handleDataChange = ({ dataSize }) => {
+ this.setState({ rowCount: dataSize });
+ }
+
+ render() {
+ return (
+
+
Row Count:{ this.state.rowCount }
+
+ { sourceCode }
+
+ );
+ }
+`;
+
+const products1 = productsGenerator(8);
+class WithoutPaginationCase extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { rowCount: products1.length };
+ }
+
+ handleDataChange = ({ dataSize }) => {
+ this.setState({ rowCount: dataSize });
+ }
+
+ render() {
+ return (
+
+
Without Pagination Case
+ Row Count:{ this.state.rowCount }
+
+ { sourceCode2 }
+
+ );
+ }
+}
+
+const products2 = productsGenerator(88);
+class WithPaginationCase extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { rowCount: products2.length };
+ }
+
+ handleDataChange = ({ dataSize }) => {
+ this.setState({ rowCount: dataSize });
+ }
+
+ render() {
+ return (
+
+
Without Pagination Case
+ Row Count:{ this.state.rowCount }
+
+ { sourceCode1 }
+
+ );
+ }
+}
+
+export default () => (
+
+
+
+
+);
+
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index 9fc20d1..9bd79b8 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -217,6 +217,7 @@ import RemoteCellEdit from 'examples/remote/remote-celledit';
import RemoteAll from 'examples/remote/remote-all';
// data
+import DataChangeListener from 'examples/data/data-change-listener';
import LoadDataWithFilter from 'examples/data/load-data-on-the-fly-with-filter';
import LoadDataWithDefaultFilter from 'examples/data/load-data-on-the-fly-with-default-filter';
import LoadDataWithSearch from 'examples/data/load-data-on-the-fly-with-search';
@@ -229,6 +230,7 @@ import 'stories/stylesheet/storybook.scss';
import '../../react-bootstrap-table2/style/react-bootstrap-table2.scss';
import '../../react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss';
import '../../react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss';
+import '../../react-bootstrap-table2-toolkit/style/react-bootstrap-table2-toolkit.scss';
// import bootstrap style by given version
import bootstrapStyle, { BOOTSTRAP_VERSION } from './bootstrap-style';
@@ -466,6 +468,7 @@ storiesOf('Remote', module)
storiesOf('Data', module)
.addDecorator(bootstrapStyle())
+ .add('Data Change Listener', () => )
.add('Load data with Filter', () => )
.add('Load data with Default Filter', () => )
.add('Load data with Search', () => )
diff --git a/packages/react-bootstrap-table2-filter/src/components/date.js b/packages/react-bootstrap-table2-filter/src/components/date.js
index 45717ae..578f558 100644
--- a/packages/react-bootstrap-table2-filter/src/components/date.js
+++ b/packages/react-bootstrap-table2-filter/src/components/date.js
@@ -132,24 +132,35 @@ class DateFilter extends Component {
className={ `filter date-filter ${className}` }
style={ style }
>
-
- 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() }
- />
+ Filter comparator
+
+
+
);
}
diff --git a/packages/react-bootstrap-table2-filter/src/components/multiselect.js b/packages/react-bootstrap-table2-filter/src/components/multiselect.js
index 5f4623b..2de86f9 100644
--- a/packages/react-bootstrap-table2-filter/src/components/multiselect.js
+++ b/packages/react-bootstrap-table2-filter/src/components/multiselect.js
@@ -111,18 +111,25 @@ class MultiSelectFilter extends Component {
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return (
-
+ Filter by {column.text}
+
+
);
}
}
diff --git a/packages/react-bootstrap-table2-filter/src/components/number.js b/packages/react-bootstrap-table2-filter/src/components/number.js
index 447eed2..29e0370 100644
--- a/packages/react-bootstrap-table2-filter/src/components/number.js
+++ b/packages/react-bootstrap-table2-filter/src/components/number.js
@@ -173,35 +173,53 @@ class NumberFilter extends Component {
className={ `filter number-filter ${className}` }
style={ style }
>
-
+ Filter comparator
+
+
{
options ?
- :
- this.numberFilter = n }
- type="number"
- style={ numberStyle }
- className={ `number-filter-input form-control ${numberClassName}` }
- placeholder={ placeholder || `Enter ${column.text}...` }
- onChange={ this.onChangeNumber }
- defaultValue={ defaultValue ? defaultValue.number : '' }
- />
+ {`Select ${column.text}`}
+
+ :
+
}
);
diff --git a/packages/react-bootstrap-table2-filter/src/components/select.js b/packages/react-bootstrap-table2-filter/src/components/select.js
index 973bdb3..db5348a 100644
--- a/packages/react-bootstrap-table2-filter/src/components/select.js
+++ b/packages/react-bootstrap-table2-filter/src/components/select.js
@@ -8,15 +8,18 @@ import { FILTER_TYPE } from '../const';
function optionsEquals(currOpts, prevOpts) {
if (Array.isArray(currOpts)) {
- for (let i = 0; i < currOpts.length; i += 1) {
- if (
- currOpts[i].value !== prevOpts[i].value ||
- currOpts[i].label !== prevOpts[i].label
- ) {
- return false;
+ if (currOpts.length === prevOpts.length) {
+ for (let i = 0; i < currOpts.length; i += 1) {
+ if (
+ currOpts[i].value !== prevOpts[i].value ||
+ currOpts[i].label !== prevOpts[i].label
+ ) {
+ return false;
+ }
}
+ return true;
}
- return currOpts.length === prevOpts.length;
+ return false;
}
const keys = Object.keys(currOpts);
for (let i = 0; i < keys.length; i += 1) {
@@ -136,17 +139,24 @@ class SelectFilter extends Component {
`filter select-filter form-control ${className} ${this.state.isSelected ? '' : 'placeholder-selected'}`;
return (
-
+ Filter by { column.text }
+
+
);
}
}
diff --git a/packages/react-bootstrap-table2-filter/src/components/text.js b/packages/react-bootstrap-table2-filter/src/components/text.js
index a17c3ea..1995ebe 100644
--- a/packages/react-bootstrap-table2-filter/src/components/text.js
+++ b/packages/react-bootstrap-table2-filter/src/components/text.js
@@ -94,17 +94,24 @@ class TextFilter extends Component {
// stopPropagation for onClick event is try to prevent sort was triggered.
return (
- this.input = n }
- type="text"
- className={ `filter text-filter form-control ${className}` }
- style={ style }
- onChange={ this.filter }
- onClick={ this.handleClick }
- placeholder={ placeholder || `Enter ${text}...` }
- value={ this.state.value }
- />
+
);
}
}
diff --git a/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss b/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss
index e2304b6..445579e 100644
--- a/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss
+++ b/packages/react-bootstrap-table2-filter/style/react-bootstrap-table2-filter.scss
@@ -1,3 +1,7 @@
+.react-bootstrap-table > table > thead > tr > th .filter-label {
+ display: block !important;
+}
+
.react-bootstrap-table > table > thead > tr > th .filter {
font-weight: normal;
}
diff --git a/packages/react-bootstrap-table2-paginator/src/data-context.js b/packages/react-bootstrap-table2-paginator/src/data-context.js
index e33f45f..654e9f8 100644
--- a/packages/react-bootstrap-table2-paginator/src/data-context.js
+++ b/packages/react-bootstrap-table2-paginator/src/data-context.js
@@ -46,6 +46,9 @@ class PaginationDataProvider extends Provider {
this.currPage = newPage;
}
}
+ if (nextProps.onDataSizeChange && nextProps.data.length !== this.props.data.length) {
+ nextProps.onDataSizeChange({ dataSize: nextProps.data.length });
+ }
}
isRemotePagination = () => this.props.isRemotePagination();
diff --git a/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js b/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js
index f51f09c..64e148f 100644
--- a/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js
+++ b/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js
@@ -54,20 +54,28 @@ class SearchBar extends React.Component {
const {
className,
style,
- placeholder
+ placeholder,
+ tableId
} = this.props;
return (
- this.input = n }
- type="text"
- style={ style }
- onKeyUp={ () => this.onKeyup() }
- onChange={ this.onChangeValue }
- className={ `form-control ${className}` }
- value={ this.state.value }
- placeholder={ placeholder || SearchBar.defaultProps.placeholder }
- />
+
);
}
}
@@ -78,7 +86,8 @@ SearchBar.propTypes = {
placeholder: PropTypes.string,
style: PropTypes.object,
delay: PropTypes.number,
- searchText: PropTypes.string
+ searchText: PropTypes.string,
+ tableId: PropTypes.string
};
SearchBar.defaultProps = {
@@ -86,7 +95,8 @@ SearchBar.defaultProps = {
style: {},
placeholder: 'Search',
delay: 250,
- searchText: ''
+ searchText: '',
+ tableId: 0
};
export default SearchBar;
diff --git a/packages/react-bootstrap-table2-toolkit/style/react-bootstrap-table2-toolkit.scss b/packages/react-bootstrap-table2-toolkit/style/react-bootstrap-table2-toolkit.scss
index e69de29..c840023 100644
--- a/packages/react-bootstrap-table2-toolkit/style/react-bootstrap-table2-toolkit.scss
+++ b/packages/react-bootstrap-table2-toolkit/style/react-bootstrap-table2-toolkit.scss
@@ -0,0 +1,3 @@
+.search-label {
+ display: block !important;
+}
\ No newline at end of file
diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js
index 0df430c..bc0bc79 100644
--- a/packages/react-bootstrap-table2/src/bootstrap-table.js
+++ b/packages/react-bootstrap-table2/src/bootstrap-table.js
@@ -18,6 +18,14 @@ class BootstrapTable extends PropsBaseResolver(Component) {
this.validateProps();
}
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.onDataSizeChange && !nextProps.pagination) {
+ if (nextProps.data.length !== this.props.data.length) {
+ nextProps.onDataSizeChange({ dataSize: nextProps.data.length });
+ }
+ }
+ }
+
// Exposed APIs
getData = () => {
return this.visibleRows();
@@ -192,6 +200,7 @@ BootstrapTable.propTypes = {
onSort: PropTypes.func,
onFilter: PropTypes.func,
onExternalFilter: PropTypes.func,
+ onDataSizeChange: PropTypes.func,
// Inject from toolkit
search: PropTypes.shape({
searchText: PropTypes.string,
diff --git a/packages/react-bootstrap-table2/src/contexts/index.js b/packages/react-bootstrap-table2/src/contexts/index.js
index 148e805..1df8d6c 100644
--- a/packages/react-bootstrap-table2/src/contexts/index.js
+++ b/packages/react-bootstrap-table2/src/contexts/index.js
@@ -211,6 +211,7 @@ const withContext = Base =>
bootstrap4={ this.props.bootstrap4 }
isRemotePagination={ this.isRemotePagination }
remoteEmitter={ this.remoteEmitter }
+ onDataSizeChange={ this.props.onDataSizeChange }
>
{
diff --git a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js
index 601c338..0c4789a 100644
--- a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js
+++ b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js
@@ -17,13 +17,17 @@ class RowExpandProvider extends React.Component {
componentWillReceiveProps(nextProps) {
if (nextProps.expandRow) {
+ const nextExpanded = nextProps.expandRow.expanded || this.state.expanded;
const isClosing = this.state.expanded.reduce((acc, cur) => {
- if (!nextProps.expandRow.expanded.includes(cur)) {
+ if (!nextExpanded.includes(cur)) {
acc.push(cur);
}
return acc;
}, []);
- this.setState(() => ({ expanded: nextProps.expandRow.expanded, isClosing }));
+ this.setState(() => ({
+ expanded: nextExpanded,
+ isClosing
+ }));
} else {
this.setState(() => ({
expanded: this.state.expanded