diff --git a/packages/react-bootstrap-table2-example/examples/column-toggle/column-toggle-with-filter.js b/packages/react-bootstrap-table2-example/examples/column-toggle/column-toggle-with-filter.js new file mode 100644 index 0000000..e67541d --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-toggle/column-toggle-with-filter.js @@ -0,0 +1,94 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const { ToggleList } = ColumnToggle; +const products = productsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + sort: true, + filter: textFilter() +}, { + dataField: 'price', + text: 'Product Price', + sort: true, + filter: textFilter() +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; + +const { ToggleList } = ColumnToggle; +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + sort: true, + filter: textFilter() +}, { + dataField: 'price', + text: 'Product Price', + sort: true, + filter: textFilter() +}]; + + + { + props => ( +
+ +
+ +
+ ) + } +
+`; + +export default () => ( +
+

Table will keep the filter/sort state when column toggle

+ + { + props => ( +
+ +
+ +
+ ) + } +
+ { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 93ca8c4..6acd9b9 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -210,6 +210,7 @@ import BasicColumnToggle from 'examples/column-toggle'; import DefaultVisibility from 'examples/column-toggle/default-visibility'; import StylingColumnToggle from 'examples/column-toggle/styling-toggle-list'; import CustomToggleList from 'examples/column-toggle/custom-toggle-list'; +import ColumnToggleWithFilter from 'examples/column-toggle/column-toggle-with-filter'; // loading overlay import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay'; @@ -451,7 +452,8 @@ storiesOf('Column Toggle', module) .add('Basic Column Toggle', () => ) .add('Default Visibility', () => ) .add('Styling Column Toggle', () => ) - .add('Custom Column Toggle', () => ); + .add('Custom Column Toggle', () => ) + .add('Column Toggle with Filter', () => ); storiesOf('Export CSV', module) .addDecorator(bootstrapStyle()) diff --git a/packages/react-bootstrap-table2-filter/src/components/date.js b/packages/react-bootstrap-table2-filter/src/components/date.js index 578f558..3d8ff45 100644 --- a/packages/react-bootstrap-table2-filter/src/components/date.js +++ b/packages/react-bootstrap-table2-filter/src/components/date.js @@ -83,14 +83,27 @@ class DateFilter extends Component { 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)); + getDefaultComparator() { + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal) { + return filterState.filterVal.comparator; } - return defaultDate; + if (defaultValue && defaultValue.comparator) { + return defaultValue.comparator; + } + return ''; + } + + getDefaultDate() { + // Set the appropriate format for the input type=date, i.e. "YYYY-MM-DD" + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal && filterState.filterVal.date) { + return dateParser(filterState.filterVal.date); + } + if (defaultValue && defaultValue.date) { + return dateParser(new Date(defaultValue.date)); + } + return ''; } applyFilter(value, comparator, isInitial) { @@ -122,8 +135,7 @@ class DateFilter extends Component { dateStyle, className, comparatorClassName, - dateClassName, - defaultValue + dateClassName } = this.props; return ( @@ -143,7 +155,7 @@ class DateFilter extends Component { style={ comparatorStyle } className={ `date-filter-comparator form-control ${comparatorClassName}` } onChange={ this.onChangeComparator } - defaultValue={ defaultValue ? defaultValue.comparator : '' } + defaultValue={ this.getDefaultComparator() } > { this.getComparatorOptions() } @@ -169,6 +181,7 @@ class DateFilter extends Component { DateFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, delay: PropTypes.number, defaultValue: PropTypes.shape({ date: PropTypes.oneOfType([PropTypes.object]), @@ -210,6 +223,7 @@ DateFilter.defaultProps = { date: undefined, comparator: '' }, + filterState: {}, withoutEmptyComparatorOption: false, comparators: legalComparators, placeholder: undefined, diff --git a/packages/react-bootstrap-table2-filter/src/components/multiselect.js b/packages/react-bootstrap-table2-filter/src/components/multiselect.js index ae8714e..1d8794c 100644 --- a/packages/react-bootstrap-table2-filter/src/components/multiselect.js +++ b/packages/react-bootstrap-table2-filter/src/components/multiselect.js @@ -69,6 +69,14 @@ class MultiSelectFilter extends Component { } } + getDefaultValue() { + const { filterState, defaultValue } = this.props; + if (filterState && typeof filterState.filterVal !== 'undefined') { + return filterState.filterVal; + } + return defaultValue; + } + getOptions() { const optionTags = []; const { options, placeholder, column, withoutEmptyOption } = this.props; @@ -106,6 +114,7 @@ class MultiSelectFilter extends Component { const { style, className, + filterState, defaultValue, onFilter, column, @@ -135,7 +144,7 @@ class MultiSelectFilter extends Component { className={ selectClass } onChange={ this.filter } onClick={ e => e.stopPropagation() } - defaultValue={ defaultValue !== undefined ? defaultValue : '' } + defaultValue={ this.getDefaultValue() } > { this.getOptions() } @@ -148,6 +157,7 @@ MultiSelectFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, options: PropTypes.object.isRequired, + filterState: PropTypes.object, comparator: PropTypes.oneOf([LIKE, EQ]), placeholder: PropTypes.string, style: PropTypes.object, @@ -160,6 +170,7 @@ MultiSelectFilter.propTypes = { MultiSelectFilter.defaultProps = { defaultValue: [], + filterState: {}, className: '', withoutEmptyOption: false, comparator: EQ, diff --git a/packages/react-bootstrap-table2-filter/src/components/number.js b/packages/react-bootstrap-table2-filter/src/components/number.js index 29e0370..7abea58 100644 --- a/packages/react-bootstrap-table2-filter/src/components/number.js +++ b/packages/react-bootstrap-table2-filter/src/components/number.js @@ -94,6 +94,28 @@ class NumberFilter extends Component { onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator }); } + getDefaultComparator() { + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal) { + return filterState.filterVal.comparator; + } + if (defaultValue && defaultValue.comparator) { + return defaultValue.comparator; + } + return ''; + } + + getDefaultValue() { + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal) { + return filterState.filterVal.number; + } + if (defaultValue && defaultValue.number) { + return defaultValue.number; + } + return ''; + } + getComparatorOptions() { const optionTags = []; const { withoutEmptyComparatorOption } = this.props; @@ -148,7 +170,6 @@ class NumberFilter extends Component { render() { const { isSelected } = this.state; const { - defaultValue, column, options, style, @@ -184,7 +205,7 @@ class NumberFilter extends Component { id={ `number-filter-comparator-${column.text}` } className={ `number-filter-comparator form-control ${comparatorClassName}` } onChange={ this.onChangeComparator } - defaultValue={ defaultValue ? defaultValue.comparator : '' } + defaultValue={ this.getDefaultComparator() } > { this.getComparatorOptions() } @@ -202,7 +223,7 @@ class NumberFilter extends Component { style={ numberStyle } className={ selectClass } onChange={ this.onChangeNumberSet } - defaultValue={ defaultValue ? defaultValue.number : '' } + defaultValue={ this.getDefaultValue() } > { this.getNumberOptions() } @@ -217,7 +238,7 @@ class NumberFilter extends Component { className={ `number-filter-input form-control ${numberClassName}` } placeholder={ placeholder || `Enter ${column.text}...` } onChange={ this.onChangeNumber } - defaultValue={ defaultValue ? defaultValue.number : '' } + defaultValue={ this.getDefaultValue() } /> } @@ -229,6 +250,7 @@ class NumberFilter extends Component { NumberFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, options: PropTypes.arrayOf(PropTypes.number), defaultValue: PropTypes.shape({ number: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), @@ -273,6 +295,7 @@ NumberFilter.defaultProps = { number: undefined, comparator: '' }, + filterState: {}, withoutEmptyComparatorOption: false, withoutEmptyNumberOption: false, comparators: legalComparators, diff --git a/packages/react-bootstrap-table2-filter/src/components/select.js b/packages/react-bootstrap-table2-filter/src/components/select.js index db5348a..7b83827 100644 --- a/packages/react-bootstrap-table2-filter/src/components/select.js +++ b/packages/react-bootstrap-table2-filter/src/components/select.js @@ -44,7 +44,7 @@ class SelectFilter extends Component { constructor(props) { super(props); this.filter = this.filter.bind(this); - const isSelected = getOptionValue(props.options, props.defaultValue) !== undefined; + const isSelected = getOptionValue(props.options, this.getDefaultValue()) !== undefined; this.state = { isSelected }; } @@ -82,6 +82,14 @@ class SelectFilter extends Component { } } + getDefaultValue() { + const { filterState, defaultValue } = this.props; + if (filterState && typeof filterState.filterVal !== 'undefined') { + return filterState.filterVal; + } + return defaultValue; + } + getOptions() { const optionTags = []; const { options, placeholder, column, withoutEmptyOption } = this.props; @@ -132,6 +140,7 @@ class SelectFilter extends Component { withoutEmptyOption, caseSensitive, getFilter, + filterState, ...rest } = this.props; @@ -152,7 +161,7 @@ class SelectFilter extends Component { className={ selectClass } onChange={ this.filter } onClick={ e => e.stopPropagation() } - defaultValue={ defaultValue !== undefined ? defaultValue : '' } + defaultValue={ this.getDefaultValue() || '' } > { this.getOptions() } @@ -164,6 +173,7 @@ class SelectFilter extends Component { SelectFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, options: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired, comparator: PropTypes.oneOf([LIKE, EQ]), placeholder: PropTypes.string, @@ -177,6 +187,7 @@ SelectFilter.propTypes = { SelectFilter.defaultProps = { defaultValue: '', + filterState: {}, className: '', withoutEmptyOption: false, comparator: EQ, diff --git a/packages/react-bootstrap-table2-filter/src/components/text.js b/packages/react-bootstrap-table2-filter/src/components/text.js index 1995ebe..a359a95 100644 --- a/packages/react-bootstrap-table2-filter/src/components/text.js +++ b/packages/react-bootstrap-table2-filter/src/components/text.js @@ -13,8 +13,14 @@ class TextFilter extends Component { this.filter = this.filter.bind(this); this.handleClick = this.handleClick.bind(this); this.timeout = null; + function getDefaultValue() { + if (props.filterState && typeof props.filterState.filterVal !== 'undefined') { + return props.filterState.filterVal; + } + return props.defaultValue; + } this.state = { - value: props.defaultValue + value: getDefaultValue() }; } @@ -89,6 +95,7 @@ class TextFilter extends Component { caseSensitive, defaultValue, getFilter, + filterState, ...rest } = this.props; @@ -119,6 +126,7 @@ class TextFilter extends Component { TextFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, comparator: PropTypes.oneOf([LIKE, EQ]), defaultValue: PropTypes.string, delay: PropTypes.number, @@ -131,6 +139,7 @@ TextFilter.propTypes = { TextFilter.defaultProps = { delay: FILTER_DELAY, + filterState: {}, defaultValue: '', caseSensitive: false }; diff --git a/packages/react-bootstrap-table2-filter/src/context.js b/packages/react-bootstrap-table2-filter/src/context.js index 058d7ff..013be6e 100644 --- a/packages/react-bootstrap-table2-filter/src/context.js +++ b/packages/react-bootstrap-table2-filter/src/context.js @@ -108,7 +108,8 @@ export default ( { this.props.children } diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index b2ac582..42d419e 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -91,6 +91,7 @@ class BootstrapTable extends PropsBaseResolver(Component) { sortOrder={ this.props.sortOrder } onSort={ this.props.onSort } onFilter={ this.props.onFilter } + currFilters={ this.props.currFilters } onExternalFilter={ this.props.onExternalFilter } selectRow={ selectRow } expandRow={ expandRow } diff --git a/packages/react-bootstrap-table2/src/header-cell.js b/packages/react-bootstrap-table2/src/header-cell.js index 3321507..18f69c0 100644 --- a/packages/react-bootstrap-table2/src/header-cell.js +++ b/packages/react-bootstrap-table2/src/header-cell.js @@ -18,6 +18,7 @@ const HeaderCell = (props) => { sortOrder, isLastSorting, onFilter, + currFilters, onExternalFilter } = props; @@ -99,7 +100,14 @@ const HeaderCell = (props) => { const onCustomFilter = onExternalFilter(column, filter.props.type); filterElm = filterRenderer(onCustomFilter, column); } else if (filter) { - filterElm = ; + filterElm = ( + + ); } const children = headerFormatter ? @@ -162,6 +170,7 @@ HeaderCell.propTypes = { sortCaret: PropTypes.func, isLastSorting: PropTypes.bool, onFilter: PropTypes.func, + currFilters: PropTypes.object, onExternalFilter: PropTypes.func }; diff --git a/packages/react-bootstrap-table2/src/header.js b/packages/react-bootstrap-table2/src/header.js index 77ee127..4360b2c 100644 --- a/packages/react-bootstrap-table2/src/header.js +++ b/packages/react-bootstrap-table2/src/header.js @@ -18,6 +18,7 @@ const Header = (props) => { sortField, sortOrder, selectRow, + currFilters, onExternalFilter, expandRow } = props; @@ -50,6 +51,7 @@ const Header = (props) => { onSort={ onSort } sorting={ currSort } onFilter={ onFilter } + currFilters={ currFilters } onExternalFilter={ onExternalFilter } sortOrder={ sortOrder } isLastSorting={ isLastSorting } @@ -89,6 +91,7 @@ Header.propTypes = { sortField: PropTypes.string, sortOrder: PropTypes.string, selectRow: PropTypes.object, + currFilters: PropTypes.object, onExternalFilter: PropTypes.func, className: PropTypes.string, expandRow: PropTypes.object