diff --git a/docs/columns.md b/docs/columns.md index 7fba38a..9fe7745 100644 --- a/docs/columns.md +++ b/docs/columns.md @@ -478,7 +478,7 @@ Or take a callback function } ``` -## column.editCellClasses - [Object | Function] +## column.editCellClasses - [String | Function] You can use `column.editCellClasses` to add custom class on `` 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 diff --git a/docs/row-selection.md b/docs/row-selection.md index 87626ab..3b6ddcd 100644 --- a/docs/row-selection.md +++ b/docs/row-selection.md @@ -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 } /> ``` + +## selectRow.style - [Object | Function] +`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 ...; } +}; +``` + +## selectRow.classes - [String | Function] +`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 ...; } +}; +``` diff --git a/packages/react-bootstrap-table2-example/examples/row-selection/selection-class.js b/packages/react-bootstrap-table2-example/examples/row-selection/selection-class.js new file mode 100644 index 0000000..ffce5b8 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/selection-class.js @@ -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' +}; + + +`; + +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') +}; + + +`; + +export default () => ( +
+ + { sourceCode1 } + + { sourceCode2 } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/row-selection/selection-style.js b/packages/react-bootstrap-table2-example/examples/row-selection/selection-style.js new file mode 100644 index 0000000..1608fdf --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/selection-style.js @@ -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' } +}; + + +`; + +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 }; + } +}; + + +`; + +export default () => ( +
+ + { sourceCode1 } + + { sourceCode2 } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index b96d8b9..e0ed3ca 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -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)', () => ); storiesOf('Row Selection', module) - .add('Single selection', () => ) - .add('Multiple selection', () => ); + .add('Single Selection', () => ) + .add('Multiple Selection', () => ) + .add('Selection Style', () => ) + .add('Selection Class', () => ); diff --git a/packages/react-bootstrap-table2-example/stories/stylesheet/row-selection/_index.scss b/packages/react-bootstrap-table2-example/stories/stylesheet/row-selection/_index.scss new file mode 100644 index 0000000..91cfc67 --- /dev/null +++ b/packages/react-bootstrap-table2-example/stories/stylesheet/row-selection/_index.scss @@ -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; +} \ No newline at end of file diff --git a/packages/react-bootstrap-table2-example/stories/stylesheet/storybook.scss b/packages/react-bootstrap-table2-example/stories/stylesheet/storybook.scss index 3c33215..47a18b1 100644 --- a/packages/react-bootstrap-table2-example/stories/stylesheet/storybook.scss +++ b/packages/react-bootstrap-table2-example/stories/stylesheet/storybook.scss @@ -5,4 +5,5 @@ @import "welcome/index"; @import "columns/index"; -@import "cell-edit/index"; \ No newline at end of file +@import "cell-edit/index"; +@import "row-selection/index"; \ No newline at end of file diff --git a/packages/react-bootstrap-table2/src/body.js b/packages/react-bootstrap-table2/src/body.js index 9a0cbba..fe0bcaa 100644 --- a/packages/react-bootstrap-table2/src/body.js +++ b/packages/react-bootstrap-table2/src/body.js @@ -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 ( { editable={ editable } selected={ selected } selectRow={ selectRow } + style={ style } + className={ classes } /> ); }); diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index f466618..13a9cce 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -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 diff --git a/packages/react-bootstrap-table2/src/row.js b/packages/react-bootstrap-table2/src/row.js index b183cd9..1949c33 100644 --- a/packages/react-bootstrap-table2/src/row.js +++ b/packages/react-bootstrap-table2/src/row.js @@ -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 ( - + { - selectRow.mode === ROW_SELECT_DISABLED + selectRow.mode === Const.ROW_SELECT_DISABLED ? null : ( { 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 ( ); @@ -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; diff --git a/packages/react-bootstrap-table2/test/body.test.js b/packages/react-bootstrap-table2/test/body.test.js index c89c35d..9a670a8 100644 --- a/packages/react-bootstrap-table2/test/body.test.js +++ b/packages/react-bootstrap-table2/test/body.test.js @@ -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( ); - - expect(wrapper.find(Row).get(0).props.selected).toBe(true); }); - it('props selected should be false if all rows were not selected', () => { - wrapper = shallow( - - ); + 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( + + ); + }); + + 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( + + ); + }); + + 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( + + ); + }); + + 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( + + ); + }); + + 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); + }); }); }); diff --git a/packages/react-bootstrap-table2/test/row.test.js b/packages/react-bootstrap-table2/test/row.test.js index 56b6b32..c332f27 100644 --- a/packages/react-bootstrap-table2/test/row.test.js +++ b/packages/react-bootstrap-table2/test/row.test.js @@ -42,6 +42,44 @@ describe('Row', () => { }); }); + describe('when style prop is defined', () => { + const customStyle = { backgroundColor: 'red' }; + beforeEach(() => { + wrapper = shallow( + ); + }); + + 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( + ); + }); + + 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;