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:
Allen 2017-10-17 10:23:36 -05:00 committed by GitHub
parent 6887c12d11
commit 0ca6335d92
5 changed files with 230 additions and 166 deletions

View File

@ -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,

View File

@ -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;
}
};

View 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;

View File

@ -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);
});
});
});
});
});

View File

@ -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([]);
});
});
});