From 849d9af8c414ab0ceb86b4fa3d52809d47192c7b Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 1 Sep 2018 14:52:29 +0800 Subject: [PATCH 1/3] fix #504 --- packages/react-bootstrap-table2/src/cell.js | 17 +++++++++++++---- .../react-bootstrap-table2/src/header-cell.js | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/react-bootstrap-table2/src/cell.js b/packages/react-bootstrap-table2/src/cell.js index db32d05..e6f2149 100644 --- a/packages/react-bootstrap-table2/src/cell.js +++ b/packages/react-bootstrap-table2/src/cell.js @@ -11,9 +11,18 @@ class Cell extends Component { } shouldComponentUpdate(nextProps) { - const shouldUpdate = - _.get(this.props.row, this.props.column.dataField) - !== _.get(nextProps.row, nextProps.column.dataField) || + let shouldUpdate = false; + if (nextProps.column.isDummyField) { + shouldUpdate = !_.isEqual(this.props.row, nextProps.row); + } else { + shouldUpdate = + _.get(this.props.row, this.props.column.dataField) + !== _.get(nextProps.row, nextProps.column.dataField); + } + + if (shouldUpdate) return true; + + shouldUpdate = this.props.column.hidden !== nextProps.column.hidden || this.props.rowIndex !== nextProps.rowIndex || this.props.columnIndex !== nextProps.columnIndex || @@ -64,7 +73,7 @@ class Cell extends Component { formatExtraData } = column; const attrs = { ...rest }; - let content = _.get(row, dataField); + let content = column.isDummyField ? null : _.get(row, dataField); if (formatter) { content = column.formatter(content, row, rowIndex, formatExtraData); diff --git a/packages/react-bootstrap-table2/src/header-cell.js b/packages/react-bootstrap-table2/src/header-cell.js index 52cf7a1..2053453 100644 --- a/packages/react-bootstrap-table2/src/header-cell.js +++ b/packages/react-bootstrap-table2/src/header-cell.js @@ -114,6 +114,7 @@ HeaderCell.propTypes = { column: PropTypes.shape({ dataField: PropTypes.string.isRequired, text: PropTypes.string.isRequired, + isDummyField: PropTypes.bool, hidden: PropTypes.bool, headerFormatter: PropTypes.func, formatter: PropTypes.func, From 3b1fc3a5598d17e4be32c38ec42750c9e90c445a Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 1 Sep 2018 14:55:48 +0800 Subject: [PATCH 2/3] add dummy column story --- .../examples/columns/dummy-column-table.js | 221 ++++++++++++++++++ .../stories/index.js | 4 +- .../react-bootstrap-table2/test/cell.test.js | 53 +++-- 3 files changed, 262 insertions(+), 16 deletions(-) create mode 100644 packages/react-bootstrap-table2-example/examples/columns/dummy-column-table.js diff --git a/packages/react-bootstrap-table2-example/examples/columns/dummy-column-table.js b/packages/react-bootstrap-table2-example/examples/columns/dummy-column-table.js new file mode 100644 index 0000000..10b6948 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/columns/dummy-column-table.js @@ -0,0 +1,221 @@ +/* eslint jsx-a11y/label-has-for: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import Code from 'components/common/code-block'; + + +const products = [ + { id: 12, name: 'Item 12', price: 12.5, inStock: false }, + { id: 13, name: 'Item 13', price: 13.5, inStock: true }, + { id: 14, name: 'Item 14', price: 14.5, inStock: true } +]; + +const columns = [ + { + dataField: 'id', + text: 'Product ID' + }, + { + dataField: 'name', + text: 'Product Name' + }, + { + dataField: 'price', + text: 'Product Price' + }, + { + dataField: 'inStock', + text: 'In Stock', + formatter: (cellContent, row) => ( +
+ +
+ ) + }, + { + dataField: 'df1', + isDummyField: true, + text: 'Action 1', + formatter: (cellContent, row) => { + if (row.inStock) { + return ( +
+ Available +
+ ); + } + return ( +
+ Backordered +
+ ); + } + }, + { + dataField: 'df2', + isDummyField: true, + text: 'Action 2', + formatter: (cellContent, row) => { + if (row.inStock) { + return ( +
+ Available +
+ ); + } + return ( +
+ Backordered +
+ ); + } + } +]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = [ + { + dataField: 'id', + text: 'Product ID' + }, + { + dataField: 'name', + text: 'Product Name' + }, + { + dataField: 'price', + text: 'Product Price' + }, + { + dataField: 'inStock', + text: 'In Stock', + formatter: (cellContent, row) => ( +
+ +
+ ) + }, + { + dataField: 'df1', + isDummyField: true, + text: 'Action 1', + formatter: (cellContent, row) => { + if (row.inStock) { + return ( +
+ Available +
+ ); + } + return ( +
+ Backordered +
+ ); + } + }, + { + dataField: 'df2', + isDummyField: true, + text: 'Action 2', + formatter: (cellContent, row) => { + if (row.inStock) { + return ( +
+ Available +
+ ); + } + return ( +
+ Backordered +
+ ); + } + } +]; + +class ProductList extends React.Component { + constructor(props) { + super(props); + this.state = { products }; + } + + toggleInStock = () => { + let newProducts = [...this.state.products]; + newProducts = newProducts.map((d) => { + if (d.id === 13) { + return { + ...d, + inStock: !d.inStock + }; + } + return d; + }); + this.setState(curr => ({ ...curr, products: newProducts })); + }; + + render() { + return ( +
+

Products

+ + +
+ ); + } +} +`; + +class ProductList extends React.Component { + constructor(props) { + super(props); + this.state = { products }; + } + + toggleInStock = () => { + let newProducts = [...this.state.products]; + newProducts = newProducts.map((d) => { + if (d.id === 13) { + return { + ...d, + inStock: !d.inStock + }; + } + return d; + }); + this.setState(curr => ({ ...curr, products: newProducts })); + }; + + render() { + return ( +
+

Action 1 and Action 2 are dummy column

+ + + { sourceCode } +
+ ); + } +} + +export default ProductList; diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index eff1ee0..45fb5cc 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -30,6 +30,7 @@ import ColumnTitleTable from 'examples/columns/column-title-table'; import ColumnEventTable from 'examples/columns/column-event-table'; import ColumnHiddenTable from 'examples/columns/column-hidden-table'; import ColumnAttrsTable from 'examples/columns/column-attrs-table'; +import DummyColumnTable from 'examples/columns/dummy-column-table'; // work on header columns import HeaderColumnFormatTable from 'examples/header-columns/column-format-table'; @@ -201,7 +202,8 @@ storiesOf('Work on Columns', module) .add('Column Event', () => ) .add('Customize Column Class', () => ) .add('Customize Column Style', () => ) - .add('Customize Column HTML attribute', () => ); + .add('Customize Column HTML attribute', () => ) + .add('Dummy Column', () => ); storiesOf('Work on Header Columns', module) .addDecorator(bootstrapStyle()) diff --git a/packages/react-bootstrap-table2/test/cell.test.js b/packages/react-bootstrap-table2/test/cell.test.js index 86bf393..e2e42ab 100644 --- a/packages/react-bootstrap-table2/test/cell.test.js +++ b/packages/react-bootstrap-table2/test/cell.test.js @@ -177,22 +177,45 @@ describe('Cell', () => { let props; let nextProps; - describe('when content is change', () => { - const column = { dataField: 'name', text: 'Product Name' }; - beforeEach(() => { - props = { - row, - columnIndex: 1, - rowIndex: 1, - column - }; - wrapper = shallow( - ); - }); + describe('if column.isDummyField is false', () => { + describe('when content is change', () => { + const column = { dataField: 'name', text: 'Product Name' }; + beforeEach(() => { + props = { + row, + columnIndex: 1, + rowIndex: 1, + column + }; + wrapper = shallow( + ); + }); - it('should return true', () => { - nextProps = { ...props, row: { id: 1, name: 'CDE' } }; - expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true); + it('should return true', () => { + nextProps = { ...props, row: { id: 1, name: 'CDE' } }; + expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true); + }); + }); + }); + + describe('if column.isDummyField is true', () => { + describe('when content is change', () => { + const column = { dataField: '', text: 'Product Name', isDummyField: true }; + beforeEach(() => { + props = { + row, + columnIndex: 1, + rowIndex: 1, + column + }; + wrapper = shallow( + ); + }); + + it('should return true', () => { + nextProps = { ...props, row: { id: 1, name: 'CDE', test: 'This is new Field' } }; + expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true); + }); }); }); From a9669007527d0ba7a22b12e099677e7200926967 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 1 Sep 2018 14:55:59 +0800 Subject: [PATCH 3/3] patch docs for #504 --- docs/columns.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/columns.md b/docs/columns.md index d472e67..afeffe7 100644 --- a/docs/columns.md +++ b/docs/columns.md @@ -7,6 +7,7 @@ Available properties in a column object: * [text (**required**)](#text) #### Optional +* [isDummyField](#isDummyField) * [hidden](#hidden) * [formatter](#formatter) * [formatExtraData](#formatExtraData) @@ -84,6 +85,11 @@ dataField: 'address.city' ## column.text (**required**) - [String] `text` will be the column text in header column by default, if your header is not only text or you want to customize the header column, please check [`column.headerFormatter`](#headerFormatter) +## column.isDummyField - [Bool] +Sometime, you just want to have a column which is not perform any data but just some action components. In this situation, we suggest you to use `isDummyField`. If column is dummy, the [`column.dataField`](#dataField) can be any string value, cause of it's meaningless. + +There's only one different for dummy column than normal column, which is dummy column will compare the whole row value instead of cell value when call `shouldComponentUpdate`. + ## column.hidden - [Bool] `hidden` allow you to hide column when `true` given.