diff --git a/docs/row-selection.md b/docs/row-selection.md index b72ceea6f..4615a4336 100644 --- a/docs/row-selection.md +++ b/docs/row-selection.md @@ -20,6 +20,7 @@ * [hideSelectAll](#hideSelectAll) * [selectionRenderer](#selectionRenderer) * [selectionHeaderRenderer](#selectionHeaderRenderer) +* [headerColumnStyle](#headerColumnStyle) ### selectRow.mode - [String] @@ -198,6 +199,31 @@ const selectRow = { > By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer. + +### selectRow.headerColumnStyle - [Object | Function] +A way to custome the selection header cell. `headerColumnStyle` not only accept a simple style object but also a callback function for more flexible customization: + +### Style Object + +```js +const selectRow = { + mode: 'checkbox', + headerColumnStyle: { backgroundColor: 'blue' } +}; +``` + +### Callback Function + +```js +const selectRow = { + mode: 'checkbox', + headerColumnStyle: (status) => ( + // status available value is checked, indeterminate and unchecked + return { backgroundColor: 'blue' }; + ) +}; +``` + ### selectRow.onSelect - [Function] This callback function will be called when a row is select/unselect and pass following three arguments: `row`, `isSelect`, `rowIndex` and `e`. diff --git a/packages/react-bootstrap-table2-example/examples/basic/exposed-function.js b/packages/react-bootstrap-table2-example/examples/basic/exposed-function.js index 6ea656d2a..562ae4872 100644 --- a/packages/react-bootstrap-table2-example/examples/basic/exposed-function.js +++ b/packages/react-bootstrap-table2-example/examples/basic/exposed-function.js @@ -45,8 +45,12 @@ class ExposedFunctionTable extends React.Component { console.log(this.node.table.props.data); } + handleGetCurrentData = () => { + console.log(this.node.table.props.data); + } + handleGetSelectedData = () => { - console.log(this.node.selectionContext.state.selected); + console.log(this.node.selectionContext.selected); } handleGetExpandedData = () => { @@ -117,7 +121,7 @@ export default class ExposedFunctionTable extends React.Component { } handleGetSelectedData = () => { - console.log(this.node.selectionContext.state.selected); + console.log(this.node.selectionContext.selected); } handleGetExpandedData = () => { diff --git a/packages/react-bootstrap-table2-example/examples/row-expand/expand-only-one.js b/packages/react-bootstrap-table2-example/examples/row-expand/expand-only-one.js index 4ef5bc415..c3033760c 100644 --- a/packages/react-bootstrap-table2-example/examples/row-expand/expand-only-one.js +++ b/packages/react-bootstrap-table2-example/examples/row-expand/expand-only-one.js @@ -43,6 +43,7 @@ const columns = [{ }]; const expandRow = { + onlyOneExpanding: true, renderer: row => (

{ \`This Expand row is belong to rowKey $\{row.id}\` }

diff --git a/packages/react-bootstrap-table2-example/examples/row-selection/header-style.js b/packages/react-bootstrap-table2-example/examples/row-selection/header-style.js new file mode 100644 index 000000000..5db7718fa --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/header-style.js @@ -0,0 +1,111 @@ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const products = productsGenerator(2); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const selectRow1 = { + mode: 'checkbox', + clickToSelect: true, + headerColumnStyle: { + backgroundColor: 'blue' + } +}; + +const sourceCode1 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = ... + +const selectRow = { + mode: 'checkbox', + clickToSelect: true, + headerColumnStyle: { + backgroundColor: 'blue' + } +}; + + +`; + +const selectRow2 = { + mode: 'checkbox', + clickToSelect: true, + headerColumnStyle: (status) => { + if (status === 'checked') { + return { + backgroundColor: 'yellow' + }; + } else if (status === 'indeterminate') { + return { + backgroundColor: 'pink' + }; + } else if (status === 'unchecked') { + return { + backgroundColor: 'grey' + }; + } + return {}; + } +}; + +const sourceCode2 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = ... + +const selectRow = { + mode: 'checkbox', + clickToSelect: true, + headerColumnStyle: (status) => { + if (status === 'checked') { + return { + backgroundColor: 'yellow' + }; + } else if (status === 'indeterminate') { + return { + backgroundColor: 'pink' + }; + } else if (status === 'unchecked') { + return { + backgroundColor: 'grey' + }; + } + return {}; + } +}; + + +`; + +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 9bd79b81b..d11abfb9c 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -142,6 +142,7 @@ import SelectionWithExpansionTable from 'examples/row-selection/selection-with-e import SelectionNoDataTable from 'examples/row-selection/selection-no-data'; import SelectionStyleTable from 'examples/row-selection/selection-style'; import SelectionClassTable from 'examples/row-selection/selection-class'; +import HeaderStyleTable from 'examples/row-selection/header-style'; import HideSelectAllTable from 'examples/row-selection/hide-select-all'; import CustomSelectionTable from 'examples/row-selection/custom-selection'; import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows'; @@ -385,6 +386,7 @@ storiesOf('Row Selection', module) .add('Selection without Data', () => ) .add('Selection Style', () => ) .add('Selection Class', () => ) + .add('Custom Selection Column Header Style', () => ) .add('Hide Select All', () => ) .add('Custom Selection', () => ) .add('Selection Background Color', () => ) diff --git a/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js b/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js index cb967b502..4d4c5cdf0 100644 --- a/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js +++ b/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js @@ -42,7 +42,7 @@ export const transform = ( cellContent = m.formatter(cellContent, row, rowIndex, m.formatExtraData); } if (m.type === String) { - return `"${cellContent}"`; + return `"${`${cellContent}`.replace(/"/g, '""')}"`; } return cellContent; }).join(separator)).join('\n'); diff --git a/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js b/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js index 64e148fd8..b95092b78 100644 --- a/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js +++ b/packages/react-bootstrap-table2-toolkit/src/search/SearchBar.js @@ -96,7 +96,7 @@ SearchBar.defaultProps = { placeholder: 'Search', delay: 250, searchText: '', - tableId: 0 + tableId: '0' }; export default SearchBar; diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index bc0bc799c..928b54822 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -167,7 +167,8 @@ BootstrapTable.propTypes = { bgColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), hideSelectColumn: PropTypes.bool, selectionRenderer: PropTypes.func, - selectionHeaderRenderer: PropTypes.func + selectionHeaderRenderer: PropTypes.func, + headerColumnStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]) }), expandRow: PropTypes.shape({ renderer: PropTypes.func, diff --git a/packages/react-bootstrap-table2/src/cell.js b/packages/react-bootstrap-table2/src/cell.js index ba1e55111..c3684bd20 100644 --- a/packages/react-bootstrap-table2/src/cell.js +++ b/packages/react-bootstrap-table2/src/cell.js @@ -8,7 +8,7 @@ import _ from './utils'; class Cell extends eventDelegater(Component) { constructor(props) { super(props); - this.handleEditingCell = this.handleEditingCell.bind(this); + this.createHandleEditingCell = this.createHandleEditingCell.bind(this); } shouldComponentUpdate(nextProps) { @@ -43,17 +43,10 @@ class Cell extends eventDelegater(Component) { return shouldUpdate; } - handleEditingCell(e) { - const { column, onStart, rowIndex, columnIndex, clickToEdit, dbclickToEdit } = this.props; - const { events } = column; - if (events) { - if (clickToEdit) { - const customClick = events.onClick; - if (_.isFunction(customClick)) customClick(e); - } else if (dbclickToEdit) { - const customDbClick = events.onDoubleClick; - if (_.isFunction(customDbClick)) customDbClick(e); - } + createHandleEditingCell = originFunc => (e) => { + const { onStart, rowIndex, columnIndex, clickToEdit, dbclickToEdit } = this.props; + if ((clickToEdit || dbclickToEdit) && _.isFunction(originFunc)) { + originFunc(e); } if (onStart) { onStart(rowIndex, columnIndex); @@ -85,9 +78,9 @@ class Cell extends eventDelegater(Component) { } if (clickToEdit && editable) { - attrs.onClick = this.handleEditingCell; + attrs.onClick = this.createHandleEditingCell(attrs.onClick); } else if (dbclickToEdit && editable) { - attrs.onDoubleClick = this.handleEditingCell; + attrs.onDoubleClick = this.createHandleEditingCell(attrs.onDoubleClick); } return ( diff --git a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js index 0c4789aa8..eef22a7ee 100644 --- a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js +++ b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import dataOperator from '../store/operators'; +import _ from '../utils'; const RowExpandContext = React.createContext(); @@ -81,7 +82,7 @@ class RowExpandProvider extends React.Component { if (expandAll) { currExpanded = expanded.concat(dataOperator.expandableKeys(data, keyField, nonExpandable)); } else { - currExpanded = expanded.filter(s => typeof data.find(d => d[keyField] === s) === 'undefined'); + currExpanded = expanded.filter(s => typeof data.find(d => _.get(d, keyField) === s) === 'undefined'); } if (onExpandAll) { diff --git a/packages/react-bootstrap-table2/src/row-selection/selection-header-cell.js b/packages/react-bootstrap-table2/src/row-selection/selection-header-cell.js index 8635bd54d..5d65ce3b2 100644 --- a/packages/react-bootstrap-table2/src/row-selection/selection-header-cell.js +++ b/packages/react-bootstrap-table2/src/row-selection/selection-header-cell.js @@ -3,6 +3,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Const from '../const'; import { BootstrapContext } from '../contexts/bootstrap'; +import _ from '../utils'; export const CheckBox = ({ className, checked, indeterminate }) => ( ; } @@ -79,6 +87,10 @@ export default class SelectionHeaderCell extends Component { attrs.onClick = this.handleCheckBoxClick; } + attrs.style = _.isFunction(headerColumnStyle) ? + headerColumnStyle(checkedStatus) : + headerColumnStyle; + return ( { diff --git a/packages/react-bootstrap-table2/src/store/rows.js b/packages/react-bootstrap-table2/src/store/rows.js index c57de061c..089c5f87a 100644 --- a/packages/react-bootstrap-table2/src/store/rows.js +++ b/packages/react-bootstrap-table2/src/store/rows.js @@ -1,4 +1,5 @@ +import _ from '../utils'; -export const matchRow = (keyField, id) => row => row[keyField] === id; +export const matchRow = (keyField, id) => row => _.get(row, keyField) === id; export const getRowByRowId = (data, keyField, id) => data.find(matchRow(keyField, id)); diff --git a/packages/react-bootstrap-table2/test/cell.test.js b/packages/react-bootstrap-table2/test/cell.test.js index d0ceb950f..a1c7a3a89 100644 --- a/packages/react-bootstrap-table2/test/cell.test.js +++ b/packages/react-bootstrap-table2/test/cell.test.js @@ -124,10 +124,10 @@ describe('Cell', () => { onClick: sinon.stub() }; }); - it('should calling custom onClick callback also', () => { + + it('should call onStart correctly', () => { wrapper.find('td').simulate('click'); expect(onStartCallBack.callCount).toBe(1); - expect(column.events.onClick.callCount).toBe(1); }); }); }); @@ -164,10 +164,10 @@ describe('Cell', () => { onDoubleClick: sinon.stub() }; }); - it('should calling custom onDoubleClick callback also', () => { + + it('should call onStart correctly', () => { wrapper.find('td').simulate('doubleclick'); expect(onStartCallBack.callCount).toBe(1); - expect(column.events.onDoubleClick.callCount).toBe(1); }); }); });