* implement row single and multiple selection

* radio button for single, checkbox for multiple
* update component if status was changing

* implement header cell for row selection

* render checkbox for multiple, nothing for single
* default css for th[data-th-row-selection]
* update component if status was changing

* transform cursor to pointer when hover button radio and checkbox

* story for single and multiple rows selection

* remove props required field and turn off eslint

* [test] adapt with other component

* props resolver for cell selection

* if row selection was disabled, return mode 'ROW_SELECT_DISABLED'

* refactor row selection cell

* rename to selection-cell
* remove unnecessary props
* better coding style

* props resolver for header cell selection

* refactor row selection for header cell

* rename to selection-header-cell
* remove unnecessary props
* better coding style
* new logic for handleSelectAllRows

* tunning for multi selection logic

* allow user to customize select all result
* remove cursor point
* remove uncessary utils

* tunning for function naming

* mock data for resolved props including both body and header

* judge cell-editable and row-selectable with mode

* [test] unit test for props-resolver

* move position of test case of cellEdit
* add test for resolveCellSelectionProps
* add test for resolveHeaderCellSelectionProps
* accept row keys for mock-component

* [test] add test for body

* [test] add test for header

* [test] add test for row

* [test] add test for selection-cell

* fix typo

* [test] add test for selection-header-cell

* add test for checkbox in selection-header-cell

* [test] add test for bootstrap-table

* test for handleRowSelect

* test for handleAllRowsSelect

* remove uncessary prop

* remove unnecessary dafault mode for selectRow

* add description for props shape
* remove uncessary declaration of inputType
* add isRequred for selectRow.mode

* [test] verify the correctness of params when clicking on selection cell

* [test] modification for test wording and unmatched data type

* handle logic of row selection inside the store

* ignore the situation of pagination
* correct the tests

* [test] add test for store/base.js

* Document for row selection

* modication for defects

* simplify proptypes to basic data type
* row selection document in README
* refactor all function test with sinon

* refactor all mock function to sinon.stub() instead jest.fn()

* fix conflict
This commit is contained in:
chunming 2017-10-11 10:28:53 -05:00 committed by Allen
parent 30d2645e4a
commit 877259158e
26 changed files with 1353 additions and 46 deletions

View File

@ -14,6 +14,7 @@
* [hover](#hover)
* [condensed](#condensed)
* [cellEdit](#cellEdit)
* [selectRow](#selectRow)
### <a name='keyField'>keyField(**required**) - [String]</a>
`keyField` is a prop to tell `react-bootstrap-table2` which column is unigue key.
@ -36,7 +37,7 @@ Same as `.table-hover` class for adding a hover effect (grey background color) o
### <a name='condensed'>condensed - [Bool]</a>
Same as `.table-condensed` class for makeing a table more compact by cutting cell padding in half
### <a name='cellEdit'>cellEdit - [Bool]</a>
### <a name='cellEdit'>cellEdit - [Object]</a>
Assign a valid `cellEdit` object can enable the cell editing on the cell. The default usage is click/dbclick to trigger cell editing and press `ENTER` to save cell or press `ESC` to cancel editing.
> Note: The `keyField` column can't be edited
@ -64,3 +65,6 @@ Default is `false`, enable it will be able to save the cell automatically when b
#### <a name='cellEdit.timeToCloseMessage'>cellEdit.timeToCloseMessage - [Function]</a>
If a [`column.validator`](./columns.md#validator) defined and the new value is invalid, `react-bootstrap-table2` will popup a alert at the bottom of editor. `cellEdit.timeToCloseMessage` is a chance to let you decide how long the alert should be stay. Default is 3000 millisecond.
### <a name='selectRow'>selectRow - [Object]</a>
Pass prop `selectRow` to enable row selection. For more detail, please navigate to [row selection document](./row-selection.md).

48
docs/row-selection.md Normal file
View File

@ -0,0 +1,48 @@
# Row selection
`react-bootstrap-table2` supports the row selection feature. By passing prop `selectRow ` to enable row selection. When you enable this feature, `react-bootstrap-table2` will append a new selection column at first.
## Available properties
The following are available properties in `selectRow`:
#### Required
* [mode (required)](#mode)
#### Optional
## <a name="mode">selectRow.mode - [String]</a>
Specifying the selection way for `single(radio)` or `multiple(checkbox)`. If `radio` was assigned, there will be a radio button in the selection column; otherwise, the `checkbox` instead.
#### values
* `radio`
* `checkbox`
#### examples
```js
const selectRowProp = {
mode: 'radio' // single row selection
};
<BootstrapTableful
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRowProp }
/>
```
```js
const selectRowProp = {
mode: 'checkbox' // multiple row selection
};
<BootstrapTableful
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRowProp }
/>
```

View File

@ -0,0 +1,53 @@
import React from 'react';
import { BootstrapTableful } 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 selectRowProp = {
mode: 'checkbox'
};
const sourceCode = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRowProp = {
mode: 'checkbox'
};
<BootstrapTableful
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRowProp }
/>
`;
export default () => (
<div>
<BootstrapTableful keyField="id" data={ products } columns={ columns } selectRow={ selectRowProp } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -0,0 +1,53 @@
import React from 'react';
import { BootstrapTableful } 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 selectRowProp = {
mode: 'radio'
};
const sourceCode = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRowProp = {
mode: 'radio'
};
<BootstrapTableful
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRowProp }
/>
`;
export default () => (
<div>
<BootstrapTableful keyField="id" data={ products } columns={ columns } selectRow={ selectRowProp } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -46,6 +46,10 @@ import CellLevelEditable from 'examples/cell-edit/cell-level-editable-table';
import CellEditHooks from 'examples/cell-edit/cell-edit-hooks-table';
import CellEditValidator from 'examples/cell-edit/cell-edit-validator-table';
// work on row selection
import SingleSelectionTable from 'examples/row-selection/single-selection';
import MultipleSelectionTable from 'examples/row-selection/multiple-selection';
// css style
import 'bootstrap/dist/css/bootstrap.min.css';
import 'stories/stylesheet/tomorrow.min.css';
@ -99,3 +103,7 @@ storiesOf('Cell Editing', module)
.add('Cell Level Editable', () => <CellLevelEditable />)
.add('Rich Hook Functions', () => <CellEditHooks />)
.add('Validation', () => <CellEditValidator />);
storiesOf('Row Selection', module)
.add('Single selection', () => <SingleSelectionTable />)
.add('Multiple selection', () => <MultipleSelectionTable />);

View File

@ -1,10 +1,13 @@
/* eslint react/prop-types: 0 */
/* eslint react/require-default-props: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import _ from './utils';
import Row from './row';
import RowSection from './row-section';
import Const from './const';
const Body = (props) => {
const {
@ -14,7 +17,9 @@ const Body = (props) => {
isEmpty,
noDataIndication,
visibleColumnSize,
cellEdit
cellEdit,
selectRow,
selectedRowKeys
} = props;
let content;
@ -25,7 +30,13 @@ const Body = (props) => {
} else {
content = data.map((row, index) => {
const key = _.get(row, keyField);
const editable = !(cellEdit && cellEdit.nonEditableRows.indexOf(key) > -1);
const editable = !(cellEdit.mode !== Const.UNABLE_TO_CELL_EDIT &&
cellEdit.nonEditableRows.indexOf(key) > -1);
const selected = selectRow.mode !== Const.ROW_SELECT_DISABLED
? selectedRowKeys.includes(key)
: null;
return (
<Row
key={ key }
@ -35,6 +46,8 @@ const Body = (props) => {
columns={ columns }
cellEdit={ cellEdit }
editable={ editable }
selected={ selected }
selectRow={ selectRow }
/>
);
});
@ -48,7 +61,9 @@ const Body = (props) => {
Body.propTypes = {
keyField: PropTypes.string.isRequired,
data: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired
columns: PropTypes.array.isRequired,
selectRow: PropTypes.object,
selectedRowKeys: PropTypes.array
};
export default Body;

View File

@ -22,8 +22,11 @@ class BootstrapTable extends PropsBaseResolver(Component) {
this.startEditing = this.startEditing.bind(this);
this.escapeEditing = this.escapeEditing.bind(this);
this.completeEditing = this.completeEditing.bind(this);
this.handleRowSelect = this.handleRowSelect.bind(this);
this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this);
this.state = {
data: this.store.get(),
selectedRowKeys: this.store.getSelectedRowKeys(),
currEditCell: {
ridx: null,
cidx: null
@ -56,6 +59,14 @@ class BootstrapTable extends PropsBaseResolver(Component) {
onComplete: this.completeEditing
});
const cellSelectionInfo = this.resolveCellSelectionProps({
onRowSelect: this.handleRowSelect
});
const headerCellSelectionInfo = this.resolveHeaderCellSelectionProps({
onAllRowsSelect: this.handleAllRowsSelect
});
return (
<div className="react-bootstrap-table-container">
<table className={ tableClass }>
@ -65,6 +76,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
sortField={ this.store.sortField }
sortOrder={ this.store.sortOrder }
onSort={ this.handleSort }
selectRow={ headerCellSelectionInfo }
/>
<Body
data={ this.state.data }
@ -74,12 +86,59 @@ class BootstrapTable extends PropsBaseResolver(Component) {
visibleColumnSize={ this.visibleColumnSize() }
noDataIndication={ noDataIndication }
cellEdit={ cellEditInfo }
selectRow={cellSelectionInfo}
selectedRowKeys={this.state.selectedRowKeys}
/>
</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 { mode } = this.props.selectRow;
const { ROW_SELECT_SINGLE } = Const;
let currSelected = [...this.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);
}
this.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 selected = this.store.isAnySelectedRow();
// set next status of all row selected by store.selected or customizing by user.
const result = option || !selected;
const currSelected = result ? this.store.selectAllRowKeys() : [];
this.store.setSelectedRowKeys(currSelected);
this.setState(() => ({
selectedRowKeys: currSelected
}));
}
handleSort(column) {
this.store.sortBy(column);
@ -146,6 +205,9 @@ BootstrapTable.propTypes = {
afterSaveCell: PropTypes.func,
nonEditableRows: PropTypes.func,
timeToCloseMessage: PropTypes.number
}),
selectRow: PropTypes.shape({
mode: PropTypes.oneOf([Const.ROW_SELECT_SINGLE, Const.ROW_SELECT_MULTIPLE]).isRequired
})
};

View File

@ -4,5 +4,11 @@ export default {
UNABLE_TO_CELL_EDIT: 'none',
CLICK_TO_CELL_EDIT: 'click',
DBCLICK_TO_CELL_EDIT: 'dbclick',
TIME_TO_CLOSE_MESSAGE: 3000
TIME_TO_CLOSE_MESSAGE: 3000,
ROW_SELECT_SINGLE: 'radio',
ROW_SELECT_MULTIPLE: 'checkbox',
ROW_SELECT_DISABLED: 'ROW_SELECT_DISABLED',
CHECKBOX_STATUS_CHECKED: 'checked',
CHECKBOX_STATUS_INDETERMINATE: 'indeterminate',
CHECKBOX_STATUS_UNCHECKED: 'unchecked'
};

View File

@ -1,20 +1,28 @@
/* eslint react/require-default-props: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import Const from './const';
import HeaderCell from './header-cell';
import SelectionHeaderCell from './row-selection/selection-header-cell';
const Header = (props) => {
const { ROW_SELECT_DISABLED } = Const;
const {
columns,
onSort,
sortField,
sortOrder
sortOrder,
selectRow
} = props;
return (
<thead>
<tr>
{
selectRow.mode === ROW_SELECT_DISABLED ? null : <SelectionHeaderCell {...selectRow} />
}
{
columns.map((column, i) => {
const currSort = column.dataField === sortField;
@ -38,7 +46,8 @@ Header.propTypes = {
columns: PropTypes.array.isRequired,
onSort: PropTypes.func,
sortField: PropTypes.string,
sortOrder: PropTypes.string
sortOrder: PropTypes.string,
selectRow: PropTypes.object
};
export default Header;

View File

@ -40,4 +40,65 @@ export default ExtendBase =>
...cellEditInfo
};
}
/**
* props resolver for cell selection
* @param {Object} options - addtional options like callback which are about to merge into props
*
* @returns {Object} result - props for cell selections
* @returns {String} result.mode - input type of row selection or disabled.
*/
resolveCellSelectionProps(options) {
const { selectRow } = this.props;
const { ROW_SELECT_DISABLED } = Const;
if (_.isDefined(selectRow)) {
return {
...selectRow,
...options
};
}
return {
mode: ROW_SELECT_DISABLED
};
}
/**
* props resolver for header cell selection
* @param {Object} options - addtional options like callback which are about to merge into props
*
* @returns {Object} result - props for cell selections
* @returns {String} result.mode - input type of row selection or disabled.
* @returns {String} result.checkedStatus - checkbox status depending on selected rows counts
*/
resolveHeaderCellSelectionProps(options) {
const { selected } = this.store;
const { selectRow } = this.props;
const {
ROW_SELECT_DISABLED, CHECKBOX_STATUS_CHECKED,
CHECKBOX_STATUS_INDETERMINATE, CHECKBOX_STATUS_UNCHECKED
} = Const;
if (_.isDefined(selectRow)) {
let checkedStatus;
const allRowsSelected = this.store.isAllRowsSelected();
// checkbox status depending on selected rows counts
if (allRowsSelected) checkedStatus = CHECKBOX_STATUS_CHECKED;
else if (selected.length === 0) checkedStatus = CHECKBOX_STATUS_UNCHECKED;
else checkedStatus = CHECKBOX_STATUS_INDETERMINATE;
return {
...selectRow,
...options,
checkedStatus
};
}
return {
mode: ROW_SELECT_DISABLED
};
}
};

View File

@ -0,0 +1,59 @@
/* eslint
react/require-default-props: 0
jsx-a11y/no-noninteractive-element-interactions: 0
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Const from '../const';
export default class SelectionCell extends Component {
static propTypes = {
mode: PropTypes.string.isRequired,
rowKey: PropTypes.any,
selected: PropTypes.bool,
onRowSelect: PropTypes.func
}
constructor() {
super();
this.handleRowClick = this.handleRowClick.bind(this);
}
shouldComponentUpdate(nextProps) {
const { selected } = this.props;
return nextProps.selected !== selected;
}
handleRowClick() {
const { ROW_SELECT_SINGLE } = Const;
const {
mode: inputType,
rowKey,
selected,
onRowSelect
} = this.props;
const checked = inputType === ROW_SELECT_SINGLE
? true
: !selected;
onRowSelect(rowKey, checked);
}
render() {
const {
mode: inputType,
selected
} = this.props;
return (
<td onClick={this.handleRowClick}>
<input
type={inputType}
checked={selected}
/>
</td>
);
}
}

View File

@ -0,0 +1,76 @@
/* eslint react/require-default-props: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Constant from '../const';
export const CheckBox = ({ checked, indeterminate }) => (
<input
type="checkbox"
checked={checked}
ref={(input) => {
if (input) input.indeterminate = indeterminate; // eslint-disable-line no-param-reassign
}}
/>
);
CheckBox.propTypes = {
checked: PropTypes.bool.isRequired,
indeterminate: PropTypes.bool.isRequired
};
export default class SelectionHeaderCell extends Component {
static propTypes = {
mode: PropTypes.string.isRequired,
checkedStatus: PropTypes.string,
onAllRowsSelect: PropTypes.func
}
constructor() {
super();
this.handleCheckBoxClick = this.handleCheckBoxClick.bind(this);
}
/**
* avoid updating if button is
* 1. radio
* 2. status was not changed.
*/
shouldComponentUpdate(nextProps) {
const { ROW_SELECT_SINGLE } = Constant;
const { mode, checkedStatus } = this.props;
if (mode === ROW_SELECT_SINGLE) return false;
return nextProps.checkedStatus !== checkedStatus;
}
handleCheckBoxClick() {
const { onAllRowsSelect } = this.props;
onAllRowsSelect();
}
render() {
const {
CHECKBOX_STATUS_CHECKED, CHECKBOX_STATUS_INDETERMINATE, ROW_SELECT_SINGLE
} = Constant;
const { mode, checkedStatus } = this.props;
const checked = checkedStatus === CHECKBOX_STATUS_CHECKED;
const indeterminate = checkedStatus === CHECKBOX_STATUS_INDETERMINATE;
return mode === ROW_SELECT_SINGLE
? <th data-row-selection />
: (
<th data-row-selection onClick={this.handleCheckBoxClick}>
<CheckBox
{...this.props}
checked={checked}
indeterminate={indeterminate}
/>
</th>
);
}
}

View File

@ -4,17 +4,24 @@ import PropTypes from 'prop-types';
import _ from './utils';
import Cell from './cell';
import SelectionCell from './row-selection/selection-cell';
import EditingCell from './editing-cell';
import Const from './const';
const Row = (props) => {
const { ROW_SELECT_DISABLED } = Const;
const {
row,
columns,
keyField,
rowIndex,
cellEdit,
selected,
selectRow,
editable: editableRow
} = props;
const {
mode,
onStart,
@ -22,8 +29,20 @@ const Row = (props) => {
cidx: editingColIdx,
...rest
} = cellEdit;
return (
<tr>
{
selectRow.mode === ROW_SELECT_DISABLED
? null
: (
<SelectionCell
{ ...selectRow }
rowKey={_.get(row, keyField)}
selected={selected}
/>
)
}
{
columns.map((column, index) => {
const { dataField } = column;

View File

@ -10,6 +10,7 @@ export default class Store {
this.sortOrder = undefined;
this.sortField = undefined;
this.selected = [];
}
isEmpty() {
@ -39,4 +40,24 @@ export default class Store {
getRowByRowId(rowId) {
return this.get().find(row => _.get(row, this.keyField) === rowId);
}
setSelectedRowKeys(selectedKeys) {
this.selected = selectedKeys;
}
getSelectedRowKeys() {
return this.selected;
}
selectAllRowKeys() {
return this.data.map(row => _.get(row, this.keyField));
}
isAllRowsSelected() {
return this.data.length === this.selected.length;
}
isAnySelectedRow() {
return this.selected.length > 0;
}
}

View File

@ -22,6 +22,10 @@
margin: 10px 6.5px;
}
th[data-row-selection] {
width: 30px;
}
td.react-bs-table-no-data {
text-align: center;
}

View File

@ -6,6 +6,7 @@ import Body from '../src/body';
import Row from '../src/row';
import Const from '../src/const';
import RowSection from '../src/row-section';
import mockBodyResolvedProps from '../test/mock-data/body-resolved-props';
describe('Body', () => {
let wrapper;
@ -27,7 +28,7 @@ describe('Body', () => {
describe('simplest body', () => {
beforeEach(() => {
wrapper = shallow(<Body keyField="id" columns={ columns } data={ data } />);
wrapper = shallow(<Body {...mockBodyResolvedProps} keyField="id" columns={ columns } data={ data } />);
});
it('should render successfully', () => {
@ -41,6 +42,7 @@ describe('Body', () => {
beforeEach(() => {
wrapper = shallow(
<Body
{...mockBodyResolvedProps}
keyField="id"
columns={ columns }
data={ data }
@ -65,6 +67,7 @@ describe('Body', () => {
emptyIndication = 'Table is empty';
wrapper = shallow(
<Body
{...mockBodyResolvedProps}
keyField="id"
columns={ columns }
data={ data }
@ -90,6 +93,7 @@ describe('Body', () => {
emptyIndicationCallBack = sinon.stub().returns(content);
wrapper = shallow(
<Body
{...mockBodyResolvedProps}
keyField="id"
columns={ columns }
data={ data }
@ -123,6 +127,7 @@ describe('Body', () => {
beforeEach(() => {
wrapper = shallow(
<Body
{...mockBodyResolvedProps}
data={ data }
columns={ columns }
keyField={ keyField }
@ -143,4 +148,58 @@ describe('Body', () => {
}
});
});
describe('when selectRow.mode is checkbox or radio (row was selectable)', () => {
const keyField = 'id';
const selectRow = { mode: 'checkbox' };
it('props selected should be true if all rows were selected', () => {
wrapper = shallow(
<Body
{...mockBodyResolvedProps}
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={[1, 2]}
selectRow={selectRow}
/>
);
expect(wrapper.find(Row).get(0).props.selected).toBe(true);
});
it('props selected should be false if all rows were not selected', () => {
wrapper = shallow(
<Body
{...mockBodyResolvedProps}
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={[]}
selectRow={selectRow}
/>
);
expect(wrapper.find(Row).get(0).props.selected).toBe(false);
});
});
describe('when selectRow.mode is ROW_SELECT_DISABLED (row was un-selectable)', () => {
beforeEach(() => {
const keyField = 'id';
wrapper = shallow(
<Body
{...mockBodyResolvedProps}
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={[]}
/>
);
});
it('prop selected should be null', () => {
expect(wrapper.find(Row).get(0).props.selected).toBeNull();
});
});
});

View File

@ -140,4 +140,111 @@ describe('BootstrapTable', () => {
expect(body.props().cellEdit.onComplete).toBeDefined();
});
});
describe('handleRowSelect', () => {
const rowKey = 1;
describe('when selectRow.mode is radio', () => {
beforeEach(() => {
wrapper = shallow(
<BootstrapTable
keyField="id"
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"
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"
columns={ columns }
data={ data }
/>
);
});
describe('when customized option was not given', () => {
describe('when nothing was selected', () => {
it('should select all rows', () => {
wrapper.instance().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', () => {
wrapper.instance().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', () => {
wrapper.instance().store.setSelectedRowKeys([1]);
wrapper.instance().handleAllRowsSelect(false);
expect(wrapper.state('selectedRowKeys').length).toBe(0);
});
});
});
});
});

View File

@ -2,8 +2,10 @@ import React from 'react';
import { shallow } from 'enzyme';
import HeaderCell from '../src/header-cell';
import SelectionHeaderCell from '../src//row-selection/selection-header-cell';
import Header from '../src/header';
import Const from '../src/const';
import mockHeaderResolvedProps from '../test/mock-data/header-resolved-props';
describe('Header', () => {
let wrapper;
@ -17,7 +19,7 @@ describe('Header', () => {
describe('simplest header', () => {
beforeEach(() => {
wrapper = shallow(<Header columns={ columns } />);
wrapper = shallow(<Header {...mockHeaderResolvedProps} columns={ columns } />);
});
it('should render successfully', () => {
@ -32,7 +34,12 @@ describe('Header', () => {
beforeEach(() => {
wrapper = shallow(
<Header columns={ columns } sortField={ sortField } sortOrder={ Const.SORT_ASC } />);
<Header
{...mockHeaderResolvedProps}
columns={ columns }
sortField={ sortField }
sortOrder={ Const.SORT_ASC }
/>);
});
it('The HeaderCell should receive correct sorting props', () => {
@ -43,4 +50,31 @@ describe('Header', () => {
expect(headerCells.at(1).prop('sortOrder')).toBe(Const.SORT_ASC);
});
});
describe('when the selectRow.mode is radio(single selection)', () => {
beforeEach(() => {
wrapper = shallow(<Header {...mockHeaderResolvedProps} columns={ columns } />);
});
it('should not render <SelectionHeaderCell />', () => {
expect(wrapper.find(SelectionHeaderCell).length).toBe(0);
});
});
describe('when the selectRow.mode is checkbox(multiple selection)', () => {
beforeEach(() => {
const selectRow = { mode: 'checkbox' };
wrapper = shallow(
<Header
{...mockHeaderResolvedProps}
columns={ columns }
selectRow={selectRow}
/>
);
});
it('should render <SelectionHeaderCell />', () => {
expect(wrapper.find(SelectionHeaderCell).length).toBe(1);
});
});
});

View File

@ -0,0 +1,16 @@
import Const from '../../src/const';
const { ROW_SELECT_DISABLED, UNABLE_TO_CELL_EDIT } = Const;
export const cellSelectionResolvedProps = {
mode: ROW_SELECT_DISABLED
};
export const cellEditResolvedProps = {
mode: UNABLE_TO_CELL_EDIT
};
export default {
cellEdit: cellEditResolvedProps,
selectRow: cellSelectionResolvedProps
};

View File

@ -0,0 +1,11 @@
import Const from '../../src/const';
const { ROW_SELECT_DISABLED } = Const;
export const headerCellSelectionResolvedProps = {
mode: ROW_SELECT_DISABLED
};
export default {
selectRow: headerCellSelectionResolvedProps
};

View File

@ -22,6 +22,7 @@ describe('TableResolver', () => {
id: 2,
name: 'B'
}];
const ExtendBase = baseResolver(Component);
const BootstrapTableMock = extendTo(ExtendBase);
let wrapper;
@ -99,7 +100,6 @@ describe('TableResolver', () => {
expect(cellEdit.cidx).toEqual(cidx);
});
});
});
describe('if cellEdit prop defined', () => {
const expectNonEditableRows = [1, 2];
@ -143,4 +143,221 @@ describe('TableResolver', () => {
expect(cellEditInfo.cb).toEqual(something.cb);
});
});
});
describe('resolveCellSelectionProps', () => {
let cellSelectionInfo;
let selectRow;
describe('if selectRow was not defined', () => {
beforeEach(() => {
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveCellSelectionProps();
});
it('should return object', () => {
expect(cellSelectionInfo).toBeDefined();
expect(cellSelectionInfo.constructor).toEqual(Object);
});
it('should contain mode in ROW_SELECT_DISABLED', () => {
expect(cellSelectionInfo.mode).toEqual(Const.ROW_SELECT_DISABLED);
});
});
describe('if selectRow was defined', () => {
describe('when mode was defined', () => {
it('should return object which contains ROW_SELECT_SINGLE if mode is radio', () => {
selectRow = { mode: 'radio' };
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveCellSelectionProps();
expect(cellSelectionInfo).toBeDefined();
expect(cellSelectionInfo.constructor).toEqual(Object);
expect(cellSelectionInfo.mode).toEqual(Const.ROW_SELECT_SINGLE);
});
it('should return object which contains ROW_SELECT_MULTIPLE if mode is checkbox', () => {
selectRow = { mode: 'checkbox' };
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveCellSelectionProps();
expect(cellSelectionInfo).toBeDefined();
expect(cellSelectionInfo.constructor).toEqual(Object);
expect(cellSelectionInfo.mode).toEqual(Const.ROW_SELECT_MULTIPLE);
});
});
describe('when options were given', () => {
beforeEach(() => {
selectRow = {};
const mockOptions = {
foo: 'test',
bar: sinon.stub()
};
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveCellSelectionProps(mockOptions);
});
it('should return object which contain options', () => {
expect(cellSelectionInfo).toEqual(expect.objectContaining({
foo: 'test',
bar: expect.any(Function)
}));
});
});
});
});
describe('resolveHeaderCellSelectionProps', () => {
let headerCellSelectionInfo;
let selectRow;
beforeEach(() => {
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveHeaderCellSelectionProps();
});
describe('if selectRow was not defined', () => {
it('should return object', () => {
expect(headerCellSelectionInfo).toBeDefined();
expect(headerCellSelectionInfo.constructor).toEqual(Object);
});
it('should contain mode in ROW_SELECT_DISABLED', () => {
expect(headerCellSelectionInfo.mode).toEqual(Const.ROW_SELECT_DISABLED);
});
});
describe('if selectRow was defined', () => {
describe('when mode was defined', () => {
it('should return object which contains ROW_SELECT_SINGLE if mode is radio', () => {
selectRow = { mode: 'radio' };
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectedRowKeys, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveHeaderCellSelectionProps();
expect(headerCellSelectionInfo).toBeDefined();
expect(headerCellSelectionInfo.constructor).toEqual(Object);
expect(headerCellSelectionInfo.mode).toEqual(Const.ROW_SELECT_SINGLE);
});
it('should return object which contains ROW_SELECT_MULTIPLE if mode is checkbox', () => {
selectRow = { mode: 'checkbox' };
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectedRowKeys, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveHeaderCellSelectionProps();
expect(headerCellSelectionInfo).toBeDefined();
expect(headerCellSelectionInfo.constructor).toEqual(Object);
expect(headerCellSelectionInfo.mode).toEqual(Const.ROW_SELECT_MULTIPLE);
});
});
describe('when options were given', () => {
beforeEach(() => {
selectRow = {};
const mockOptions = {
foo: 'test',
bar: sinon.stub()
};
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectedRowKeys, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveHeaderCellSelectionProps(mockOptions);
});
it('should return object which contain options', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
foo: 'test',
bar: expect.any(Function)
}));
});
});
describe('if all rows were selected', () => {
beforeEach(() => {
selectRow = {};
const selectedRowKeys = [1, 2];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
wrapper.instance().store.setSelectedRowKeys(selectedRowKeys);
headerCellSelectionInfo = wrapper.instance().resolveHeaderCellSelectionProps();
});
it('should return checkedStatus which eqauls to checked', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
checkedStatus: Const.CHECKBOX_STATUS_CHECKED
}));
});
});
describe('if part of rows were selected', () => {
beforeEach(() => {
selectRow = {};
const selectedRowKeys = [1];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
wrapper.instance().store.setSelectedRowKeys(selectedRowKeys);
headerCellSelectionInfo = wrapper.instance().resolveHeaderCellSelectionProps();
});
it('should return checkedStatus which eqauls to indeterminate', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
checkedStatus: Const.CHECKBOX_STATUS_INDETERMINATE
}));
});
});
describe('if none of row was selected', () => {
beforeEach(() => {
selectRow = {};
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
wrapper.instance().store.setSelectedRowKeys(selectedRowKeys);
headerCellSelectionInfo = wrapper.instance().resolveHeaderCellSelectionProps();
});
it('should return checkedStatus which eqauls to unchecked', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
checkedStatus: Const.CHECKBOX_STATUS_UNCHECKED
}));
});
});
});
});
});

View File

@ -0,0 +1,136 @@
import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import SelectionCell from '../../src/row-selection/selection-cell';
describe('<SelectionCell />', () => {
const mode = 'checkbox';
let wrapper;
describe('shouldComponentUpdate', () => {
const selected = true;
describe('when selected prop has not been changed', () => {
it('should not update component', () => {
const nextProps = { selected };
wrapper = shallow(<SelectionCell rowKey={1} mode={mode} selected={selected} />);
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(false);
});
});
describe('when selected prop has been changed', () => {
it('should update component', () => {
const nextProps = { selected: !selected };
wrapper = shallow(<SelectionCell rowKey={1} mode={mode} selected={selected} />);
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
});
describe('handleRowClick', () => {
describe('when <input /> was been clicked', () => {
const rowKey = 1;
const mockOnRowSelect = sinon.stub();
const spy = sinon.spy(SelectionCell.prototype, 'handleRowClick');
beforeEach(() => {
spy.reset();
mockOnRowSelect.reset();
});
it('should call handleRowClicked', () => {
wrapper = shallow(
<SelectionCell
selected
rowKey={rowKey}
mode={mode}
onRowSelect={mockOnRowSelect}
/>
);
wrapper.find('td').simulate('click');
expect(spy.calledOnce).toBe(true);
expect(mockOnRowSelect.calledOnce).toBe(true);
});
describe('if selectRow.mode is radio', () => {
beforeEach(() => {
wrapper = shallow(
<SelectionCell
selected
rowKey={rowKey}
mode="radio"
onRowSelect={mockOnRowSelect}
/>
);
});
it('should be called with correct paramters', () => {
// first click
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(1);
expect(mockOnRowSelect.calledWith(rowKey, true)).toBe(true);
// second click
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(2);
expect(mockOnRowSelect.calledWith(rowKey, true)).toBe(true);
});
});
describe('if selectRow.mode is checkbox', () => {
beforeEach(() => {
wrapper = shallow(
<SelectionCell
rowKey={rowKey}
mode="checkbox"
onRowSelect={mockOnRowSelect}
/>
);
});
it('should be called with correct paramters', () => {
// first click
wrapper.setProps({ selected: true });
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(1);
expect(mockOnRowSelect.calledWith(rowKey, false)).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);
});
});
});
});
describe('render', () => {
const selected = true;
beforeEach(() => {
wrapper = shallow(
<SelectionCell
rowKey={1}
mode={mode}
selected={selected}
/>
);
});
it('should render component correctly', () => {
expect(wrapper.find('td').length).toBe(1);
expect(wrapper.find('input').length).toBe(1);
expect(wrapper.find('input').get(0).props.type).toBe(mode);
expect(wrapper.find('input').get(0).props.checked).toBe(selected);
});
});
});

View File

@ -0,0 +1,144 @@
import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import Constant from '../../src/const';
import SelectionHeaderCell, { CheckBox } from '../../src/row-selection/selection-header-cell';
let wrapper;
describe('<SelectionHeaderCell />', () => {
describe('shouldComponentUpdate', () => {
describe('when props.mode is radio', () => {
it('should not update component', () => {
wrapper = shallow(<SelectionHeaderCell mode="radio" />);
expect(wrapper.instance().shouldComponentUpdate({})).toBe(false);
});
});
describe('when props.mode is checkbox', () => {
describe('if checkedStatus prop has not been changed', () => {
it('should not update component', () => {
const checkedStatus = Constant.CHECKBOX_STATUS_CHECKED;
const nextProps = { checkedStatus };
wrapper = shallow(
<SelectionHeaderCell mode="checkbox" checkedStatus={checkedStatus} />);
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(false);
});
});
describe('if checkedStatus prop has been changed', () => {
it('should update component', () => {
const { CHECKBOX_STATUS_INDETERMINATE, CHECKBOX_STATUS_CHECKED } = Constant;
const checkedStatus = CHECKBOX_STATUS_CHECKED;
const nextProps = { checkedStatus };
wrapper = shallow(
<SelectionHeaderCell mode="checkbox" checkedStatus={CHECKBOX_STATUS_INDETERMINATE} />);
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
});
});
describe('handleCheckBoxClick', () => {
describe('when <th /> was clicked', () => {
const spy = sinon.spy(SelectionHeaderCell.prototype, 'handleCheckBoxClick');
const mockOnAllRowsSelect = sinon.stub();
beforeEach(() => {
spy.reset();
mockOnAllRowsSelect.reset();
});
describe('if props.mode is radio', () => {
beforeEach(() => {
wrapper = shallow(
<SelectionHeaderCell
mode="radio"
checkedStatus={Constant.CHECKBOX_STATUS_CHECKED}
onAllRowsSelect={mockOnAllRowsSelect}
/>);
});
it('should do nothing', () => {
wrapper.find('th').simulate('click');
expect(spy.callCount).toBe(0);
expect(mockOnAllRowsSelect.callCount).toBe(0);
});
});
describe('if props.mode is checkbox', () => {
beforeEach(() => {
wrapper = shallow(
<SelectionHeaderCell
mode="checkbox"
checkedStatus={Constant.CHECKBOX_STATUS_CHECKED}
onAllRowsSelect={mockOnAllRowsSelect}
/>);
});
it('should call handleCheckBoxClick', () => {
wrapper.find('th').simulate('click');
expect(spy.calledOnce).toBe(true);
expect(mockOnAllRowsSelect.calledOnce).toBe(true);
});
});
});
});
describe('render', () => {
describe('when props.mode is radio', () => {
beforeEach(() => {
const checkedStatus = Constant.CHECKBOX_STATUS_CHECKED;
wrapper = shallow(<SelectionHeaderCell mode="radio" checkedStatus={checkedStatus} />);
});
it('should not render checkbox', () => {
expect(wrapper.find('th').length).toBe(1);
expect(wrapper.find('th[data-row-selection]').length).toBe(1);
expect(wrapper.find(CheckBox).length).toBe(0);
});
});
describe('when props.mode is checkbox', () => {
const checkedStatus = Constant.CHECKBOX_STATUS_CHECKED;
beforeEach(() => {
wrapper = shallow(<SelectionHeaderCell mode="checkbox" checkedStatus={checkedStatus} />);
});
it('should render checkbox', () => {
const checked = checkedStatus === Constant.CHECKBOX_STATUS_CHECKED;
const indeterminate = checkedStatus === Constant.CHECKBOX_STATUS_INDETERMINATE;
expect(wrapper.find('th').length).toBe(1);
expect(wrapper.find('th[data-row-selection]').length).toBe(1);
expect(wrapper.find(CheckBox).length).toBe(1);
expect(wrapper.find(CheckBox).get(0).props.checked).toBe(checked);
expect(wrapper.find(CheckBox).get(0).props.indeterminate).toBe(indeterminate);
});
});
});
});
describe('<CheckBox />', () => {
describe('render', () => {
it('should render component correctly', () => {
const checked = true;
const indeterminate = false;
wrapper = shallow(<CheckBox checked={checked} indeterminate={indeterminate} />);
expect(wrapper.find('input').length).toBe(1);
expect(wrapper.find('input').prop('checked')).toBe(checked);
expect(wrapper.find('input').prop('type')).toBe('checkbox');
});
});
});

View File

@ -6,6 +6,8 @@ import Cell from '../src/cell';
import Row from '../src/row';
import Const from '../src/const';
import EditingCell from '../src/editing-cell';
import SelectionCell from '../src//row-selection/selection-cell';
import mockBodyResolvedProps from '../test/mock-data/body-resolved-props';
const defaultColumns = [{
dataField: 'id',
@ -30,7 +32,7 @@ describe('Row', () => {
describe('simplest row', () => {
beforeEach(() => {
wrapper = shallow(
<Row rowIndex={ 1 } columns={ defaultColumns } row={ row } cellEdit={ {} } />);
<Row {...mockBodyResolvedProps} rowIndex={ 1 } columns={ defaultColumns } row={ row } />);
});
it('should render successfully', () => {
@ -53,6 +55,7 @@ describe('Row', () => {
};
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
row={ row }
rowIndex={ rowIndex }
columns={ columns }
@ -92,6 +95,7 @@ describe('Row', () => {
columns[nonEditableColIndex].editable = false;
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
row={ row }
rowIndex={ rowIndex }
columns={ columns }
@ -128,6 +132,7 @@ describe('Row', () => {
columns[nonEditableColIndex].editable = editableCallBack;
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
row={ row }
rowIndex={ rowIndex }
columns={ columns }
@ -160,6 +165,7 @@ describe('Row', () => {
columns[nonEditableColIndex].editable = editableCallBack;
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
row={ row }
rowIndex={ rowIndex }
columns={ columns }
@ -193,6 +199,7 @@ describe('Row', () => {
beforeEach(() => {
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
row={ row }
rowIndex={ rowIndex }
columns={ columns }
@ -222,6 +229,7 @@ describe('Row', () => {
cellEdit.onEscape = sinon.stub();
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
row={ row }
rowIndex={ 1 }
columns={ columns }
@ -233,9 +241,12 @@ describe('Row', () => {
});
it('should render EditingCell correctly', () => {
const complexComponents = wrapper.find('tr').children().findWhere(
n => n.type().name === 'Cell' || n.type().name === 'EditingCell');
expect(wrapper.length).toBe(1);
expect(wrapper.find(EditingCell).length).toBe(1);
expect(wrapper.find('tr').children().at(editingColIndex).type()).toEqual(EditingCell);
expect(complexComponents.at(editingColIndex).type()).toEqual(EditingCell);
});
});
@ -248,6 +259,7 @@ describe('Row', () => {
cellEdit.onEscape = sinon.stub();
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
row={ row }
rowIndex={ 1 }
columns={ columns }
@ -266,4 +278,33 @@ describe('Row', () => {
});
});
});
describe('when selectRow.mode is ROW_SELECT_DISABLED (row was un-selectable)', () => {
beforeEach(() => {
wrapper = shallow(
<Row {...mockBodyResolvedProps} rowIndex={ 1 } columns={ defaultColumns } row={ row } />);
});
it('should not render <SelectionCell />', () => {
expect(wrapper.find(SelectionCell).length).toBe(0);
});
});
describe('when selectRow.mode is checkbox or radio (row was selectable)', () => {
beforeEach(() => {
const selectRow = { mode: 'checkbox' };
wrapper = shallow(
<Row
{...mockBodyResolvedProps}
rowIndex={ 1 }
columns={ defaultColumns }
row={ row }
selectRow={selectRow}
/>);
});
it('should render <SelectionCell />', () => {
expect(wrapper.find(SelectionCell).length).toBe(1);
});
});
});

View File

@ -1,5 +1,6 @@
import Base from '../../src/store/base';
import Const from '../../src/const';
import _ from '../../src/utils';
describe('Store Base', () => {
let store;
@ -109,4 +110,41 @@ describe('Store Base', () => {
}).toThrow();
});
});
describe('selectAllRowKeys', () => {
it('should return all row keys', () => {
const rowKeys = store.selectAllRowKeys();
expect(Array.isArray(rowKeys)).toBeTruthy();
expect(rowKeys).toEqual([3, 2, 4, 1]);
});
});
describe('isAllRowsSelected', () => {
it('should return true when all rows was selected', () => {
store.selected = data.map(row => _.get(row, store.keyField));
expect(store.isAllRowsSelected()).toBeTruthy();
});
it('should return false when all rows was not selected', () => {
store.selected = [1];
expect(store.isAllRowsSelected()).not.toBeTruthy();
});
});
describe('isAnySelectedRow', () => {
it('should return true when one or more than one rows were selected', () => {
store.selected = data.map(row => _.get(row, store.keyField));
expect(store.isAnySelectedRow()).toBeTruthy();
});
it('should return false when none was selected', () => {
store.selected = [];
expect(store.isAnySelectedRow()).not.toBeTruthy();
});
});
});

View File

@ -1,9 +1,15 @@
import Store from '../../src/store/base';
export const extendTo = Base =>
class MockComponent extends Base {
constructor(props) {
super(props);
const { data } = props;
this.store = new Store(props);
this.state = {
data: this.props.data,
data,
currEditCell: {
ridx: null,
cidx: null