From b7fac973d2c2fd53c868e0ee0e63fe721200aa91 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 3 Nov 2018 16:37:02 +0800 Subject: [PATCH 1/6] fix #297 --- .../src/contexts/selection-context.js | 62 +++++++++++-------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/packages/react-bootstrap-table2/src/contexts/selection-context.js b/packages/react-bootstrap-table2/src/contexts/selection-context.js index ce8d354..f266824 100644 --- a/packages/react-bootstrap-table2/src/contexts/selection-context.js +++ b/packages/react-bootstrap-table2/src/contexts/selection-context.js @@ -43,20 +43,23 @@ class SelectionProvider extends React.Component { let currSelected = [...this.state.selected]; - if (mode === ROW_SELECT_SINGLE) { // when select mode is radio - currSelected = [rowKey]; - } else if (checked) { // when select mode is checkbox - currSelected.push(rowKey); - } else { - currSelected = currSelected.filter(value => value !== rowKey); - } - - if (onSelect) { - const row = dataOperator.getRowByRowId(data, keyField, rowKey); - onSelect(row, checked, rowIndex, e); - } - - this.setState(() => ({ selected: currSelected })); + this.setState(() => { + let result = true; + if (onSelect) { + const row = dataOperator.getRowByRowId(data, keyField, rowKey); + result = onSelect(row, checked, rowIndex, e); + } + if (result === true || result === undefined) { + if (mode === ROW_SELECT_SINGLE) { // when select mode is radio + currSelected = [rowKey]; + } else if (checked) { // when select mode is checkbox + currSelected.push(rowKey); + } else { + currSelected = currSelected.filter(value => value !== rowKey); + } + } + return { selected: currSelected }; + }); } handleAllRowsSelect = (e, isUnSelect) => { @@ -78,19 +81,24 @@ class SelectionProvider extends React.Component { currSelected = selected.filter(s => typeof data.find(d => d[keyField] === s) === 'undefined'); } - if (onSelectAll) { - onSelectAll( - !isUnSelect, - dataOperator.getSelectedRows( - data, - keyField, - isUnSelect ? this.state.selected : currSelected - ), - e - ); - } - - this.setState(() => ({ selected: currSelected })); + this.setState(() => { + let result; + if (onSelectAll) { + result = onSelectAll( + !isUnSelect, + dataOperator.getSelectedRows( + data, + keyField, + isUnSelect ? this.state.selected : currSelected + ), + e + ); + if (Array.isArray(result)) { + currSelected = result; + } + } + return { selected: currSelected }; + }); } render() { From 4554db02d251b49231b0d5ad680c7842e668e5e1 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 3 Nov 2018 16:37:51 +0800 Subject: [PATCH 2/6] add story for advance selection contrl --- .../selection-advance-management.js | 88 +++++++++++++++++++ .../stories/index.js | 2 + 2 files changed, 90 insertions(+) create mode 100644 packages/react-bootstrap-table2-example/examples/row-selection/selection-advance-management.js diff --git a/packages/react-bootstrap-table2-example/examples/row-selection/selection-advance-management.js b/packages/react-bootstrap-table2-example/examples/row-selection/selection-advance-management.js new file mode 100644 index 0000000..22af899 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/selection-advance-management.js @@ -0,0 +1,88 @@ +/* eslint no-alert: 0 */ +/* eslint consistent-return: 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 sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +class AdvSelectionManagment extends React.Component { + handleOnSelect = (row, isSelect) => { + if (isSelect && row.id < 3) { + alert('Oops, You can not select Product ID which less than 3'); + return false; // return false to deny current select action + } + return true; // return true or dont return to approve current select action + } + + handleOnSelectAll = (isSelect, rows) => { + if (isSelect) { + return rows.filter(r => r.id >= 3).map(r => r.id); + } + } + + render() { + const selectRow = { + mode: 'checkbox', + clickToSelect: true, + onSelect: this.handleOnSelect, + onSelectAll: this.handleOnSelectAll + }; + return ( +
+

You can not select Product ID less than 3

+ + { sourceCode } +
+ ); + } +} +`; + +export default class AdvSelectionManagment extends React.Component { + handleOnSelect = (row, isSelect) => { + if (isSelect && row.id < 3) { + alert('Oops, You can not select Product ID which less than 3'); + return false; // return false to deny current select action + } + return true; // return true or dont return to approve current select action + } + + handleOnSelectAll = (isSelect, rows) => { + if (isSelect) { + return rows.filter(r => r.id >= 3).map(r => r.id); + } + } + + render() { + const selectRow = { + mode: 'checkbox', + clickToSelect: true, + onSelect: this.handleOnSelect, + onSelectAll: this.handleOnSelectAll + }; + return ( +
+

You can not select Product ID less than 3

+ + { sourceCode } +
+ ); + } +} diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 7ba6b1b..fd04751 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -118,6 +118,7 @@ import MultipleSelectionTable from 'examples/row-selection/multiple-selection'; import ClickToSelectTable from 'examples/row-selection/click-to-select'; import DefaultSelectTable from 'examples/row-selection/default-select'; import SelectionManagement from 'examples/row-selection/selection-management'; +import AdvanceSelectionManagement from 'examples/row-selection/selection-advance-management'; import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit'; import SelectionWithExpansionTable from 'examples/row-selection/selection-with-expansion'; import SelectionNoDataTable from 'examples/row-selection/selection-no-data'; @@ -312,6 +313,7 @@ storiesOf('Row Selection', module) .add('Click to Select', () => ) .add('Default Select', () => ) .add('Selection Management', () => ) + .add('Advance Selection Management', () => ) .add('Click to Select and Edit Cell', () => ) .add('Row Select and Expand', () => ) .add('Selection without Data', () => ) From 9bc25c9d3ef3c00e0a46c6b4edf086389f1f77d5 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 3 Nov 2018 16:45:05 +0800 Subject: [PATCH 3/6] patch docs for selection control --- docs/row-selection.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/row-selection.md b/docs/row-selection.md index 9a09361..c8b97e3 100644 --- a/docs/row-selection.md +++ b/docs/row-selection.md @@ -211,18 +211,44 @@ const selectRow = { }; ``` +> If you want to reject current select action, just return `false`: + +```js +const selectRow = { + mode: 'checkbox', + onSelect: (row, isSelect, rowIndex, e) => { + if (SOME_CONDITION) { + return false; + } + } +}; +``` + ### selectRow.onSelectAll - [Function] This callback function will be called when select/unselect all and it only work when you configure [`selectRow.mode`](#mode) as `checkbox`. ```js const selectRow = { mode: 'checkbox', - onSelectAll: (isSelect, results, e) => { + onSelectAll: (isSelect, rows, e) => { // ... } }; ``` +> If you want to control the final selection result, just return a row key array: + +```js +const selectRow = { + mode: 'checkbox', + onSelectAll: (isSelect, rows, e) => { + if (isSelect && SOME_CONDITION) { + return [1, 3, 4]; // finally, key 1, 3, 4 will being selected + } + } +}; +``` + ### selectRow.hideSelectColumn - [Bool] Default is `false`, if you don't want to have a selection column, give this prop as `true` From 98a04a57100a2cd9f990ae540e30f16d53a541fa Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 4 Nov 2018 16:24:34 +0800 Subject: [PATCH 4/6] fix #641 --- .../src/components/select.js | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/packages/react-bootstrap-table2-filter/src/components/select.js b/packages/react-bootstrap-table2-filter/src/components/select.js index e505138..a30e691 100644 --- a/packages/react-bootstrap-table2-filter/src/components/select.js +++ b/packages/react-bootstrap-table2-filter/src/components/select.js @@ -7,6 +7,14 @@ import { LIKE, EQ } from '../comparison'; 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].label !== prevOpts[i].label) { + return false; + } + } + return currOpts.length === prevOpts.length; + } const keys = Object.keys(currOpts); for (let i = 0; i < keys.length; i += 1) { if (currOpts[keys[i]] !== prevOpts[keys[i]]) { @@ -16,11 +24,21 @@ function optionsEquals(currOpts, prevOpts) { return Object.keys(currOpts).length === Object.keys(prevOpts).length; } +function getOptionValue(options, key) { + if (Array.isArray(options)) { + const result = options + .filter(({ label }) => label === key) + .map(({ value }) => value); + return result[0]; + } + return options[key]; +} + class SelectFilter extends Component { constructor(props) { super(props); this.filter = this.filter.bind(this); - const isSelected = props.options[props.defaultValue] !== undefined; + const isSelected = getOptionValue(props.options, props.defaultValue) !== undefined; this.state = { isSelected }; } @@ -66,9 +84,14 @@ class SelectFilter extends Component { )); } - Object.keys(options).forEach(key => - optionTags.push() - ); + if (Array.isArray(options)) { + options.forEach(({ value, label }) => + optionTags.push()); + } else { + Object.keys(options).forEach(key => + optionTags.push() + ); + } return optionTags; } @@ -128,7 +151,7 @@ class SelectFilter extends Component { SelectFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, - options: PropTypes.object.isRequired, + options: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired, comparator: PropTypes.oneOf([LIKE, EQ]), placeholder: PropTypes.string, style: PropTypes.object, From 4a16cb314d65e31986191a6d889486c340f7690d Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 4 Nov 2018 16:24:48 +0800 Subject: [PATCH 5/6] add story for #641 --- .../select-filter-preserve-option-order.js | 70 +++++++++++++++++++ .../stories/index.js | 2 + 2 files changed, 72 insertions(+) create mode 100644 packages/react-bootstrap-table2-example/examples/column-filter/select-filter-preserve-option-order.js diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/select-filter-preserve-option-order.js b/packages/react-bootstrap-table2-example/examples/column-filter/select-filter-preserve-option-order.js new file mode 100644 index 0000000..f7a0fca --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-filter/select-filter-preserve-option-order.js @@ -0,0 +1,70 @@ +/* eslint max-len: 0 */ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter'; +import Code from 'components/common/code-block'; +import { productsQualityGenerator } from 'utils/common'; + +const products = productsQualityGenerator(6); + +const selectOptions = [ + { label: 0, value: 'good' }, + { label: 1, value: 'Bad' }, + { label: 2, value: 'unknown' } +]; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'quality', + text: 'Product Quailty', + formatter: cell => selectOptions.find(opt => opt.label === cell).value, + filter: selectFilter({ + options: selectOptions + }) +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import filterFactory, { selectFilter } from 'react-bootstrap-table2-filter'; + +const selectOptions = [ + { label: 0, value: 'good' }, + { label: 1, value: 'Bad' }, + { label: 2, value: 'unknown' } +]; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'quality', + text: 'Product Quailty', + formatter: cell => selectOptions.find(opt => opt.label === cell).value, + filter: selectFilter({ + options: selectOptions + }) +}]; + + +`; + +export default () => ( +
+

selectFilter.options accept an Array and we keep that order when rendering the options

+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index fd04751..2757280 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -55,6 +55,7 @@ import CustomFilterValue from 'examples/column-filter/custom-filter-value'; import SelectFilter from 'examples/column-filter/select-filter'; import SelectFilterWithDefaultValue from 'examples/column-filter/select-filter-default-value'; import SelectFilterComparator from 'examples/column-filter/select-filter-like-comparator'; +import SelectFilterWithPreservedOptionsOrder from 'examples/column-filter/select-filter-preserve-option-order'; import CustomSelectFilter from 'examples/column-filter/custom-select-filter'; import MultiSelectFilter from 'examples/column-filter/multi-select-filter'; import MultiSelectFilterDefaultValue from 'examples/column-filter/multi-select-filter-default-value'; @@ -263,6 +264,7 @@ storiesOf('Column Filter', module) .add('Programmatically Multi Select Filter', () => ) .add('Custom Filter', () => ) .add('Advance Custom Filter', () => ) + .add('Preserved Option Order on Select Filter', () => ) .add('Clear All Filters', () => ); storiesOf('Work on Rows', module) From a0ba41c103835996a8047753b9c8682240451525 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 4 Nov 2018 16:32:13 +0800 Subject: [PATCH 6/6] patch docs for #641 --- .../react-bootstrap-table2-filter/README.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/react-bootstrap-table2-filter/README.md b/packages/react-bootstrap-table2-filter/README.md index 8369efd..f73b962 100644 --- a/packages/react-bootstrap-table2-filter/README.md +++ b/packages/react-bootstrap-table2-filter/README.md @@ -115,6 +115,27 @@ const qualityFilter = selectFilter({ // omit... ``` +> Note, the selectOptions can be an array also: + +```js +const selectOptions = [ + { label: 0, value: 'good' }, + { label: 1, value: 'Bad' }, + { label: 2, value: 'unknown' } +]; +const columns = [ + ..., { + dataField: 'quality', + text: 'Product Quailty', + formatter: cell => selectOptions.find(opt => opt.label === cell).value, + filter: selectFilter({ + options: selectOptions + }) +}]; +``` + +The benifit is `react-bootstrap-table2` will render the select options by the order of array. + ## MultiSelect Filter A quick example: