* implement cell editor style/class

* add stories for custom cell editor style and class

* patch testing for cell editor style and class

* patch docs for cell editor style and class
This commit is contained in:
Allen 2017-10-18 09:15:41 -05:00 committed by GitHub
parent afef2adcaf
commit cb6410bbe4
11 changed files with 365 additions and 4 deletions

View File

@ -27,6 +27,8 @@ Available properties in a column object:
* [headerAttrs](#headerAttrs)
* [editable](#editable)
* [validator](#validator)
* [editCellStyle](#editCellStyle)
* [editCellClasses](#editCellClasses)
Following is a most simplest and basic usage:
@ -450,4 +452,46 @@ The return value can be a bool or an object. If your valiation is pass, return `
valid: false,
message: 'SOME_REASON_HERE'
}
```
## <a name='editCellStyle'>column.editCellStyle - [Object | Function]</a>
You can use `column.editCellStyle` to custom the style of `<td>` when cell editing. It like most of customizable functionality, it also accept a callback function with following params:
**Parameters**
* `cell`: The value of current cell.
* `row`: The object of `row` being processed in the `BootstrapTable`.
* `rowIndex`: The index of the current `row` being processed in the `BootstrapTable`.
* `colIndex`: The index of the current `column` being processed in `BootstrapTable`.
```js
{
editCellStyle: { ... }
}
```
Or take a callback function
```js
{
editCellStyle: (cell, row, rowIndex, colIndex) => {
// it is suppose to return an object
}
}
```
## <a name='editCellClasses'>column.editCellClasses - [Object | 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
{
editCellClasses: 'custom-class'
}
```
Or take a callback function
```js
{
editCellClasses: (cell, row, rowIndex, colIndex) => {
// it is suppose to return a string
}
}
```

View File

@ -0,0 +1,59 @@
/* 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',
editCellClasses: 'editing-name'
}, {
dataField: 'price',
text: 'Product Price',
editCellClasses: (cell, row, rowIndex, colIndex) =>
(cell > 2101 ? 'editing-price-bigger-than-2101' : 'editing-price-small-than-2101')
}];
const sourceCode = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
editCellClasses: 'editing-name'
}, {
dataField: 'price',
text: 'Product Price',
editCellClasses: (cell, row, rowIndex, colIndex) =>
(cell > 2101 ? 'editing-price-bigger-than-2101' : 'editing-price-small-than-2101')
}];
const cellEdit = {
mode: 'click'
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
cellEdit={ cellEdit }
/>
`;
const cellEdit = {
mode: 'click'
};
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -0,0 +1,67 @@
/* 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',
editCellStyle: {
backgroundColor: '#20B2AA'
}
}, {
dataField: 'price',
text: 'Product Price',
editCellStyle: (cell, row, rowIndex, colIndex) => {
const backgroundColor = cell > 2101 ? '#00BFFF' : '#00FFFF';
return { backgroundColor };
}
}];
const sourceCode = `\
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
editCellStyle: {
backgroundColor: '#20B2AA'
}
}, {
dataField: 'price',
text: 'Product Price',
editCellStyle: (cell, row, rowIndex, colIndex) => {
const backgroundColor = cell > 2101 ? '#00BFFF' : '#00FFFF';
return { backgroundColor };
}
}];
const cellEdit = {
mode: 'click'
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
cellEdit={ cellEdit }
/>
`;
const cellEdit = {
mode: 'click'
};
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -45,6 +45,8 @@ import ColumnLevelEditableTable from 'examples/cell-edit/column-level-editable-t
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';
import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table';
import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
import CellEditWithPromise from 'examples/cell-edit/cell-edit-with-promise-table';
import CellEditWithRedux from 'examples/cell-edit/cell-edit-with-redux-table';
@ -105,6 +107,8 @@ storiesOf('Cell Editing', module)
.add('Cell Level Editable', () => <CellLevelEditable />)
.add('Rich Hook Functions', () => <CellEditHooks />)
.add('Validation', () => <CellEditValidator />)
.add('Custom Cell Style When Editing', () => <CellEditStyleTable />)
.add('Custom Cell Classes When Editing', () => <CellEditClassTable />)
.add('Async Cell Editing(Promise)', () => <CellEditWithPromise />)
.add('Async Cell Editing(Redux)', () => <CellEditWithRedux />);

View File

@ -0,0 +1,11 @@
.editing-name {
background-color: #20B2AA;
}
.editing-price-bigger-than-2101 {
background-color: #00BFFF;
}
.editing-price-small-than-2101 {
background-color: #00FFFF;
}

View File

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

View File

@ -96,7 +96,7 @@ class EditingCell extends Component {
render() {
const { invalidMessage } = this.state;
const { row, column } = this.props;
const { row, column, className, style } = this.props;
const { dataField } = column;
const value = _.get(row, dataField);
@ -108,7 +108,10 @@ class EditingCell extends Component {
const hasError = _.isDefined(invalidMessage);
const editorClass = hasError ? cs('animated', 'shake') : null;
return (
<td className="react-bootstrap-table-editing-cell">
<td
className={ cs('react-bootstrap-table-editing-cell', className) }
style={ style }
>
<TextEditor
ref={ node => this.editor = node }
defaultValue={ value }
@ -126,11 +129,15 @@ EditingCell.propTypes = {
column: PropTypes.object.isRequired,
onUpdate: PropTypes.func.isRequired,
onEscape: PropTypes.func.isRequired,
timeToCloseMessage: PropTypes.number
timeToCloseMessage: PropTypes.number,
className: PropTypes.string,
style: PropTypes.object
};
EditingCell.defaultProps = {
timeToCloseMessage: Const.TIME_TO_CLOSE_MESSAGE
timeToCloseMessage: Const.TIME_TO_CLOSE_MESSAGE,
className: null,
style: {}
};
export default EditingCell;

View File

@ -105,6 +105,8 @@ HeaderCell.propTypes = {
sort: PropTypes.bool,
sortFunc: PropTypes.func,
editable: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
editCellStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
editCellClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
validator: PropTypes.func
}).isRequired,
index: PropTypes.number.isRequired,

View File

@ -53,11 +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;
if (_.isFunction(column.editCellStyle)) {
style = column.editCellStyle(content, row, rowIndex, index);
}
if (_.isFunction(column.editCellClasses)) {
classes = column.editCellClasses(content, row, rowIndex, index);
}
return (
<EditingCell
key={ content }
row={ row }
column={ column }
className={ classes }
style={ style }
{ ...rest }
/>
);

View File

@ -75,6 +75,46 @@ describe('EditingCell', () => {
expect(onEscape.callCount).toBe(1);
});
describe('if style prop is defined', () => {
const customStyle = { backgroundColor: 'red' };
beforeEach(() => {
wrapper = shallow(
<EditingCell
row={ row }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
style={ customStyle }
/>
);
});
it('should render component with style successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('style')).toEqual(customStyle);
});
});
describe('if className prop is defined', () => {
const className = 'test-class';
beforeEach(() => {
wrapper = shallow(
<EditingCell
row={ row }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
className={ className }
/>
);
});
it('should render component with style successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.hasClass(className)).toBe(true);
});
});
describe('if blurToSave prop is true', () => {
beforeEach(() => {
wrapper = mount(

View File

@ -249,6 +249,122 @@ describe('Row', () => {
expect(wrapper.find(EditingCell).length).toBe(1);
expect(complexComponents.at(editingColIndex).type()).toEqual(EditingCell);
});
describe('if column.editCellStyle defined as object', () => {
const definedStyleColIndex = editingColIndex;
beforeEach(() => {
columns[definedStyleColIndex].editCellStyle = { backgroundColor: 'red' };
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
row={ row }
rowIndex={ rowIndex }
columns={ columns }
keyField={ keyField }
cellEdit={ cellEdit }
/>
);
});
it('should also rendering EditingCell with correct style object', () => {
expect(wrapper.find(EditingCell).length).toBe(1);
expect(wrapper.find(EditingCell).props().style)
.toEqual(columns[definedStyleColIndex].editCellStyle);
});
});
describe('if column.editCellStyle defined as function', () => {
const definedStyleColIndex = editingColIndex;
const customStyle = { backgroundColor: 'red' };
let editCellStyleCallBack;
beforeEach(() => {
editCellStyleCallBack = sinon.stub().returns(customStyle);
columns[definedStyleColIndex].editCellStyle = editCellStyleCallBack;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
row={ row }
rowIndex={ rowIndex }
columns={ columns }
keyField={ keyField }
cellEdit={ cellEdit }
/>
);
});
it('should calling custom column.editCellStyle callback correctly', () => {
expect(editCellStyleCallBack.callCount).toBe(1);
expect(
editCellStyleCallBack.calledWith(
row[columns[editingColIndex].dataField], row, rowIndex, editingColIndex)
).toBe(true);
});
it('should also rendering EditingCell with correct style object', () => {
expect(wrapper.find(EditingCell).length).toBe(1);
expect(wrapper.find(EditingCell).props().style).toEqual(customStyle);
});
});
describe('if column.editCellClasses defined as string', () => {
const definedStyleColIndex = editingColIndex;
beforeEach(() => {
columns[definedStyleColIndex].editCellClasses = 'custom-class';
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
row={ row }
rowIndex={ rowIndex }
columns={ columns }
keyField={ keyField }
cellEdit={ cellEdit }
/>
);
});
it('should also rendering EditingCell with correct class', () => {
expect(wrapper.find(EditingCell).length).toBe(1);
expect(wrapper.find(EditingCell).props().className)
.toEqual(columns[definedStyleColIndex].editCellClasses);
});
});
describe('if column.editCellClasses defined as function', () => {
const definedStyleColIndex = editingColIndex;
const customClass = 'custom-class';
let editCellClassesCallBack;
beforeEach(() => {
editCellClassesCallBack = sinon.stub().returns(customClass);
columns[definedStyleColIndex].editCellClasses = editCellClassesCallBack;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
row={ row }
rowIndex={ rowIndex }
columns={ columns }
keyField={ keyField }
cellEdit={ cellEdit }
/>
);
});
it('should calling custom column.editCellStyle callback correctly', () => {
expect(editCellClassesCallBack.callCount).toBe(1);
expect(
editCellClassesCallBack.calledWith(
row[columns[editingColIndex].dataField], row, rowIndex, editingColIndex)
).toBe(true);
});
it('should also rendering EditingCell with correct class', () => {
expect(wrapper.find(EditingCell).length).toBe(1);
expect(wrapper.find(EditingCell).props().className).toEqual(customClass);
});
});
});
describe('and cellEdit.ridx is not match to current row index', () => {