diff --git a/docs/row-selection.md b/docs/row-selection.md index ac7eba7..d7f2e57 100644 --- a/docs/row-selection.md +++ b/docs/row-selection.md @@ -16,6 +16,8 @@ * [onSelect](#onSelect) * [onSelectAll](#onSelectAll) * [hideSelectColumn](#hideSelectColumn) +* [selectionRenderer](#selectionRenderer) +* [selectionHeaderRenderer](#selectionHeaderRenderer) ### selectRow.mode - [String] @@ -156,6 +158,34 @@ const selectRow = { }; ``` +### selectRow.selectionRenderer - [Bool] +Provide a callback function which allow you to custom the checkbox/radio box. This callback only have one argument which is an object and contain following properties: + +```js +const selectRow = { + mode: 'checkbox', + selectionRenderer: ({ mode, checked, disabled }) => ( + // .... + ) +}; +``` + +> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer. + +### selectRow.selectionHeaderRenderer - [Bool] +Provide a callback function which allow you to custom the checkbox/radio box in the selection header column. This callback only have one argument which is an object and contain following properties: + +```js +const selectRow = { + mode: 'checkbox', + selectionHeaderRenderer: ({ mode, checked, indeterminate }) => ( + // .... + ) +}; +``` + +> By default, `react-bootstrap-table2` will help you to handle the click event, it's not necessary to handle again by developer. + ### 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/row-selection/custom-selection.js b/packages/react-bootstrap-table2-example/examples/row-selection/custom-selection.js new file mode 100644 index 0000000..4291c68 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/custom-selection.js @@ -0,0 +1,107 @@ +/* eslint react/prop-types: 0 */ +/* eslint no-param-reassign: 0 */ +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(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const selectRow1 = { + mode: 'radio', + clickToSelect: true, + selectionHeaderRenderer: () => 'X', + selectionRenderer: ({ mode, ...rest }) => ( + + ) +}; + +const selectRow2 = { + mode: 'checkbox', + clickToSelect: true, + selectionHeaderRenderer: ({ indeterminate, ...rest }) => ( + { + if (input) input.indeterminate = indeterminate; + } } + { ...rest } + /> + ), + selectionRenderer: ({ mode, ...rest }) => ( + + ) +}; + +const sourceCode1 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = ....; + +const selectRow = { + mode: 'radio', + clickToSelect: true, + selectionHeaderRenderer: () => 'X', + selectionRenderer: ({ mode, ...rest }) => ( + + ) +}; + + +`; + +const sourceCode2 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = ....; + +const selectRow = { + mode: 'checkbox', + clickToSelect: true, + selectionHeaderRenderer: ({ indeterminate, ...rest }) => ( + { + if (input) input.indeterminate = indeterminate; + } } + { ...rest } + /> + ), + selectionRenderer: ({ mode, ...rest }) => ( + + ) +}; + + +`; + +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 42f2985..a3fe4c3 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -95,6 +95,7 @@ import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-sele 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 CustomSelectionTable from 'examples/row-selection/custom-selection'; import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows'; import SelectionBgColorTable from 'examples/row-selection/selection-bgcolor'; import SelectionHooks from 'examples/row-selection/selection-hooks'; @@ -222,6 +223,7 @@ storiesOf('Row Selection', module) .add('Selection without Data', () => ) .add('Selection Style', () => ) .add('Selection Class', () => ) + .add('Custom Selection', () => ) .add('Selection Background Color', () => ) .add('Not Selectabled Rows', () => ) .add('Selection Hooks', () => ) diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index 91e46cc..27e96ab 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -142,7 +142,9 @@ BootstrapTable.propTypes = { classes: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), nonSelectable: PropTypes.array, bgColor: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), - hideSelectColumn: PropTypes.bool + hideSelectColumn: PropTypes.bool, + selectionRenderer: PropTypes.func, + selectionHeaderRenderer: PropTypes.func }), onRowSelect: PropTypes.func, onAllRowsSelect: PropTypes.func, diff --git a/packages/react-bootstrap-table2/src/row-selection/selection-cell.js b/packages/react-bootstrap-table2/src/row-selection/selection-cell.js index 3050cf2..bec0ef0 100644 --- a/packages/react-bootstrap-table2/src/row-selection/selection-cell.js +++ b/packages/react-bootstrap-table2/src/row-selection/selection-cell.js @@ -14,7 +14,8 @@ export default class SelectionCell extends Component { onRowSelect: PropTypes.func, disabled: PropTypes.bool, rowIndex: PropTypes.number, - clickToSelect: PropTypes.bool + clickToSelect: PropTypes.bool, + selectionRenderer: PropTypes.func } constructor() { @@ -53,16 +54,25 @@ export default class SelectionCell extends Component { const { mode: inputType, selected, - disabled + disabled, + selectionRenderer } = this.props; return ( - + { + selectionRenderer ? selectionRenderer({ + mode: inputType, + checked: selected, + disabled + }) : ( + + ) + } ); } 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 0105bf3..9879026 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 @@ -22,7 +22,8 @@ export default class SelectionHeaderCell extends Component { static propTypes = { mode: PropTypes.string.isRequired, checkedStatus: PropTypes.string, - onAllRowsSelect: PropTypes.func + onAllRowsSelect: PropTypes.func, + selectionHeaderRenderer: PropTypes.func } constructor() { @@ -52,25 +53,37 @@ export default class SelectionHeaderCell extends Component { render() { const { - CHECKBOX_STATUS_CHECKED, CHECKBOX_STATUS_INDETERMINATE, ROW_SELECT_SINGLE + CHECKBOX_STATUS_CHECKED, CHECKBOX_STATUS_INDETERMINATE, ROW_SELECT_MULTIPLE } = Const; - const { mode, checkedStatus } = this.props; + const { mode, checkedStatus, selectionHeaderRenderer } = this.props; const checked = checkedStatus === CHECKBOX_STATUS_CHECKED; const indeterminate = checkedStatus === CHECKBOX_STATUS_INDETERMINATE; - return mode === ROW_SELECT_SINGLE - ? - : ( - - - + const attrs = {}; + let content; + if (selectionHeaderRenderer) { + content = selectionHeaderRenderer({ + mode, + checked, + indeterminate + }); + attrs.onClick = this.handleCheckBoxClick; + } else if (mode === ROW_SELECT_MULTIPLE) { + content = ( + ); + attrs.onClick = this.handleCheckBoxClick; + } + + return ( + { content } + ); } } diff --git a/packages/react-bootstrap-table2/test/row-selection/selection-cell.test.js b/packages/react-bootstrap-table2/test/row-selection/selection-cell.test.js index c0069e2..02e9d85 100644 --- a/packages/react-bootstrap-table2/test/row-selection/selection-cell.test.js +++ b/packages/react-bootstrap-table2/test/row-selection/selection-cell.test.js @@ -193,5 +193,36 @@ describe('', () => { expect(wrapper.find('input').get(0).props.disabled).toBeTruthy(); }); }); + + describe('when selectionRenderer prop is defined', () => { + const DummySelection = () =>
; + const selectionRenderer = jest.fn().mockReturnValue(); + + beforeEach(() => { + selectionRenderer.mockClear(); + wrapper = shallow( + + ); + }); + + it('should render component correctly', () => { + expect(wrapper.find(DummySelection)).toHaveLength(1); + }); + + it('should call props.selectionRenderer correctly', () => { + expect(selectionRenderer).toHaveBeenCalledTimes(1); + expect(selectionRenderer).toHaveBeenCalledWith({ + mode, + checked: selected, + disabled: wrapper.prop('disabled') + }); + }); + }); }); }); diff --git a/packages/react-bootstrap-table2/test/row-selection/selection-header-cell.test.js b/packages/react-bootstrap-table2/test/row-selection/selection-header-cell.test.js index d1e4b03..73571d4 100644 --- a/packages/react-bootstrap-table2/test/row-selection/selection-header-cell.test.js +++ b/packages/react-bootstrap-table2/test/row-selection/selection-header-cell.test.js @@ -126,6 +126,36 @@ describe('', () => { expect(wrapper.find(CheckBox).get(0).props.indeterminate).toBe(indeterminate); }); }); + + describe('when props.selectionHeaderRenderer is defined', () => { + const checkedStatus = Const.CHECKBOX_STATUS_CHECKED; + const DummySelection = () =>
; + const selectionHeaderRenderer = jest.fn().mockReturnValue(); + + beforeEach(() => { + selectionHeaderRenderer.mockClear(); + wrapper = shallow( + + ); + }); + + it('should render correctly', () => { + expect(wrapper.find(DummySelection)).toHaveLength(1); + }); + + it('should call props.selectionHeaderRenderer correctly', () => { + expect(selectionHeaderRenderer).toHaveBeenCalledTimes(1); + expect(selectionHeaderRenderer).toHaveBeenCalledWith({ + mode: 'checkbox', + checked: checkedStatus === Const.CHECKBOX_STATUS_CHECKED, + indeterminate: checkedStatus === Const.CHECKBOX_STATUS_INDETERMINATE + }); + }); + }); }); });