mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2025-10-16 11:55:39 +00:00
fix #116
* implement selection hook * add story for selection hooks * add tests for selection hooks * add missing test for store.setSelectedRowKeys(array) * patch docs for selection hooks
This commit is contained in:
parent
cf142d3b39
commit
974f129aad
@ -15,6 +15,8 @@ The following are available properties in `selectRow`:
|
||||
* [nonSelectable)](#nonSelectable)
|
||||
* [clickToSelect)](#clickToSelect)
|
||||
* [clickToEdit](#clickToEdit)
|
||||
* [onSelect](#onSelect)
|
||||
* [onSelectAll](#onSelectAll)
|
||||
|
||||
#### Optional
|
||||
|
||||
@ -144,4 +146,29 @@ const selectRow = {
|
||||
clickToSelect: true
|
||||
clickToEdit: true
|
||||
};
|
||||
```
|
||||
```
|
||||
|
||||
# <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` and `rowIndex`.
|
||||
|
||||
```js
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
onSelect: (row, isSelect, rowIndex) => {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# <a name='onSelectAll'>selectRow.onSelectAll - [Function]</a>
|
||||
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) => {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
66
packages/react-bootstrap-table2-example/examples/row-selection/selection-hooks.js
vendored
Normal file
66
packages/react-bootstrap-table2-example/examples/row-selection/selection-hooks.js
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
|
||||
/* eslint no-console: 0 */
|
||||
import React from 'react';
|
||||
|
||||
import BootstrapTable from 'react-bootstrap-table2';
|
||||
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 selectRow = {
|
||||
mode: 'checkbox',
|
||||
clickToSelect: true,
|
||||
onSelect: (row, isSelect, rowIndex) => {
|
||||
console.log(row.id);
|
||||
console.log(isSelect);
|
||||
console.log(rowIndex);
|
||||
},
|
||||
onSelectAll: (isSelect, rows) => {
|
||||
console.log(isSelect);
|
||||
console.log(rows);
|
||||
}
|
||||
};
|
||||
|
||||
const sourceCode = `\
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
clickToSelect: true
|
||||
};
|
||||
|
||||
<BootstrapTable
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
selectRow={ selectRow }
|
||||
/>
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
@ -59,6 +59,7 @@ import SelectionStyleTable from 'examples/row-selection/selection-style';
|
||||
import SelectionClassTable from 'examples/row-selection/selection-class';
|
||||
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';
|
||||
|
||||
// css style
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
@ -126,4 +127,5 @@ storiesOf('Row Selection', module)
|
||||
.add('Selection Style', () => <SelectionStyleTable />)
|
||||
.add('Selection Class', () => <SelectionClassTable />)
|
||||
.add('Selection Background Color', () => <SelectionBgColorTable />)
|
||||
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />);
|
||||
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />)
|
||||
.add('Selection Hooks', () => <SelectionHooks />);
|
||||
|
||||
@ -130,6 +130,8 @@ BootstrapTable.propTypes = {
|
||||
mode: PropTypes.oneOf([Const.ROW_SELECT_SINGLE, Const.ROW_SELECT_MULTIPLE]).isRequired,
|
||||
clickToSelect: PropTypes.bool,
|
||||
clickToEdit: PropTypes.bool,
|
||||
onSelect: PropTypes.func,
|
||||
onSelectAll: PropTypes.func,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
|
||||
classes: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||
nonSelectable: PropTypes.array,
|
||||
|
||||
@ -12,7 +12,8 @@ export default class SelectionCell extends Component {
|
||||
rowKey: PropTypes.any,
|
||||
selected: PropTypes.bool,
|
||||
onRowSelect: PropTypes.func,
|
||||
disabled: PropTypes.bool
|
||||
disabled: PropTypes.bool,
|
||||
rowIndex: PropTypes.number
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -32,7 +33,8 @@ export default class SelectionCell extends Component {
|
||||
rowKey,
|
||||
selected,
|
||||
onRowSelect,
|
||||
disabled
|
||||
disabled,
|
||||
rowIndex
|
||||
} = this.props;
|
||||
|
||||
if (disabled) return;
|
||||
@ -41,7 +43,7 @@ export default class SelectionCell extends Component {
|
||||
? true
|
||||
: !selected;
|
||||
|
||||
onRowSelect(rowKey, checked);
|
||||
onRowSelect(rowKey, checked, rowIndex);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -21,8 +21,8 @@ class RowSelectionWrapper extends Component {
|
||||
* @param {String} rowKey - row key of what was selected.
|
||||
* @param {Boolean} checked - next checked status of input button.
|
||||
*/
|
||||
handleRowSelect(rowKey, checked) {
|
||||
const { selectRow: { mode }, store } = this.props;
|
||||
handleRowSelect(rowKey, checked, rowIndex) {
|
||||
const { selectRow: { mode, onSelect }, store } = this.props;
|
||||
const { ROW_SELECT_SINGLE } = Const;
|
||||
|
||||
let currSelected = [...store.getSelectedRowKeys()];
|
||||
@ -37,6 +37,11 @@ class RowSelectionWrapper extends Component {
|
||||
|
||||
store.setSelectedRowKeys(currSelected);
|
||||
|
||||
if (onSelect) {
|
||||
const row = store.getRowByRowId(rowKey);
|
||||
onSelect(row, checked, rowIndex);
|
||||
}
|
||||
|
||||
this.setState(() => ({
|
||||
selectedRowKeys: currSelected
|
||||
}));
|
||||
@ -47,19 +52,26 @@ class RowSelectionWrapper extends Component {
|
||||
* @param {Boolean} option - customized result for all rows selection
|
||||
*/
|
||||
handleAllRowsSelect(option) {
|
||||
const { store, selectRow } = this.props;
|
||||
const selected = store.isAnySelectedRow(selectRow.nonSelectable);
|
||||
const { store, selectRow: {
|
||||
onSelectAll,
|
||||
nonSelectable
|
||||
} } = this.props;
|
||||
const selected = store.isAnySelectedRow(nonSelectable);
|
||||
|
||||
// set next status of all row selected by store.selected or customizing by user.
|
||||
const result = option || !selected;
|
||||
|
||||
const currSelected = result ?
|
||||
store.selectAllRows(selectRow.nonSelectable) :
|
||||
store.cleanSelectedRows(selectRow.nonSelectable);
|
||||
store.selectAllRows(nonSelectable) :
|
||||
store.cleanSelectedRows(nonSelectable);
|
||||
|
||||
|
||||
store.setSelectedRowKeys(currSelected);
|
||||
|
||||
if (onSelectAll) {
|
||||
onSelectAll(result, store.getSelectedRows());
|
||||
}
|
||||
|
||||
this.setState(() => ({
|
||||
selectedRowKeys: currSelected
|
||||
}));
|
||||
|
||||
6
packages/react-bootstrap-table2/src/row.js
vendored
6
packages/react-bootstrap-table2/src/row.js
vendored
@ -21,6 +21,7 @@ class Row extends Component {
|
||||
selected,
|
||||
keyField,
|
||||
selectable,
|
||||
rowIndex,
|
||||
selectRow: {
|
||||
onRowSelect,
|
||||
clickToEdit
|
||||
@ -33,12 +34,12 @@ class Row extends Component {
|
||||
this.clickNum += 1;
|
||||
_.debounce(() => {
|
||||
if (this.clickNum === 1) {
|
||||
onRowSelect(key, !selected);
|
||||
onRowSelect(key, !selected, rowIndex);
|
||||
}
|
||||
this.clickNum = 0;
|
||||
}, Const.DELAY_FOR_DBCLICK)();
|
||||
} else {
|
||||
onRowSelect(key, !selected);
|
||||
onRowSelect(key, !selected, rowIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -83,6 +84,7 @@ class Row extends Component {
|
||||
<SelectionCell
|
||||
{ ...selectRow }
|
||||
rowKey={ key }
|
||||
rowIndex={ rowIndex }
|
||||
selected={ selected }
|
||||
disabled={ !selectable }
|
||||
/>
|
||||
|
||||
@ -50,6 +50,10 @@ export default class Store {
|
||||
this.selected = selectedKeys;
|
||||
}
|
||||
|
||||
getSelectedRows() {
|
||||
return this.selected.map(k => this.getRowByRowId(k));
|
||||
}
|
||||
|
||||
getSelectedRowKeys() {
|
||||
return this.selected;
|
||||
}
|
||||
|
||||
@ -318,7 +318,7 @@ describe('Body', () => {
|
||||
|
||||
it('should calling selectRow.bgColor callback correctly', () => {
|
||||
expect(bgColorCallBack.calledOnce).toBeTruthy();
|
||||
expect(bgColorCallBack.calledWith(data[0]), 1);
|
||||
expect(bgColorCallBack.calledWith(data[0]), 1).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render Row component with correct style.backgroundColor prop', () => {
|
||||
|
||||
@ -6,6 +6,7 @@ import SelectionCell from '../../src/row-selection/selection-cell';
|
||||
|
||||
describe('<SelectionCell />', () => {
|
||||
const mode = 'checkbox';
|
||||
const rowIndex = 1;
|
||||
|
||||
let wrapper;
|
||||
|
||||
@ -56,6 +57,7 @@ describe('<SelectionCell />', () => {
|
||||
selected
|
||||
rowKey={ rowKey }
|
||||
mode={ mode }
|
||||
rowIndex={ rowIndex }
|
||||
onRowSelect={ mockOnRowSelect }
|
||||
/>
|
||||
);
|
||||
@ -69,7 +71,7 @@ describe('<SelectionCell />', () => {
|
||||
it('should calling onRowSelect callback correctly', () => {
|
||||
expect(mockOnRowSelect.calledOnce).toBe(true);
|
||||
expect(
|
||||
mockOnRowSelect.calledWith(rowKey, !selected)
|
||||
mockOnRowSelect.calledWith(rowKey, !selected, rowIndex)
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -81,6 +83,7 @@ describe('<SelectionCell />', () => {
|
||||
selected
|
||||
rowKey={ rowKey }
|
||||
mode={ mode }
|
||||
rowIndex={ rowIndex }
|
||||
onRowSelect={ mockOnRowSelect }
|
||||
disabled
|
||||
/>
|
||||
@ -104,6 +107,7 @@ describe('<SelectionCell />', () => {
|
||||
selected
|
||||
rowKey={ rowKey }
|
||||
mode="radio"
|
||||
rowIndex={ rowIndex }
|
||||
onRowSelect={ mockOnRowSelect }
|
||||
/>
|
||||
);
|
||||
@ -113,12 +117,12 @@ describe('<SelectionCell />', () => {
|
||||
// first click
|
||||
wrapper.find('td').simulate('click');
|
||||
expect(mockOnRowSelect.callCount).toBe(1);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, true)).toBe(true);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, true, rowIndex)).toBe(true);
|
||||
|
||||
// second click
|
||||
wrapper.find('td').simulate('click');
|
||||
expect(mockOnRowSelect.callCount).toBe(2);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, true)).toBe(true);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, true, rowIndex)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@ -128,6 +132,7 @@ describe('<SelectionCell />', () => {
|
||||
<SelectionCell
|
||||
rowKey={ rowKey }
|
||||
mode="checkbox"
|
||||
rowIndex={ rowIndex }
|
||||
onRowSelect={ mockOnRowSelect }
|
||||
/>
|
||||
);
|
||||
@ -138,13 +143,13 @@ describe('<SelectionCell />', () => {
|
||||
wrapper.setProps({ selected: true });
|
||||
wrapper.find('td').simulate('click');
|
||||
expect(mockOnRowSelect.callCount).toBe(1);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, false)).toBe(true);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, false, rowIndex)).toBe(true);
|
||||
|
||||
// second click
|
||||
wrapper.setProps({ selected: false });
|
||||
wrapper.find('td').simulate('click');
|
||||
expect(mockOnRowSelect.callCount).toBe(2);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, true)).toBe(true);
|
||||
expect(mockOnRowSelect.calledWith(rowKey, true, rowIndex)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -158,6 +163,7 @@ describe('<SelectionCell />', () => {
|
||||
<SelectionCell
|
||||
rowKey={ 1 }
|
||||
mode={ mode }
|
||||
rowIndex={ rowIndex }
|
||||
selected={ selected }
|
||||
/>
|
||||
);
|
||||
@ -176,6 +182,7 @@ describe('<SelectionCell />', () => {
|
||||
<SelectionCell
|
||||
rowKey={ 1 }
|
||||
mode={ mode }
|
||||
rowIndex={ rowIndex }
|
||||
selected={ selected }
|
||||
disabled
|
||||
/>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Store from '../../src/store/base';
|
||||
@ -28,6 +29,8 @@ describe('RowSelectionWrapper', () => {
|
||||
mode: 'radio'
|
||||
};
|
||||
|
||||
const rowIndex = 1;
|
||||
|
||||
const keyField = 'id';
|
||||
|
||||
const store = new Store({ data, keyField });
|
||||
@ -64,10 +67,10 @@ describe('RowSelectionWrapper', () => {
|
||||
const secondSelectedRow = data[1][keyField];
|
||||
|
||||
it('call handleRowSelect function should seting correct state.selectedRowKeys', () => {
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow);
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow, rowIndex);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([firstSelectedRow]);
|
||||
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow);
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow, rowIndex);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([secondSelectedRow]);
|
||||
});
|
||||
});
|
||||
@ -90,16 +93,16 @@ describe('RowSelectionWrapper', () => {
|
||||
});
|
||||
|
||||
it('call handleRowSelect function should seting correct state.selectedRowKeys', () => {
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow, true);
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow, true, rowIndex);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([firstSelectedRow]));
|
||||
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow, true);
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow, true, rowIndex);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([firstSelectedRow, secondSelectedRow]));
|
||||
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow, false);
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow, false, rowIndex);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([secondSelectedRow]));
|
||||
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow, false);
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow, false, rowIndex);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([]);
|
||||
});
|
||||
|
||||
@ -119,4 +122,61 @@ describe('RowSelectionWrapper', () => {
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selectRow.onSelect is defined', () => {
|
||||
const selectedRow = data[0][keyField];
|
||||
const onSelectCallBack = sinon.stub();
|
||||
|
||||
beforeEach(() => {
|
||||
selectRow.mode = 'checkbox';
|
||||
selectRow.onSelect = onSelectCallBack;
|
||||
wrapper = shallow(
|
||||
<RowSelectionWrapper
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
selectRow={ selectRow }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('selectRow.onSelect callback should be called correctly when calling handleRowSelect function', () => {
|
||||
wrapper.instance().handleRowSelect(selectedRow, true, rowIndex);
|
||||
expect(onSelectCallBack.callCount).toEqual(1);
|
||||
expect(onSelectCallBack.calledWith(data[0], true, rowIndex)).toBeTruthy();
|
||||
|
||||
wrapper.instance().handleRowSelect(selectedRow, false, rowIndex);
|
||||
expect(onSelectCallBack.callCount).toEqual(2);
|
||||
expect(onSelectCallBack.calledWith(data[0], false, rowIndex)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selectRow.onSelectAll is defined', () => {
|
||||
const onSelectAllCallBack = sinon.stub();
|
||||
|
||||
beforeEach(() => {
|
||||
selectRow.mode = 'checkbox';
|
||||
selectRow.onSelectAll = onSelectAllCallBack;
|
||||
wrapper = shallow(
|
||||
<RowSelectionWrapper
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
selectRow={ selectRow }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('selectRow.onSelect callback should be called correctly when calling handleRowSelect function', () => {
|
||||
wrapper.instance().handleAllRowsSelect();
|
||||
expect(onSelectAllCallBack.callCount).toEqual(1);
|
||||
expect(onSelectAllCallBack.calledWith(true, data)).toBeTruthy();
|
||||
|
||||
wrapper.instance().handleAllRowsSelect();
|
||||
expect(onSelectAllCallBack.callCount).toEqual(2);
|
||||
expect(onSelectAllCallBack.calledWith(false, [])).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -605,7 +605,7 @@ describe('Row', () => {
|
||||
});
|
||||
|
||||
it('should calling selectRow.onRowSelect with correct argument', () => {
|
||||
expect(onRowSelectCallBack.calledWith(row[keyField], false)).toBeTruthy();
|
||||
expect(onRowSelectCallBack.calledWith(row[keyField], false, rowIndex)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@ -636,7 +636,7 @@ describe('Row', () => {
|
||||
});
|
||||
|
||||
it('should calling selectRow.onRowSelect with correct argument', () => {
|
||||
expect(onRowSelectCallBack.calledWith(row[keyField], true)).toBeTruthy();
|
||||
expect(onRowSelectCallBack.calledWith(row[keyField], true, rowIndex)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -87,6 +87,31 @@ describe('Store Base', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getSelectedRows', () => {
|
||||
const selected = [1, 4];
|
||||
beforeEach(() => {
|
||||
store.setSelectedRowKeys(selected);
|
||||
});
|
||||
|
||||
it('should return all selected rows by store.selected', () => {
|
||||
const result = store.getSelectedRows();
|
||||
expect(result).toBeDefined();
|
||||
expect(result.length).toEqual(2);
|
||||
result.forEach((r) => {
|
||||
expect(r).toBeDefined();
|
||||
expect(selected.includes(r[store.keyField])).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setSelectedRowKeys', () => {
|
||||
const selected = [1, 4];
|
||||
it('should set store.selected correctly', () => {
|
||||
store.setSelectedRowKeys(selected);
|
||||
expect(store.getSelectedRowKeys()).toEqual(selected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edit', () => {
|
||||
it('should update a specified field correctly', () => {
|
||||
const newValue = 'newValue';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user