mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2025-10-16 11:55:39 +00:00
refine row selection to wrapper
* refine row selection code base to wrapper * refine testing for moving row selection code base to wrapper
This commit is contained in:
parent
6887c12d11
commit
0ca6335d92
@ -15,11 +15,8 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
this.validateProps();
|
||||
|
||||
this.handleSort = this.handleSort.bind(this);
|
||||
this.handleRowSelect = this.handleRowSelect.bind(this);
|
||||
this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this);
|
||||
this.state = {
|
||||
data: props.store.get(),
|
||||
selectedRowKeys: props.store.getSelectedRowKeys()
|
||||
data: props.store.get()
|
||||
};
|
||||
}
|
||||
|
||||
@ -57,11 +54,11 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
});
|
||||
|
||||
const cellSelectionInfo = this.resolveCellSelectionProps({
|
||||
onRowSelect: this.handleRowSelect
|
||||
onRowSelect: this.props.onRowSelect
|
||||
});
|
||||
|
||||
const headerCellSelectionInfo = this.resolveHeaderCellSelectionProps({
|
||||
onAllRowsSelect: this.handleAllRowsSelect,
|
||||
onAllRowsSelect: this.props.onAllRowsSelect,
|
||||
selected: store.selected,
|
||||
allRowsSelected: store.isAllRowsSelected()
|
||||
});
|
||||
@ -86,59 +83,13 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
noDataIndication={ noDataIndication }
|
||||
cellEdit={ cellEditInfo }
|
||||
selectRow={cellSelectionInfo}
|
||||
selectedRowKeys={this.state.selectedRowKeys}
|
||||
selectedRowKeys={store.getSelectedRowKeys()}
|
||||
/>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* row selection handler
|
||||
* @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;
|
||||
const { ROW_SELECT_SINGLE } = Const;
|
||||
|
||||
let currSelected = [...store.getSelectedRowKeys()];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
store.setSelectedRowKeys(currSelected);
|
||||
|
||||
this.setState(() => ({
|
||||
selectedRowKeys: currSelected
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* handle all rows selection on header cell by store.selected or given specific result.
|
||||
* @param {Boolean} option - customized result for all rows selection
|
||||
*/
|
||||
handleAllRowsSelect(option) {
|
||||
const { store } = this.props;
|
||||
const selected = store.isAnySelectedRow();
|
||||
|
||||
// set next status of all row selected by store.selected or customizing by user.
|
||||
const result = option || !selected;
|
||||
|
||||
const currSelected = result ? store.selectAllRowKeys() : [];
|
||||
|
||||
store.setSelectedRowKeys(currSelected);
|
||||
|
||||
this.setState(() => ({
|
||||
selectedRowKeys: currSelected
|
||||
}));
|
||||
}
|
||||
|
||||
handleSort(column) {
|
||||
const { store } = this.props;
|
||||
store.sortBy(column);
|
||||
@ -180,6 +131,8 @@ BootstrapTable.propTypes = {
|
||||
selectRow: PropTypes.shape({
|
||||
mode: PropTypes.oneOf([Const.ROW_SELECT_SINGLE, Const.ROW_SELECT_MULTIPLE]).isRequired
|
||||
}),
|
||||
onRowSelect: PropTypes.func,
|
||||
onAllRowsSelect: PropTypes.func,
|
||||
onCellUpdate: PropTypes.func,
|
||||
onStartEditing: PropTypes.func,
|
||||
onEscapeEditing: PropTypes.func,
|
||||
|
||||
24
packages/react-bootstrap-table2/src/container.js
vendored
24
packages/react-bootstrap-table2/src/container.js
vendored
@ -5,6 +5,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import Store from './store/base';
|
||||
import CellEditWrapper from './cell-edit/wrapper';
|
||||
import RowSelectionWrapper from './row-selection/wrapper';
|
||||
import _ from './utils';
|
||||
|
||||
const withDataStore = (Base) => {
|
||||
@ -35,10 +36,10 @@ const withDataStore = (Base) => {
|
||||
if (_.isObject(response)) {
|
||||
const { value } = response;
|
||||
this.store.edit(rowId, dataField, value || newValue);
|
||||
this.table.completeEditing();
|
||||
this.cellEditWrapper.completeEditing();
|
||||
}
|
||||
}).catch((e) => {
|
||||
this.table.updateEditingWithErr(e.message);
|
||||
this.cellEditWrapper.updateEditingWithErr(e.message);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
@ -49,13 +50,24 @@ const withDataStore = (Base) => {
|
||||
<CellEditWrapper
|
||||
keyField={ this.props.keyField }
|
||||
cellEdit={ this.props.cellEdit }
|
||||
ref={ node => this.table = node }
|
||||
ref={ node => this.cellEditWrapper = node }
|
||||
elem={ elem }
|
||||
onUpdateCell={ this.handleUpdateCell }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderRowSelection(elem) {
|
||||
return (
|
||||
<RowSelectionWrapper
|
||||
keyField={ this.props.keyField }
|
||||
selectRow={ this.props.selectRow }
|
||||
store={ this.store }
|
||||
elem={ elem }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const baseProps = {
|
||||
...this.props,
|
||||
@ -63,9 +75,15 @@ const withDataStore = (Base) => {
|
||||
};
|
||||
|
||||
let element = React.createElement(Base, baseProps);
|
||||
|
||||
if (this.props.selectRow) {
|
||||
element = this.renderRowSelection(element);
|
||||
}
|
||||
|
||||
if (this.props.cellEdit) {
|
||||
element = this.renderCellEdit(element);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
77
packages/react-bootstrap-table2/src/row-selection/wrapper.js
vendored
Normal file
77
packages/react-bootstrap-table2/src/row-selection/wrapper.js
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
/* eslint arrow-body-style: 0 */
|
||||
/* eslint react/prop-types: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Const from '../const';
|
||||
|
||||
class RowSelectionWrapper extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleRowSelect = this.handleRowSelect.bind(this);
|
||||
this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this);
|
||||
this.state = {
|
||||
selectedRowKeys: props.store.getSelectedRowKeys()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* row selection handler
|
||||
* @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;
|
||||
const { ROW_SELECT_SINGLE } = Const;
|
||||
|
||||
let currSelected = [...store.getSelectedRowKeys()];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
store.setSelectedRowKeys(currSelected);
|
||||
|
||||
this.setState(() => ({
|
||||
selectedRowKeys: currSelected
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* handle all rows selection on header cell by store.selected or given specific result.
|
||||
* @param {Boolean} option - customized result for all rows selection
|
||||
*/
|
||||
handleAllRowsSelect(option) {
|
||||
const { store } = this.props;
|
||||
const selected = store.isAnySelectedRow();
|
||||
|
||||
// set next status of all row selected by store.selected or customizing by user.
|
||||
const result = option || !selected;
|
||||
|
||||
const currSelected = result ? store.selectAllRowKeys() : [];
|
||||
|
||||
store.setSelectedRowKeys(currSelected);
|
||||
|
||||
this.setState(() => ({
|
||||
selectedRowKeys: currSelected
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.cloneElement(this.props.elem, {
|
||||
onRowSelect: this.handleRowSelect,
|
||||
onAllRowsSelect: this.handleAllRowsSelect
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RowSelectionWrapper.propTypes = {
|
||||
elem: PropTypes.element.isRequired,
|
||||
store: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default RowSelectionWrapper;
|
||||
@ -161,114 +161,4 @@ describe('BootstrapTable', () => {
|
||||
expect(body.props().cellEdit.onUpdate).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleRowSelect', () => {
|
||||
const rowKey = 1;
|
||||
|
||||
describe('when selectRow.mode is radio', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
store={ store }
|
||||
columns={ columns }
|
||||
data={ data }
|
||||
selectRow={{ mode: 'radio' }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('state.selectedRowKeys should contain only single key', () => {
|
||||
wrapper.instance().handleRowSelect(rowKey);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([rowKey]);
|
||||
|
||||
wrapper.instance().handleRowSelect(rowKey);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([rowKey]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selectRow.mode is checbox', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
store={ store }
|
||||
columns={ columns }
|
||||
data={ data }
|
||||
selectRow={{ mode: 'checkbox' }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
describe('if checked is false', () => {
|
||||
it('state.selectedRowKeys should pop selected row key', () => {
|
||||
wrapper.instance().handleRowSelect(rowKey, false);
|
||||
|
||||
expect(wrapper.state('selectedRowKeys')).not.toContain(rowKey);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if checked is true', () => {
|
||||
it('state.selectedRowKeys should push one extra key', () => {
|
||||
wrapper.instance().handleRowSelect(rowKey, true);
|
||||
|
||||
expect(wrapper.state('selectedRowKeys')).toContain(rowKey);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleAllRowsSelect', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
store={ store }
|
||||
columns={ columns }
|
||||
data={ data }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
describe('when customized option was not given', () => {
|
||||
describe('when nothing was selected', () => {
|
||||
it('should select all rows', () => {
|
||||
store.setSelectedRowKeys([]);
|
||||
|
||||
wrapper.instance().handleAllRowsSelect();
|
||||
|
||||
expect(wrapper.state('selectedRowKeys').length).toBe(data.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when one or more than one row was selected', () => {
|
||||
it('should unselect all rows', () => {
|
||||
store.setSelectedRowKeys([1]);
|
||||
|
||||
wrapper.instance().handleAllRowsSelect();
|
||||
|
||||
expect(wrapper.state('selectedRowKeys').length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when customized option was given', () => {
|
||||
describe('when option is truthy', () => {
|
||||
it('should select all rows', () => {
|
||||
wrapper.instance().handleAllRowsSelect(true);
|
||||
|
||||
expect(wrapper.state('selectedRowKeys').length).toBe(data.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when option is falsy', () => {
|
||||
it('should unselect all rows', () => {
|
||||
store.setSelectedRowKeys([1]);
|
||||
|
||||
wrapper.instance().handleAllRowsSelect(false);
|
||||
|
||||
expect(wrapper.state('selectedRowKeys').length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -0,0 +1,126 @@
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Store from '../../src/store/base';
|
||||
import BootstrapTable from '../../src/bootstrap-table';
|
||||
import RowSelectionWrapper from '../../src/row-selection/wrapper';
|
||||
|
||||
describe('RowSelectionWrapper', () => {
|
||||
let wrapper;
|
||||
let elem;
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Name'
|
||||
}];
|
||||
|
||||
const data = [{
|
||||
id: 1,
|
||||
name: 'A'
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'B'
|
||||
}];
|
||||
|
||||
const selectRow = {
|
||||
mode: 'radio'
|
||||
};
|
||||
|
||||
const keyField = 'id';
|
||||
|
||||
const store = new Store({ data, keyField });
|
||||
|
||||
beforeEach(() => {
|
||||
elem = React.createElement(BootstrapTable, { data, selectRow, columns, keyField, store });
|
||||
wrapper = shallow(
|
||||
<RowSelectionWrapper
|
||||
keyField={ keyField }
|
||||
selectRow={ selectRow }
|
||||
elem={ elem }
|
||||
store={ store }
|
||||
onUpdateCell={ sinon.stub() }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render RowSelectionWrapper correctly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find(BootstrapTable)).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have correct state', () => {
|
||||
expect(wrapper.state().selectedRowKeys).toBeDefined();
|
||||
expect(wrapper.state().selectedRowKeys.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should inject correct props to elem', () => {
|
||||
expect(wrapper.props().onRowSelect).toBeDefined();
|
||||
expect(wrapper.props().onAllRowsSelect).toBeDefined();
|
||||
});
|
||||
|
||||
describe('when selectRow.mode is \'radio\'', () => {
|
||||
const firstSelectedRow = data[0][keyField];
|
||||
const secondSelectedRow = data[1][keyField];
|
||||
|
||||
it('call handleRowSelect function should seting correct state.selectedRowKeys', () => {
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([firstSelectedRow]);
|
||||
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([secondSelectedRow]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selectRow.mode is \'checkbox\'', () => {
|
||||
const firstSelectedRow = data[0][keyField];
|
||||
const secondSelectedRow = data[1][keyField];
|
||||
|
||||
beforeEach(() => {
|
||||
selectRow.mode = 'checkbox';
|
||||
elem = React.createElement(BootstrapTable, { data, selectRow, columns, keyField, store });
|
||||
wrapper = shallow(
|
||||
<RowSelectionWrapper
|
||||
keyField={ keyField }
|
||||
selectRow={ selectRow }
|
||||
elem={ elem }
|
||||
store={ store }
|
||||
onUpdateCell={ sinon.stub() }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('call handleRowSelect function should seting correct state.selectedRowKeys', () => {
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow, true);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([firstSelectedRow]));
|
||||
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow, true);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([firstSelectedRow, secondSelectedRow]));
|
||||
|
||||
wrapper.instance().handleRowSelect(firstSelectedRow, false);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([secondSelectedRow]));
|
||||
|
||||
wrapper.instance().handleRowSelect(secondSelectedRow, false);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([]);
|
||||
});
|
||||
|
||||
it('call handleAllRowsSelect function should seting correct state.selectedRowKeys', () => {
|
||||
wrapper.instance().handleAllRowsSelect();
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([firstSelectedRow, secondSelectedRow]));
|
||||
|
||||
wrapper.instance().handleAllRowsSelect();
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([]);
|
||||
});
|
||||
|
||||
it('call handleAllRowsSelect function with a bool args should seting correct state.selectedRowKeys', () => {
|
||||
wrapper.instance().handleAllRowsSelect(true);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual(expect.arrayContaining([firstSelectedRow, secondSelectedRow]));
|
||||
|
||||
wrapper.instance().handleAllRowsSelect(false);
|
||||
expect(wrapper.state('selectedRowKeys')).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user