From 19dc4d398425b9176e48452dc620c70d6d830c5d Mon Sep 17 00:00:00 2001 From: Allen Date: Sun, 29 Oct 2017 18:17:20 +0800 Subject: [PATCH] fix #129 * implement custom row style * add story for custom row style * add tests for rowStyle * patch docs for rowStyle --- docs/README.md | 18 +++ .../examples/rows/row-style.js | 93 +++++++++++ .../stories/index.js | 6 + packages/react-bootstrap-table2/src/body.js | 14 +- .../src/bootstrap-table.js | 5 +- .../react-bootstrap-table2/test/body.test.js | 149 +++++++++++++++++- 6 files changed, 275 insertions(+), 10 deletions(-) create mode 100644 packages/react-bootstrap-table2-example/examples/rows/row-style.js diff --git a/docs/README.md b/docs/README.md index 1d45c9e..1d852ca 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,7 @@ * [condensed](#condensed) * [cellEdit](#cellEdit) * [selectRow](#selectRow) +* [rowStyle](#rowStyle) * [defaultSorted](#defaultSorted) ### keyField(**required**) - [String] @@ -44,6 +45,23 @@ Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for ### selectRow - [Object] Makes table rows selectable, please see [selectRow definition](./row-selection.md) for more detail. +### rowStyle = [Object | Function] +Custom the style of table rows: + +```js + +``` + +This prop also accept a callback function for flexible to custom row style: + +```js +const rowStyle = (row, rowIndex) => { + return { ... }; +}; + + +``` + ### defaultSorted - [Array] `defaultSorted` accept an object array which allow you to define the default sort columns when first render. diff --git a/packages/react-bootstrap-table2-example/examples/rows/row-style.js b/packages/react-bootstrap-table2-example/examples/rows/row-style.js new file mode 100644 index 0000000..76de2ea --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/rows/row-style.js @@ -0,0 +1,93 @@ +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 rowStyle1 = { backgroundColor: '#c8e6c9' }; + +const sourceCode1 = `\ +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const rowStyle = { backgroundColor: '#c8e6c9' }; + + +`; + +const rowStyle2 = (row, rowIndex) => { + const style = {}; + if (row.id > 3) { + style.backgroundColor = '#c8e6c9'; + } else { + style.backgroundColor = '#00BFFF'; + } + + if (rowIndex > 2) { + style.fontWeight = 'bold'; + style.color = 'white'; + } + + return style; +}; + +const sourceCode2 = `\ +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const rowStyle2 = (row, rowIndex) => { + const style = {}; + if (row.id > 3) { + style.backgroundColor = '#c8e6c9'; + } else { + style.backgroundColor = '#00BFFF'; + } + + if (rowIndex > 2) { + style.fontWeight = 'bold'; + style.color = 'white'; + } + + return style; +}; + + +`; + +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 98d0331..bd1522f 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -32,6 +32,9 @@ import HeaderColumnClassTable from 'examples/header-columns/column-class-table'; import HeaderColumnStyleTable from 'examples/header-columns/column-style-table'; import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table'; +// work on rows +import RowStyleTable from 'examples/rows/row-style'; + // table sort import EnableSortTable from 'examples/sort/enable-sort-table'; import DefaultSortTable from 'examples/sort/default-sort-table'; @@ -103,6 +106,9 @@ storiesOf('Work on Header Columns', module) .add('Customize Column Style', () => ) .add('Customize Column HTML attribute', () => ); +storiesOf('Work on Rows', module) + .add('Row Style', () => ); + storiesOf('Sort Table', module) .add('Enable Sort', () => ) .add('Default Sort Table', () => ) diff --git a/packages/react-bootstrap-table2/src/body.js b/packages/react-bootstrap-table2/src/body.js index 453ab0d..f8c07f9 100644 --- a/packages/react-bootstrap-table2/src/body.js +++ b/packages/react-bootstrap-table2/src/body.js @@ -19,11 +19,11 @@ const Body = (props) => { visibleColumnSize, cellEdit, selectRow, - selectedRowKeys + selectedRowKeys, + rowStyle } = props; const { - style: selectedStyle, classes: selectedClasses, bgColor, nonSelectable @@ -44,10 +44,16 @@ const Body = (props) => { ? selectedRowKeys.includes(key) : null; - let style; + let style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle; let classes; if (selected) { - style = _.isFunction(selectedStyle) ? selectedStyle(row, index) : selectedStyle; + const selectedStyle = _.isFunction(selectRow.style) + ? selectRow.style(row, index) + : selectRow.style; + style = { + ...style, + ...selectedStyle + }; classes = _.isFunction(selectedClasses) ? selectedClasses(row, index) : selectedClasses; if (bgColor) { diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index 2c4e2fd..32103b2 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -35,7 +35,8 @@ class BootstrapTable extends PropsBaseResolver(Component) { bordered, condensed, noDataIndication, - caption + caption, + rowStyle } = this.props; const tableClass = cs('table', { @@ -83,6 +84,7 @@ class BootstrapTable extends PropsBaseResolver(Component) { cellEdit={ cellEditInfo } selectRow={ cellSelectionInfo } selectedRowKeys={ store.getSelectedRowKeys() } + rowStyle={ rowStyle } /> @@ -140,6 +142,7 @@ BootstrapTable.propTypes = { }), onRowSelect: PropTypes.func, onAllRowsSelect: PropTypes.func, + rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), defaultSorted: PropTypes.arrayOf(PropTypes.shape({ dataField: PropTypes.string.isRequired, order: PropTypes.oneOf([Const.SORT_DESC, Const.SORT_ASC]).isRequired diff --git a/packages/react-bootstrap-table2/test/body.test.js b/packages/react-bootstrap-table2/test/body.test.js index 2e98068..1f160ad 100644 --- a/packages/react-bootstrap-table2/test/body.test.js +++ b/packages/react-bootstrap-table2/test/body.test.js @@ -26,6 +26,8 @@ describe('Body', () => { name: 'B' }]; + const keyField = 'id'; + describe('simplest body', () => { beforeEach(() => { wrapper = shallow(); @@ -117,9 +119,148 @@ describe('Body', () => { }); }); + describe('when rowStyle prop is defined', () => { + const rowStyle = { backgroundColor: 'red', color: 'white' }; + + describe('and it is a style object', () => { + beforeEach(() => { + wrapper = shallow( + ); + }); + + it('should rendering Row component with correct style', () => { + const rows = wrapper.find(Row); + rows.forEach((row) => { + expect(row.props().style).toEqual(rowStyle); + }); + }); + }); + + describe('and it is a callback functoin', () => { + const rowStyleCallBack = sinon.stub().returns(rowStyle); + beforeEach(() => { + wrapper = shallow( + ); + }); + + it('should calling rowStyleCallBack correctly', () => { + expect(rowStyleCallBack.callCount).toBe(data.length); + }); + + it('should calling rowStyleCallBack with correct argument', () => { + expect(rowStyleCallBack.firstCall.calledWith(data[0], 0)).toBeTruthy(); + expect(rowStyleCallBack.secondCall.calledWith(data[1], 1)).toBeTruthy(); + }); + + it('should rendering Row component with correct style', () => { + const rows = wrapper.find(Row); + rows.forEach((row) => { + expect(row.props().style).toEqual(rowStyle); + }); + }); + }); + + describe('when selectRow.style is defined', () => { + const selectedRowKey = data[0][keyField]; + const selectedRowKeys = [selectedRowKey]; + const selectedStyle = { backgroundColor: 'green', fontWeight: 'bold' }; + const selectRow = { mode: 'radio', style: selectedStyle }; + + beforeEach(() => { + wrapper = shallow( + ); + }); + + it('should rendering selected Row component with mixing selectRow.style correctly', () => { + const selectedRow = wrapper.find(Row).get(0); + expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({ + ...rowStyle, + ...selectedStyle + })); + }); + + describe('and selectRow.bgColor is also defined', () => { + beforeEach(() => { + selectRow.bgColor = 'gray'; + wrapper = shallow( + ); + }); + + it('should rendering selected Row component with mixing selectRow.style correctly', () => { + const selectedRow = wrapper.find(Row).get(0); + expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({ + ...rowStyle, + ...selectedStyle, + backgroundColor: selectRow.bgColor + })); + }); + + it('should render selected Row component with correct style.backgroundColor', () => { + const selectedRow = wrapper.find(Row).get(0); + expect(selectedRow.props.style.backgroundColor).toEqual(selectRow.bgColor); + }); + }); + }); + + describe('when selectRow.bgColor is defined', () => { + const selectedRowKey = data[0][keyField]; + const selectedRowKeys = [selectedRowKey]; + const selectRow = { mode: 'radio', bgColor: 'gray' }; + + beforeEach(() => { + selectRow.bgColor = 'gray'; + wrapper = shallow( + ); + }); + + it('should rendering selected Row component with correct style', () => { + const selectedRow = wrapper.find(Row).get(0); + expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({ + ...rowStyle, + backgroundColor: selectRow.bgColor + })); + }); + }); + }); + describe('when cellEdit.nonEditableRows props is defined', () => { const nonEditableRows = [data[1].id]; - const keyField = 'id'; const cellEdit = { mode: Const.CLICK_TO_CELL_EDIT, nonEditableRows @@ -150,7 +291,6 @@ 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]; @@ -194,7 +334,7 @@ describe('Body', () => { }); it('should render Row component with correct style prop', () => { - expect(wrapper.find(Row).get(0).props.style).toBe(style); + expect(JSON.stringify(wrapper.find(Row).get(0).props.style)).toBe(JSON.stringify(style)); }); }); @@ -222,7 +362,7 @@ describe('Body', () => { }); it('should render Row component with correct style prop', () => { - expect(wrapper.find(Row).get(0).props.style).toBe(style); + expect(JSON.stringify(wrapper.find(Row).get(0).props.style)).toBe(JSON.stringify(style)); }); }); @@ -377,7 +517,6 @@ describe('Body', () => { describe('when selectRow.mode is ROW_SELECT_DISABLED (row was un-selectable)', () => { beforeEach(() => { - const keyField = 'id'; wrapper = shallow(