* implement row seleciton style and class

* add testing for row selection style and class

* refine select row test

* add stories for row selection style and class

* add docs for row selection style and class

* patch for wrong docs
This commit is contained in:
Allen 2017-10-20 00:44:25 -05:00 committed by GitHub
parent cb6410bbe4
commit 10f06dca10
12 changed files with 424 additions and 33 deletions

View File

@ -478,7 +478,7 @@ Or take a callback function
}
```
## <a name='editCellClasses'>column.editCellClasses - [Object | Function]</a>
## <a name='editCellClasses'>column.editCellClasses - [String | Function]</a>
You can use `column.editCellClasses` to add custom class on `<td>` when cell editing. It's same as [`column.editCellStyle`](#editCellStyle) which also accept a callback function to able to custom your class more flexible. Following is the arguments of this callback function: `cell`, `row`, `rowIndex`, `colIndex`.
```js

View File

@ -9,6 +9,8 @@ The following are available properties in `selectRow`:
#### Required
* [mode (**required**)](#mode)
* [style](#style)
* [classes)](#classes)
#### Optional
@ -46,3 +48,41 @@ const selectRow = {
selectRow={ selectRowProp }
/>
```
## <a name='style'>selectRow.style - [Object | Function]</a>
`selectRow.style` allow you to have custom style on selected rows:
```js
const selectRow = {
mode: 'checkbox',
style: { background: 'red' }
};
```
If you wanna more flexible customization, `selectRow.style` also accept a function:
```js
const selectRow = {
mode: 'checkbox',
style: (row, rowIndex) => { return ...; }
};
```
## <a name='classes'>selectRow.classes - [String | Function]</a>
`selectRow.classes` allow you to add css class on selected rows:
```js
const selectRow = {
mode: 'checkbox',
classes: 'custom-class'
};
```
If you wanna more flexible customization, `selectRow.classes` also accept a function:
```js
const selectRow = {
mode: 'checkbox',
classes: (row, rowIndex) => { return ...; }
};
```

View File

@ -0,0 +1,90 @@
/* eslint no-unused-vars: 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 selectRow1 = {
mode: 'checkbox',
classes: 'selection-row'
};
const selectRow2 = {
mode: 'checkbox',
classes: (row, rowIndex) =>
(rowIndex > 1 ? 'row-index-bigger-than-2101' : 'row-index-small-than-2101')
};
const sourceCode1 = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
classes: 'selection-row'
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
const sourceCode2 = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
classes: (row, rowIndex) =>
(rowIndex > 1 ? 'row-index-bigger-than-2101' : 'row-index-small-than-2101')
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow1 } />
<Code>{ sourceCode1 }</Code>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow2 } />
<Code>{ sourceCode2 }</Code>
</div>
);

View File

@ -0,0 +1,94 @@
/* eslint no-unused-vars: 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 selectRow1 = {
mode: 'checkbox',
style: { backgroundColor: '#c8e6c9' }
};
const selectRow2 = {
mode: 'checkbox',
style: (row, rowIndex) => {
const backgroundColor = rowIndex > 1 ? '#00BFFF' : '#00FFFF';
return { backgroundColor };
}
};
const sourceCode1 = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
style: { backgroundColor: '#c8e6c9' }
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
const sourceCode2 = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
style: (row, rowIndex) => {
const backgroundColor = rowIndex > 1 ? '#00BFFF' : '#00FFFF';
return { backgroundColor };
}
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow1 } />
<Code>{ sourceCode1 }</Code>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow2 } />
<Code>{ sourceCode2 }</Code>
</div>
);

View File

@ -53,6 +53,8 @@ import CellEditWithRedux from 'examples/cell-edit/cell-edit-with-redux-table';
// work on row selection
import SingleSelectionTable from 'examples/row-selection/single-selection';
import MultipleSelectionTable from 'examples/row-selection/multiple-selection';
import SelectionStyleTable from 'examples/row-selection/selection-style';
import SelectionClassTable from 'examples/row-selection/selection-class';
// css style
import 'bootstrap/dist/css/bootstrap.min.css';
@ -113,6 +115,8 @@ storiesOf('Cell Editing', module)
.add('Async Cell Editing(Redux)', () => <CellEditWithRedux />);
storiesOf('Row Selection', module)
.add('Single selection', () => <SingleSelectionTable />)
.add('Multiple selection', () => <MultipleSelectionTable />);
.add('Single Selection', () => <SingleSelectionTable />)
.add('Multiple Selection', () => <MultipleSelectionTable />)
.add('Selection Style', () => <SelectionStyleTable />)
.add('Selection Class', () => <SelectionClassTable />);

View File

@ -0,0 +1,11 @@
.selection-row {
background-color: #c8e6c9;
}
.row-index-bigger-than-2101 {
background-color: #00BFFF;
}
.row-index-small-than-2101 {
background-color: #00FFFF;
}

View File

@ -5,4 +5,5 @@
@import "welcome/index";
@import "columns/index";
@import "cell-edit/index";
@import "cell-edit/index";
@import "row-selection/index";

View File

@ -22,6 +22,11 @@ const Body = (props) => {
selectedRowKeys
} = props;
const {
style: selectedStyle,
classes: selectedClasses
} = selectRow;
let content;
if (isEmpty) {
@ -37,6 +42,13 @@ const Body = (props) => {
? selectedRowKeys.includes(key)
: null;
let style = {};
let classes = '';
if (selected) {
style = _.isFunction(selectedStyle) ? selectedStyle(row, index) : selectedStyle;
classes = _.isFunction(selectedClasses) ? selectedClasses(row, index) : selectedClasses;
}
return (
<Row
key={ key }
@ -48,6 +60,8 @@ const Body = (props) => {
editable={ editable }
selected={ selected }
selectRow={ selectRow }
style={ style }
className={ classes }
/>
);
});

View File

@ -127,7 +127,9 @@ BootstrapTable.propTypes = {
editing: PropTypes.bool
}),
selectRow: PropTypes.shape({
mode: PropTypes.oneOf([Const.ROW_SELECT_SINGLE, Const.ROW_SELECT_MULTIPLE]).isRequired
mode: PropTypes.oneOf([Const.ROW_SELECT_SINGLE, Const.ROW_SELECT_MULTIPLE]).isRequired,
style: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
classes: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
}),
onRowSelect: PropTypes.func,
onAllRowsSelect: PropTypes.func

View File

@ -9,13 +9,13 @@ import EditingCell from './cell-edit/editing-cell';
import Const from './const';
const Row = (props) => {
const { ROW_SELECT_DISABLED } = Const;
const {
row,
columns,
keyField,
rowIndex,
className,
style,
cellEdit,
selected,
selectRow,
@ -31,9 +31,9 @@ const Row = (props) => {
} = cellEdit;
return (
<tr>
<tr style={ style } className={ className }>
{
selectRow.mode === ROW_SELECT_DISABLED
selectRow.mode === Const.ROW_SELECT_DISABLED
? null
: (
<SelectionCell
@ -53,21 +53,21 @@ const Row = (props) => {
editable = column.editable(content, row, rowIndex, index);
}
if (rowIndex === editingRowIdx && index === editingColIdx) {
let style = column.editCellStyle || {};
let classes = column.editCellClasses;
let editCellstyle = column.editCellStyle || {};
let editCellclasses = column.editCellClasses;
if (_.isFunction(column.editCellStyle)) {
style = column.editCellStyle(content, row, rowIndex, index);
editCellstyle = column.editCellStyle(content, row, rowIndex, index);
}
if (_.isFunction(column.editCellClasses)) {
classes = column.editCellClasses(content, row, rowIndex, index);
editCellclasses = column.editCellClasses(content, row, rowIndex, index);
}
return (
<EditingCell
key={ content }
row={ row }
column={ column }
className={ classes }
style={ style }
className={ editCellclasses }
style={ editCellstyle }
{ ...rest }
/>
);
@ -93,11 +93,15 @@ const Row = (props) => {
Row.propTypes = {
row: PropTypes.object.isRequired,
rowIndex: PropTypes.number.isRequired,
columns: PropTypes.array.isRequired
columns: PropTypes.array.isRequired,
style: PropTypes.object,
className: PropTypes.string
};
Row.defaultProps = {
editable: true
editable: true,
style: {},
className: null
};
export default Row;

View File

@ -152,35 +152,128 @@ describe('Body', () => {
describe('when selectRow.mode is checkbox or radio (row was selectable)', () => {
const keyField = 'id';
const selectRow = { mode: 'checkbox' };
const selectedRowKey = data[0][keyField];
const selectedRowKeys = [selectedRowKey];
it('props selected should be true if all rows were selected', () => {
beforeEach(() => {
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ [1, 2] }
selectedRowKeys={ selectedRowKeys }
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 }
/>
);
it('should render Row component with correct selected prop', () => {
const rows = wrapper.find(Row);
for (let i = 0; i < rows.length; i += 1) {
const row = rows.get(i);
expect(row.props.selected).toBe(selectedRowKeys.indexOf(row.props.row[keyField]) > -1);
}
});
expect(wrapper.find(Row).get(0).props.selected).toBe(false);
describe('if selectRow.style is defined as an object', () => {
const style = { backgroundColor: 'red' };
beforeEach(() => {
selectRow.style = style;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should render Row component with correct style prop', () => {
expect(wrapper.find(Row).get(0).props.style).toBe(style);
});
});
describe('if selectRow.style is defined as a function', () => {
const style = { backgroundColor: 'red' };
const styleCallBack = sinon.stub().returns(style);
beforeEach(() => {
selectRow.style = styleCallBack;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should calling style callback correctly', () => {
expect(styleCallBack.callCount).toBe(1);
expect(styleCallBack.calledWith(data[0]), 1);
});
it('should render Row component with correct style prop', () => {
expect(wrapper.find(Row).get(0).props.style).toBe(style);
});
});
describe('if selectRow.classes is defined as a string', () => {
const className = 'custom-class';
beforeEach(() => {
selectRow.classes = className;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should render Row component with correct className prop', () => {
expect(wrapper.find(Row).get(0).props.className).toEqual(className);
});
});
describe('if selectRow.classes is defined as a function', () => {
const className = 'custom-class';
const classesCallBack = sinon.stub().returns(className);
beforeEach(() => {
selectRow.classes = classesCallBack;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should calling style callback correctly', () => {
expect(classesCallBack.callCount).toBe(1);
expect(classesCallBack.calledWith(data[0]), 1);
});
it('should render Row component with correct style prop', () => {
expect(wrapper.find(Row).get(0).props.className).toEqual(className);
});
});
});

View File

@ -42,6 +42,44 @@ describe('Row', () => {
});
});
describe('when style prop is defined', () => {
const customStyle = { backgroundColor: 'red' };
beforeEach(() => {
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
rowIndex={ 1 }
columns={ defaultColumns }
row={ row }
style={ customStyle }
/>);
});
it('should render component with style successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.prop('style')).toEqual(customStyle);
});
});
describe('when className prop is defined', () => {
const className = 'test-class';
beforeEach(() => {
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
rowIndex={ 1 }
columns={ defaultColumns }
row={ row }
className={ className }
/>);
});
it('should render component with style successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.hasClass(className)).toBe(true);
});
});
describe('when cellEdit prop is defined', () => {
let columns;
let cellEdit;