diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..49d515e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: AllenFang + +--- + +**Describe the bug** +A clear and concise description of what the bug is. In addition, please search issues before you open a report to make sure there's no any duplicated report + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**codesandbox** +Please give a simple and minimal example on https://codesandbox.io so that we can reproduce it easily and handle it effectively diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..9ab483b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: AllenFang + +--- + +**Is this feature requested before?** +Please search issues to make sure to create feature which is never report yet. + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/i-have-a-question.md b/.github/ISSUE_TEMPLATE/i-have-a-question.md new file mode 100644 index 0000000..d05d435 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/i-have-a-question.md @@ -0,0 +1,17 @@ +--- +name: I have a question +about: I have a question +title: '' +labels: '' +assignees: AllenFang + +--- + +**Question** +A clear and concise description of you question. In addition, please search issues before you open a question to make sure there's no any duplicated questions. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**codesandbox** +Please give a simple and minimal example on https://codesandbox.io so that we can reproduce it easily. diff --git a/README.md b/README.md index ca05350..290d5ee 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # react-bootstrap-table2 [![Build Status](https://travis-ci.org/react-bootstrap-table/react-bootstrap-table2.svg?branch=master)](https://travis-ci.org/react-bootstrap-table/react-bootstrap-table2) -Rebuilt of [react-bootstrap-table](https://github.com/AllenFang/react-bootstrap-table) +Rebuild of [react-bootstrap-table](https://github.com/AllenFang/react-bootstrap-table) > Note that `react-bootstrap-table2`'s npm module name is [**`react-bootstrap-table-next`**](https://www.npmjs.com/package/react-bootstrap-table-next) due to the name being already taken. diff --git a/docs/README.md b/docs/README.md index dc12d3a..20bbd13 100644 --- a/docs/README.md +++ b/docs/README.md @@ -98,7 +98,14 @@ import overlayFactory from 'react-bootstrap-table2-overlay'; Actually, `react-bootstrap-table-overlay` is depends on [`react-loading-overlay`](https://github.com/derrickpelletier/react-loading-overlay) and `overlayFactory` just a factory function and you can pass any props which available for `react-loading-overlay`: ```js -overlay={ overlayFactory({ spinner: true, background: 'rgba(192,192,192,0.3)' }) } +overlay={ + overlayFactory({ + spinner: true, + styles: { + overlay: (base) => ({...base, background: 'rgba(255, 0, 0, 0.5)'}) + } + }) +} ``` ### caption - [String | Node] @@ -334,4 +341,4 @@ handleDataChange = ({ dataSize }) => { onDataSizeChange={ handleDataChange } .... /> -``` \ No newline at end of file +``` diff --git a/docs/columns.md b/docs/columns.md index 55a4003..906536e 100644 --- a/docs/columns.md +++ b/docs/columns.md @@ -11,6 +11,7 @@ Available properties in a column object: * [hidden](#hidden) * [formatter](#formatter) * [formatExtraData](#formatExtraData) +* [type](#type) * [sort](#sort) * [sortFunc](#sortFunc) * [sortCaret](#sortCaret) @@ -132,6 +133,10 @@ The third argument: `components` have following specified properties: ## column.formatExtraData - [Any] It's only used for [`column.formatter`](#formatter), you can define any value for it and will be passed as fourth argument for [`column.formatter`](#formatter) callback function. +## column.type - [String] +Specify the data type on column. Available value so far is `string`, `number`, `bool` and `date`. Default is `string`. +`column.type` can be used when you enable the cell editing and want to save your cell data with correct data type. + ## column.sort - [Bool] Enable the column sort via a `true` value given. diff --git a/docs/row-expand.md b/docs/row-expand.md index 08198fa..0110fa1 100644 --- a/docs/row-expand.md +++ b/docs/row-expand.md @@ -18,6 +18,7 @@ * [expandColumnPosition](#expandColumnPosition) * [expandColumnRenderer](#expandColumnRenderer) * [expandHeaderColumnRenderer](#expandHeaderColumnRenderer) +* [parentClassName](#parentClassName) ### expandRow.renderer - [Function] @@ -25,12 +26,13 @@ Specify the content of expand row, `react-bootstrap-table2` will pass a row obje #### values * **row** +* **rowIndex** #### examples ```js const expandRow = { - renderer: row => ( + renderer: (row, rowIndex) => (

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

You can render anything here, also you can add additional data on every row object

@@ -165,3 +167,24 @@ const expandRow = { expandColumnPosition: 'right' }; ``` + +### expandRow.parentClassName - [String | Function] +Apply the custom class name on parent row of expanded row. For example: + +```js +const expandRow = { + renderer: (row) => ..., + parentClassName: 'foo' +}; +``` +Below case is more flexible way to custom the class name: + +```js +const expandRow = { + renderer: (row) => ..., + parentClassName: (isExpanded, row, rowIndex) => { + if (rowIndex > 2) return 'foo'; + return 'bar'; + } +}; +``` \ No newline at end of file diff --git a/docs/row-selection.md b/docs/row-selection.md index 18dd991..af2d16a 100644 --- a/docs/row-selection.md +++ b/docs/row-selection.md @@ -16,11 +16,13 @@ * [clickToEdit](#clickToEdit) * [onSelect](#onSelect) * [onSelectAll](#onSelectAll) +* [selectColumnPosition](#selectColumnPosition) * [hideSelectColumn](#hideSelectColumn) * [hideSelectAll](#hideSelectAll) * [selectionRenderer](#selectionRenderer) * [selectionHeaderRenderer](#selectionHeaderRenderer) * [headerColumnStyle](#headerColumnStyle) +* [selectColumnStyle](#selectColumnStyle) ### selectRow.mode - [String] @@ -224,6 +226,42 @@ const selectRow = { }; ``` +### selectRow.selectColumnStyle - [Object | Function] +A way to custome the selection cell. `selectColumnStyle` not only accept a simple style object but also a callback function for more flexible customization: + +### Style Object + +```js +const selectRow = { + mode: 'checkbox', + selectColumnStyle: { backgroundColor: 'blue' } +}; +``` + +### Callback Function +If a callback function present, you can get below information to custom the selection cell: + +* `checked`: Whether current row is seleccted or not +* `disabled`: Whether current row is disabled or not +* `rowIndex`: Current row index +* `rowKey`: Current row key + + +```js +const selectRow = { + mode: 'checkbox', + selectColumnStyle: ({ + checked, + disabled, + rowIndex, + rowKey + }) => ( + // .... + 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`. @@ -275,6 +313,16 @@ const selectRow = { }; ``` +### selectRow.selectColumnPosition - [String] +Default is `left`. You can give this as `right` for rendering selection column in the right side. + +```js +const selectRow = { + mode: 'checkbox', + selectColumnPosition: 'right' +}; +``` + ### selectRow.hideSelectColumn - [Bool] Default is `false`, if you don't want to have a selection column, give this prop as `true` diff --git a/packages/react-bootstrap-table2-editor/README.md b/packages/react-bootstrap-table2-editor/README.md index f5d98aa..cda2e0a 100644 --- a/packages/react-bootstrap-table2-editor/README.md +++ b/packages/react-bootstrap-table2-editor/README.md @@ -89,7 +89,10 @@ const columns = [ In the following, we go though all the predefined editors: ### Dropdown Editor -Dropdown editor give a select menu to choose a data from a list, the `editor.options` is required property for dropdown editor. +Dropdown editor give a select menu to choose a data from a list. When use dropdown editor, either `editor.options` or `editor.getOptions` should be required prop. + +#### editor.options +This is most simple case for assign the dropdown options data directly. ```js import { Type } from 'react-bootstrap-table2-editor'; @@ -119,6 +122,46 @@ const columns = [ }]; ``` +#### editor.getOptions +It is much flexible which accept a function and you can assign the dropdown options dynamically. + +There are two case for `getOptions`: + +* *Synchronous*: Just return the options array in `getOptions` callback function +* *Asynchronous*: Call `setOptions` function argument when you get the options from remote. + + +```js +// Synchronous + +const columns = [ + ..., { + dataField: 'type', + text: 'Job Type', + editor: { + type: Type.SELECT, + getOptions: (setOptions, { row, column }) => [.....] + } +}]; + +// Asynchronous + +const columns = [ + ..., { + dataField: 'type', + text: 'Job Type', + editor: { + type: Type.SELECT, + getOptions: (setOptions, { row, column }) => { + setTimeout(() => setOptions([...]), 1500); + } + } +}]; + +``` + +[here](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html?selectedKind=Cell%20Editing&selectedStory=Dropdown%20Editor%20with%20Dynamic%20Options) is an online example. + ### Date Editor Date editor is use ``, the configuration is very simple: diff --git a/packages/react-bootstrap-table2-editor/package.json b/packages/react-bootstrap-table2-editor/package.json index 23d1837..2f5df7c 100644 --- a/packages/react-bootstrap-table2-editor/package.json +++ b/packages/react-bootstrap-table2-editor/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap-table2-editor", - "version": "1.2.3", + "version": "1.3.2", "description": "it's the editor addon for react-bootstrap-table2", "main": "./lib/index.js", "scripts": { diff --git a/packages/react-bootstrap-table2-editor/src/context.js b/packages/react-bootstrap-table2-editor/src/context.js index e9317e2..ea3c1f0 100644 --- a/packages/react-bootstrap-table2-editor/src/context.js +++ b/packages/react-bootstrap-table2-editor/src/context.js @@ -56,12 +56,13 @@ export default ( } handleCellUpdate(row, column, newValue) { + const newValueWithType = dataOperator.typeConvert(column.type, newValue); const { cellEdit } = this.props; const { beforeSaveCell } = cellEdit.options; const oldValue = _.get(row, column.dataField); const beforeSaveCellDone = (result = true) => { if (result) { - this.doUpdate(row, column, newValue); + this.doUpdate(row, column, newValueWithType); } else { this.escapeEditing(); } @@ -69,7 +70,7 @@ export default ( if (_.isFunction(beforeSaveCell)) { const result = beforeSaveCell( oldValue, - newValue, + newValueWithType, row, column, beforeSaveCellDone @@ -78,7 +79,7 @@ export default ( return; } } - this.doUpdate(row, column, newValue); + this.doUpdate(row, column, newValueWithType); } doUpdate(row, column, newValue) { diff --git a/packages/react-bootstrap-table2-editor/src/dropdown-editor.js b/packages/react-bootstrap-table2-editor/src/dropdown-editor.js index 166a208..b019e2e 100644 --- a/packages/react-bootstrap-table2-editor/src/dropdown-editor.js +++ b/packages/react-bootstrap-table2-editor/src/dropdown-editor.js @@ -4,6 +4,21 @@ import cs from 'classnames'; import PropTypes from 'prop-types'; class DropDownEditor extends Component { + constructor(props) { + super(props); + let options = props.options; + if (props.getOptions) { + options = props.getOptions( + this.setOptions.bind(this), + { + row: props.row, + column: props.column + } + ) || []; + } + this.state = { options }; + } + componentDidMount() { const { defaultValue, didMount } = this.props; this.select.value = defaultValue; @@ -11,12 +26,16 @@ class DropDownEditor extends Component { if (didMount) didMount(); } + setOptions(options) { + this.setState({ options }); + } + getValue() { return this.select.value; } render() { - const { defaultValue, didMount, className, options, ...rest } = this.props; + const { defaultValue, didMount, getOptions, className, ...rest } = this.props; const editorClass = cs('form-control editor edit-select', className); const attr = { @@ -31,7 +50,7 @@ class DropDownEditor extends Component { defaultValue={ defaultValue } > { - options.map(({ label, value }) => ( + this.state.options.map(({ label, value }) => ( )) } @@ -41,6 +60,8 @@ class DropDownEditor extends Component { } DropDownEditor.propTypes = { + row: PropTypes.object.isRequired, + column: PropTypes.object.isRequired, defaultValue: PropTypes.oneOfType([ PropTypes.string, PropTypes.number @@ -52,13 +73,16 @@ DropDownEditor.propTypes = { label: PropTypes.string, value: PropTypes.any })) - ]).isRequired, - didMount: PropTypes.func + ]), + didMount: PropTypes.func, + getOptions: PropTypes.func }; DropDownEditor.defaultProps = { className: '', defaultValue: '', style: {}, - didMount: undefined + options: [], + didMount: undefined, + getOptions: undefined }; export default DropDownEditor; diff --git a/packages/react-bootstrap-table2-editor/src/editing-cell.js b/packages/react-bootstrap-table2-editor/src/editing-cell.js index 7c693b1..2a0d85b 100644 --- a/packages/react-bootstrap-table2-editor/src/editing-cell.js +++ b/packages/react-bootstrap-table2-editor/src/editing-cell.js @@ -201,7 +201,7 @@ export default (_, onStartEdit) => if (_.isFunction(column.editorRenderer)) { editor = column.editorRenderer(editorProps, value, row, column, rowIndex, columnIndex); } else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.SELECT) { - editor = ; + editor = ; } else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.TEXTAREA) { editor = ; } else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.CHECKBOX) { diff --git a/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-with-data-type.js b/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-with-data-type.js new file mode 100644 index 0000000..28a064b --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-with-data-type.js @@ -0,0 +1,123 @@ +/* eslint prefer-template: 0 */ +import React from 'react'; +import BootstrapTable from 'react-bootstrap-table-next'; +import cellEditFactory, { Type } from 'react-bootstrap-table2-editor'; +import Code from 'components/common/code-block'; +import { stockGenerator } from 'utils/common'; + +const products = stockGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Stock ID' +}, { + dataField: 'name', + text: 'Stock Name' +}, { + dataField: 'price', + text: 'Price', + type: 'number' +}, { + dataField: 'visible', + text: 'Visible?', + type: 'bool', + editor: { + type: Type.CHECKBOX, + value: 'true:false' + } +}, { + dataField: 'inStockDate', + text: 'Stock Date', + type: 'date', + formatter: (cell) => { + let dateObj = cell; + if (typeof cell !== 'object') { + dateObj = new Date(cell); + } + return `${('0' + dateObj.getUTCDate()).slice(-2)}/${('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/${dateObj.getUTCFullYear()}`; + }, + editor: { + type: Type.DATE + } +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import cellEditFactory from 'react-bootstrap-table2-editor'; + +const columns = [{ + dataField: 'id', + text: 'Stock ID' +}, { + dataField: 'name', + text: 'Stock Name' +}, { + dataField: 'price', + text: 'Price', + type: 'number' +}, { + dataField: 'visible', + text: 'Visible?', + type: 'bool', + editor: { + type: Type.CHECKBOX, + value: 'true:false' + } +}, { + dataField: 'inStockDate', + text: 'Stock Date', + type: 'date', + formatter: (cell) => { + let dateObj = cell; + if (typeof cell !== 'object') { + dateObj = new Date(cell); + } + return \`$\{('0' + dateObj.getUTCDate()).slice(-2)}/$\{('0' + (dateObj.getUTCMonth() + 1)).slice(-2)}/$\{dateObj.getUTCFullYear()}\`; + }, + editor: { + type: Type.DATE + } +}]; + +function afterSaveCell(oldValue, newValue) { + console.log('--after save cell--'); + console.log('New Value was apply as'); + console.log(newValue); + console.log(\`and the type is $\{typeof newValue}\`); +} + + +`; + +function afterSaveCell(oldValue, newValue) { + console.log('--after save cell--'); + console.log('New Value was apply as'); + console.log(newValue); + console.log(`and the type is ${typeof newValue}`); +} + +export default () => ( +
+

Save Cell Value with Specified Data Type

+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/cell-edit/dropdown-editor-with-dynamic-options-table.js b/packages/react-bootstrap-table2-example/examples/cell-edit/dropdown-editor-with-dynamic-options-table.js new file mode 100644 index 0000000..02c822b --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/cell-edit/dropdown-editor-with-dynamic-options-table.js @@ -0,0 +1,164 @@ +/* eslint no-console: 0 */ +/* eslint react/prefer-stateless-function: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import cellEditFactory, { Type } from 'react-bootstrap-table2-editor'; +import Code from 'components/common/code-block'; +import { jobsGenerator } from 'utils/common'; + +const jobs = jobsGenerator().map(j => ({ + ...j, + type2: j.type +})); + +const columns = [{ + dataField: 'id', + text: 'Job ID' +}, { + dataField: 'name', + text: 'Job Name' +}, { + dataField: 'owner', + text: 'Job Owner' +}, { + dataField: 'type', + text: 'Job Type1', + editor: { + type: Type.SELECT, + getOptions: (setOptions, { row, column }) => { + console.log(`current editing row id: ${row.id}`); + console.log(`current editing column: ${column.dataField}`); + return [{ + value: 'A', + label: 'A' + }, { + value: 'B', + label: 'B' + }, { + value: 'C', + label: 'C' + }, { + value: 'D', + label: 'D' + }, { + value: 'E', + label: 'E' + }]; + } + } +}, { + dataField: 'type2', + text: 'Job Type2', + editor: { + type: Type.SELECT, + getOptions: (setOptions) => { + setTimeout(() => { + setOptions([{ + value: 'A', + label: 'A' + }, { + value: 'B', + label: 'B' + }, { + value: 'C', + label: 'C' + }, { + value: 'D', + label: 'D' + }, { + value: 'E', + label: 'E' + }]); + }, 2000); + } + } +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import cellEditFactory, { Type } from 'react-bootstrap-table2-editor'; + +const columns = [{ + dataField: 'id', + text: 'Job ID' +}, { + dataField: 'name', + text: 'Job Name' +}, { + dataField: 'owner', + text: 'Job Owner' +}, { + dataField: 'type', + text: 'Job Type1', + editor: { + type: Type.SELECT, + getOptions: (setOptions, { row, column }) => { + console.log(\`current editing row id: $\{row.id}\`); + console.log(\`current editing column: $\{column.dataField}\`); + return [{ + value: 'A', + label: 'A' + }, { + value: 'B', + label: 'B' + }, { + value: 'C', + label: 'C' + }, { + value: 'D', + label: 'D' + }, { + value: 'E', + label: 'E' + }]; + } + } +}, { + dataField: 'type2', + text: 'Job Type2', + editor: { + type: Type.SELECT, + getOptions: (setOptions) => { + setTimeout(() => { + setOptions([{ + value: 'A', + label: 'A' + }, { + value: 'B', + label: 'B' + }, { + value: 'C', + label: 'C' + }, { + value: 'D', + label: 'D' + }, { + value: 'E', + label: 'E' + }]); + }, 2000); + } + } +}]; + + +`; + +export default () => ( +
+

Dropdown Editor with Dynamic Options

+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-filter/custom-filter-logic.js b/packages/react-bootstrap-table2-example/examples/column-filter/custom-filter-logic.js index 906d9d0..a861f5a 100644 --- a/packages/react-bootstrap-table2-example/examples/column-filter/custom-filter-logic.js +++ b/packages/react-bootstrap-table2-example/examples/column-filter/custom-filter-logic.js @@ -12,8 +12,12 @@ import BootstrapTable from 'react-bootstrap-table-next'; import filterFactory, { textFilter } from 'react-bootstrap-table2-filter'; class Table extends React.Component { - filterByPrice = filterVal => - products.filter(product => product.price == filterVal); + filterByPrice = (filterVal, data) => { + if (filterVal) { + return data.filter(product => product.price == filterVal); + } + return data; + } render() { const columns = [{ @@ -46,8 +50,12 @@ class Table extends React.Component { `; export default class Table extends React.Component { - filterByPrice = filterVal => - products.filter(product => product.price == filterVal); + filterByPrice = (filterVal, data) => { + if (filterVal) { + return data.filter(product => product.price == filterVal); + } + return data; + } render() { const columns = [{ @@ -67,7 +75,7 @@ export default class Table extends React.Component { return (
-

Implement a eq filter on product price column

+

Implement a eq price filter

+ { + props => ( +
+ +
+ +
+ ) + } + +`; + +export default () => ( +
+

Table will keep the filter/sort state when column toggle

+ + { + props => ( +
+ +
+ +
+ ) + } +
+ { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/columns/row-expand-with-formatted-dummy-column.js b/packages/react-bootstrap-table2-example/examples/columns/row-expand-with-formatted-dummy-column.js new file mode 100644 index 0000000..a7a7ea3 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/columns/row-expand-with-formatted-dummy-column.js @@ -0,0 +1,188 @@ +/* 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 sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +class DummyColumnWithRowExpand extends React.Component { + constructor(props) { + super(props); + + this.state = { + hoverIdx: null + }; + } + + expandRow = { + renderer: () => ( +
Content
+ ), + showExpandColumn: true, + expandByColumnOnly: true + }; + + actionFormater = (cell, row, rowIndex, { hoverIdx }) => { + if ((hoverIdx !== null || hoverIdx !== undefined) && hoverIdx === rowIndex) { + return ( +
+ ); + } + return ( +
+ ); + } + + rowEvents = { + onMouseEnter: (e, row, rowIndex) => { + this.setState({ hoverIdx: rowIndex }); + }, + onMouseLeave: () => { + this.setState({ hoverIdx: null }); + } + } + + rowStyle = (row, rowIndex) => { + row.index = rowIndex; + const style = {}; + if (rowIndex % 2 === 0) { + style.backgroundColor = 'transparent'; + } else { + style.backgroundColor = 'rgba(54, 163, 173, .10)'; + } + style.borderTop = 'none'; + + return style; + } + + render() { + const columns = [{ + dataField: 'id', + text: 'Product ID' + }, { + dataField: 'name', + text: 'Product Name' + }, { + dataField: 'price', + text: 'Product Price' + }, { + text: '', + isDummyField: true, + formatter: this.actionFormater, + formatExtraData: { hoverIdx: this.state.hoverIdx }, + headerStyle: { width: '50px' }, + style: { height: '30px' } + }]; + return ( +
+ +
+ ); + } +} +`; + +export default class DummyColumnWithRowExpand extends React.Component { + constructor(props) { + super(props); + + this.state = { + hoverIdx: null + }; + } + + expandRow = { + renderer: () => ( +
Content
+ ), + showExpandColumn: true, + expandByColumnOnly: true + }; + + actionFormater = (cell, row, rowIndex, { hoverIdx }) => { + if ((hoverIdx !== null || hoverIdx !== undefined) && hoverIdx === rowIndex) { + return ( +
+ ); + } + return ( +
+ ); + } + + rowEvents = { + onMouseEnter: (e, row, rowIndex) => { + this.setState({ hoverIdx: rowIndex }); + }, + onMouseLeave: () => { + this.setState({ hoverIdx: null }); + } + } + + rowStyle = (row, rowIndex) => { + row.index = rowIndex; + const style = {}; + if (rowIndex % 2 === 0) { + style.backgroundColor = 'transparent'; + } else { + style.backgroundColor = 'rgba(54, 163, 173, .10)'; + } + style.borderTop = 'none'; + + return style; + } + + render() { + const columns = [{ + dataField: 'id', + text: 'Product ID' + }, { + dataField: 'name', + text: 'Product Name' + }, { + dataField: 'price', + text: 'Product Price' + }, { + isDummyField: true, + text: '', + formatter: this.actionFormater, + formatExtraData: { hoverIdx: this.state.hoverIdx }, + headerStyle: { width: '50px' }, + style: { height: '30px' } + }]; + return ( +
+ + { sourceCode } +
+ ); + } +} diff --git a/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js b/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js index efd296c..13ce1b1 100644 --- a/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js +++ b/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js @@ -36,7 +36,7 @@ const RemotePagination = ({ loading, data, page, sizePerPage, onTableChange, tot columns={ columns } pagination={ paginationFactory({ page, sizePerPage, totalSize }) } onTableChange={ onTableChange } - overlay={ overlayFactory({ spinner: true, background: 'rgba(192,192,192,0.3)' }) } + overlay={ overlayFactory({ spinner: true, styles: { overlay: (base) => ({...base, background: 'rgba(255, 0, 0, 0.5)'}) } }) } /> { sourceCode }
@@ -101,7 +101,12 @@ const RemotePagination = ({ loading, data, page, sizePerPage, onTableChange, tot columns={ columns } pagination={ paginationFactory({ page, sizePerPage, totalSize }) } onTableChange={ onTableChange } - overlay={ overlayFactory({ spinner: true, background: 'rgba(192,192,192,0.3)' }) } + overlay={ + overlayFactory({ + spinner: true, + styles: { overlay: base => ({ ...base, background: 'rgba(255, 0, 0, 0.5)' }) } + }) + } /> { sourceCode }
diff --git a/packages/react-bootstrap-table2-example/examples/pagination/custom-pagination.js b/packages/react-bootstrap-table2-example/examples/pagination/custom-pagination.js index 4ea5164..f456904 100644 --- a/packages/react-bootstrap-table2-example/examples/pagination/custom-pagination.js +++ b/packages/react-bootstrap-table2-example/examples/pagination/custom-pagination.js @@ -74,7 +74,7 @@ const options = { // hidePageListOnlyOnePage: true, // Hide the pagination list when only one page firstPageText: 'First', prePageText: 'Back', - nextPageText: 'Next', + nextPageText: Next, lastPageText: 'Last', nextPageTitle: 'First page', prePageTitle: 'Pre page', diff --git a/packages/react-bootstrap-table2-example/examples/row-expand/index.js b/packages/react-bootstrap-table2-example/examples/row-expand/index.js index 0a08904..eb21023 100644 --- a/packages/react-bootstrap-table2-example/examples/row-expand/index.js +++ b/packages/react-bootstrap-table2-example/examples/row-expand/index.js @@ -18,9 +18,9 @@ const columns = [{ }]; const expandRow = { - renderer: row => ( + renderer: (row, rowIndex) => (
-

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

+

{ `This Expand row is belong to rowKey ${row.id} and index: ${rowIndex}` }

You can render anything here, also you can add additional data on every row object

expandRow.renderer callback will pass the origin row object to you

diff --git a/packages/react-bootstrap-table2-example/examples/row-expand/parent-row-classname.js b/packages/react-bootstrap-table2-example/examples/row-expand/parent-row-classname.js new file mode 100644 index 0000000..d384c37 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-expand/parent-row-classname.js @@ -0,0 +1,106 @@ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import Code from 'components/common/code-block'; +import { productsExpandRowsGenerator } from 'utils/common'; + +const products = productsExpandRowsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const expandRow1 = { + parentClassName: 'parent-expand-foo', + renderer: row => ( +
+

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

+

You can render anything here, also you can add additional data on every row object

+

expandRow.renderer callback will pass the origin row object to you

+
+ ) +}; + +const expandRow2 = { + parentClassName: (isExpanded, row, rowIndex) => { + if (rowIndex > 2) return 'parent-expand-foo'; + return 'parent-expand-bar'; + }, + renderer: row => ( +
+

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

+

You can render anything here, also you can add additional data on every row object

+

expandRow.renderer callback will pass the origin row object to you

+
+ ) +}; + + +const sourceCode1 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = // omit... + +const expandRow = { + parentClassName: 'parent-expand-foo', + renderer: row => ( +
.....
+ ) +}; + + +`; + +const sourceCode2 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = // omit... + +const expandRow = { + parentClassName: (isExpanded, row, rowIndex) => { + if (rowIndex > 2) return 'parent-expand-foo'; + return 'parent-expand-bar'; + }, + renderer: row => ( +
...
+ ) +}; + + +`; + +export default () => ( +
+ + { sourceCode1 } + + { sourceCode2 } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/row-selection/select-column-style.js b/packages/react-bootstrap-table2-example/examples/row-selection/select-column-style.js new file mode 100644 index 0000000..fd1e237 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/select-column-style.js @@ -0,0 +1,110 @@ +/* eslint no-unused-vars: 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(2); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const selectRow1 = { + mode: 'checkbox', + clickToSelect: true, + selectColumnStyle: { + backgroundColor: 'grey' + } +}; + +const sourceCode1 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = ... + +const selectRow = { + mode: 'checkbox', + clickToSelect: true, + selectColumnStyle: { + backgroundColor: 'grey' + } +}; + + +`; + +const selectRow2 = { + mode: 'checkbox', + clickToSelect: true, + selectColumnStyle: ({ + checked, + disabled, + rowIndex, + rowKey + }) => { + if (checked) { + return { + backgroundColor: 'yellow' + }; + } + return { + backgroundColor: 'pink' + }; + } +}; + +const sourceCode2 = `\ +import BootstrapTable from 'react-bootstrap-table-next'; + +const columns = ... + +const selectRow = { + mode: 'checkbox', + clickToSelect: true, + selectColumnStyle: ({ + checked, + disabled, + rowIndex, + rowKey + }) => { + if (checked) { + return { + backgroundColor: 'yellow' + }; + } + return { + backgroundColor: 'pink' + }; + } +}; + + +`; + +export default () => ( +
+ + { sourceCode1 } + + { sourceCode2 } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/row-selection/selection-column-position.js b/packages/react-bootstrap-table2-example/examples/row-selection/selection-column-position.js new file mode 100644 index 0000000..efe7338 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/row-selection/selection-column-position.js @@ -0,0 +1,59 @@ +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 selectRow = { + mode: 'checkbox', + clickToSelect: true, + selectColumnPosition: 'right' +}; + +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' +}]; + +const selectRow = { + mode: 'checkbox', + clickToSelect: true, + selectColumnPosition: 'right' +}; + + +`; + +export default () => ( +
+ + { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/package.json b/packages/react-bootstrap-table2-example/package.json index f48c610..f38f1c9 100644 --- a/packages/react-bootstrap-table2-example/package.json +++ b/packages/react-bootstrap-table2-example/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap-table2-example", - "version": "1.0.25", + "version": "1.0.29", "description": "", "main": "index.js", "private": true, diff --git a/packages/react-bootstrap-table2-example/src/utils/common.js b/packages/react-bootstrap-table2-example/src/utils/common.js index beea31b..f6baec2 100644 --- a/packages/react-bootstrap-table2-example/src/utils/common.js +++ b/packages/react-bootstrap-table2-example/src/utils/common.js @@ -69,8 +69,9 @@ const endDate = new Date(); export const stockGenerator = (quantity = 5) => Array.from({ length: quantity }, (value, index) => ({ id: index, - name: `Todo item ${index}`, + name: `Stock Name ${index}`, price: Math.floor((Math.random() * 2) + 1), + visible: Math.random() > 0.5, inStockDate: new Date(startDate.getTime() + Math.random() * (endDate.getTime() - startDate.getTime())) })); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index d11abfb..6acd9b9 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -35,6 +35,7 @@ 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'; +import RowExpandWithFormattedDummyColumn from 'examples/columns/row-expand-with-formatted-dummy-column.js'; // work on header columns import HeaderColumnFormatTable from 'examples/header-columns/column-format-table'; @@ -125,10 +126,12 @@ import EditorStyleTable from 'examples/cell-edit/editor-style-table'; import EditorClassTable from 'examples/cell-edit/editor-class-table'; import DBClickEditWithSelection from 'examples/cell-edit/dbclick-to-edit-with-selection-table'; import DropdownEditorTable from 'examples/cell-edit/dropdown-editor-table'; +import DropdownEditorWithDynamicOptionsTable from 'examples/cell-edit/dropdown-editor-with-dynamic-options-table'; import TextareaEditorTable from 'examples/cell-edit/textarea-editor-table'; import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table'; import DateEditorTable from 'examples/cell-edit/date-editor-table'; import CustomEditorTable from 'examples/cell-edit/custom-editor-table'; +import CellEditorWithDataType from 'examples/cell-edit/cell-edit-with-data-type'; // work on row selection import SingleSelectionTable from 'examples/row-selection/single-selection'; @@ -149,6 +152,8 @@ 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'; import HideSelectionColumnTable from 'examples/row-selection/hide-selection-column'; +import SelectionColumnStyleTable from 'examples/row-selection/select-column-style'; +import SelectionColumnPositionTable from 'examples/row-selection/selection-column-position'; // work on row expand import BasicRowExpand from 'examples/row-expand'; @@ -160,6 +165,7 @@ import ExpandOnlyOne from 'examples/row-expand/expand-only-one'; import CustomExpandColumn from 'examples/row-expand/custom-expand-column'; import ExpandColumnPosition from 'examples/row-expand/expand-column-position'; import ExpandHooks from 'examples/row-expand/expand-hooks'; +import ParentRowClassName from 'examples/row-expand/parent-row-classname'; // pagination import PaginationTable from 'examples/pagination'; @@ -204,6 +210,7 @@ import BasicColumnToggle from 'examples/column-toggle'; import DefaultVisibility from 'examples/column-toggle/default-visibility'; import StylingColumnToggle from 'examples/column-toggle/styling-toggle-list'; import CustomToggleList from 'examples/column-toggle/custom-toggle-list'; +import ColumnToggleWithFilter from 'examples/column-toggle/column-toggle-with-filter'; // loading overlay import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay'; @@ -270,7 +277,8 @@ storiesOf('Work on Columns', module) .add('Customize Column Class', () => ) .add('Customize Column Style', () => ) .add('Customize Column HTML attribute', () => ) - .add('Dummy Column', () => ); + .add('Dummy Column', () => ) + .add('Row Expand with Dummy Column Formatter', () => ); storiesOf('Work on Header Columns', module) .addDecorator(bootstrapStyle()) @@ -368,10 +376,12 @@ storiesOf('Cell Editing', module) .add('Custom Editor Style', () => ) .add('DoubleClick to Edit with Selection', () => ) .add('Dropdown Editor', () => ) + .add('Dropdown Editor with Dynamic Options', () => ) .add('Textarea Editor', () => ) .add('Checkbox Editor', () => ) .add('Date Editor', () => ) - .add('Custom Editor', () => ); + .add('Custom Editor', () => ) + .add('Cell Editor with Data Type', () => ); storiesOf('Row Selection', module) .addDecorator(bootstrapStyle()) @@ -392,7 +402,9 @@ storiesOf('Row Selection', module) .add('Selection Background Color', () => ) .add('Not Selectabled Rows', () => ) .add('Selection Hooks', () => ) - .add('Hide Selection Column', () => ); + .add('Hide Selection Column', () => ) + .add('Custom Selection Column Style', () => ) + .add('Selection Column Position', () => ); storiesOf('Row Expand', module) .addDecorator(bootstrapStyle()) @@ -404,7 +416,8 @@ storiesOf('Row Expand', module) .add('Expand Only One Row at The Same Time', () => ) .add('Custom Expand Indicator', () => ) .add('Expand Column Position', () => ) - .add('Expand Hooks', () => ); + .add('Expand Hooks', () => ) + .add('Custom Parent Row ClassName', () => ); storiesOf('Pagination', module) .addDecorator(bootstrapStyle()) @@ -431,7 +444,7 @@ storiesOf('Table Search', module) .add('Default Search Table', () => ) .add('Default Custom Search', () => ) .add('Fully Custom Search', () => ) - .add('Search Fromatted Value', () => ) + .add('Search Formatted Value', () => ) .add('Custom Search Value', () => ); storiesOf('Column Toggle', module) @@ -439,7 +452,8 @@ storiesOf('Column Toggle', module) .add('Basic Column Toggle', () => ) .add('Default Visibility', () => ) .add('Styling Column Toggle', () => ) - .add('Custom Column Toggle', () => ); + .add('Custom Column Toggle', () => ) + .add('Column Toggle with Filter', () => ); storiesOf('Export CSV', module) .addDecorator(bootstrapStyle()) diff --git a/packages/react-bootstrap-table2-example/stories/stylesheet/row-expand/_index.scss b/packages/react-bootstrap-table2-example/stories/stylesheet/row-expand/_index.scss new file mode 100644 index 0000000..0693da3 --- /dev/null +++ b/packages/react-bootstrap-table2-example/stories/stylesheet/row-expand/_index.scss @@ -0,0 +1,7 @@ +.parent-expand-foo { + background-color: coral; +} + +.parent-expand-bar { + background-color: aqua; +} \ 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 14959a6..435b846 100644 --- a/packages/react-bootstrap-table2-example/stories/stylesheet/storybook.scss +++ b/packages/react-bootstrap-table2-example/stories/stylesheet/storybook.scss @@ -12,3 +12,4 @@ @import "sort/index"; @import "search/index"; @import "loading-overlay/index"; +@import "row-expand/index"; \ No newline at end of file diff --git a/packages/react-bootstrap-table2-filter/package.json b/packages/react-bootstrap-table2-filter/package.json index 08bc8a8..e2b298e 100644 --- a/packages/react-bootstrap-table2-filter/package.json +++ b/packages/react-bootstrap-table2-filter/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap-table2-filter", - "version": "1.1.9", + "version": "1.1.11", "description": "it's a column filter addon for react-bootstrap-table2", "main": "./lib/index.js", "repository": { diff --git a/packages/react-bootstrap-table2-filter/src/components/date.js b/packages/react-bootstrap-table2-filter/src/components/date.js index 578f558..3d8ff45 100644 --- a/packages/react-bootstrap-table2-filter/src/components/date.js +++ b/packages/react-bootstrap-table2-filter/src/components/date.js @@ -83,14 +83,27 @@ class DateFilter extends Component { return optionTags; } - getDefaultDate() { - let defaultDate = ''; - const { defaultValue } = this.props; - if (defaultValue && defaultValue.date) { - // Set the appropriate format for the input type=date, i.e. "YYYY-MM-DD" - defaultDate = dateParser(new Date(defaultValue.date)); + getDefaultComparator() { + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal) { + return filterState.filterVal.comparator; } - return defaultDate; + if (defaultValue && defaultValue.comparator) { + return defaultValue.comparator; + } + return ''; + } + + getDefaultDate() { + // Set the appropriate format for the input type=date, i.e. "YYYY-MM-DD" + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal && filterState.filterVal.date) { + return dateParser(filterState.filterVal.date); + } + if (defaultValue && defaultValue.date) { + return dateParser(new Date(defaultValue.date)); + } + return ''; } applyFilter(value, comparator, isInitial) { @@ -122,8 +135,7 @@ class DateFilter extends Component { dateStyle, className, comparatorClassName, - dateClassName, - defaultValue + dateClassName } = this.props; return ( @@ -143,7 +155,7 @@ class DateFilter extends Component { style={ comparatorStyle } className={ `date-filter-comparator form-control ${comparatorClassName}` } onChange={ this.onChangeComparator } - defaultValue={ defaultValue ? defaultValue.comparator : '' } + defaultValue={ this.getDefaultComparator() } > { this.getComparatorOptions() } @@ -169,6 +181,7 @@ class DateFilter extends Component { DateFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, delay: PropTypes.number, defaultValue: PropTypes.shape({ date: PropTypes.oneOfType([PropTypes.object]), @@ -210,6 +223,7 @@ DateFilter.defaultProps = { date: undefined, comparator: '' }, + filterState: {}, withoutEmptyComparatorOption: false, comparators: legalComparators, placeholder: undefined, diff --git a/packages/react-bootstrap-table2-filter/src/components/multiselect.js b/packages/react-bootstrap-table2-filter/src/components/multiselect.js index 2de86f9..1d8794c 100644 --- a/packages/react-bootstrap-table2-filter/src/components/multiselect.js +++ b/packages/react-bootstrap-table2-filter/src/components/multiselect.js @@ -18,8 +18,18 @@ function optionsEquals(currOpts, prevOpts) { return Object.keys(currOpts).length === Object.keys(prevOpts).length; } -const getSelections = container => - Array.from(container.selectedOptions).map(item => item.value); +const getSelections = (container) => { + if (container.selectedOptions) { + return Array.from(container.selectedOptions).map(item => item.value); + } + const selections = []; + const totalLen = container.options.length; + for (let i = 0; i < totalLen; i += 1) { + const option = container.options.item(i); + if (option.selected) selections.push(option.value); + } + return selections; +}; class MultiSelectFilter extends Component { constructor(props) { @@ -55,10 +65,18 @@ class MultiSelectFilter extends Component { needFilter = true; } if (needFilter) { - this.applyFilter(this.selectInput.value); + this.applyFilter(getSelections(this.selectInput)); } } + getDefaultValue() { + const { filterState, defaultValue } = this.props; + if (filterState && typeof filterState.filterVal !== 'undefined') { + return filterState.filterVal; + } + return defaultValue; + } + getOptions() { const optionTags = []; const { options, placeholder, column, withoutEmptyOption } = this.props; @@ -96,6 +114,7 @@ class MultiSelectFilter extends Component { const { style, className, + filterState, defaultValue, onFilter, column, @@ -125,7 +144,7 @@ class MultiSelectFilter extends Component { className={ selectClass } onChange={ this.filter } onClick={ e => e.stopPropagation() } - defaultValue={ defaultValue !== undefined ? defaultValue : '' } + defaultValue={ this.getDefaultValue() } > { this.getOptions() } @@ -138,6 +157,7 @@ MultiSelectFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, options: PropTypes.object.isRequired, + filterState: PropTypes.object, comparator: PropTypes.oneOf([LIKE, EQ]), placeholder: PropTypes.string, style: PropTypes.object, @@ -150,6 +170,7 @@ MultiSelectFilter.propTypes = { MultiSelectFilter.defaultProps = { defaultValue: [], + filterState: {}, className: '', withoutEmptyOption: false, comparator: EQ, diff --git a/packages/react-bootstrap-table2-filter/src/components/number.js b/packages/react-bootstrap-table2-filter/src/components/number.js index 29e0370..7abea58 100644 --- a/packages/react-bootstrap-table2-filter/src/components/number.js +++ b/packages/react-bootstrap-table2-filter/src/components/number.js @@ -94,6 +94,28 @@ class NumberFilter extends Component { onFilter(column, FILTER_TYPE.NUMBER)({ number: value, comparator }); } + getDefaultComparator() { + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal) { + return filterState.filterVal.comparator; + } + if (defaultValue && defaultValue.comparator) { + return defaultValue.comparator; + } + return ''; + } + + getDefaultValue() { + const { defaultValue, filterState } = this.props; + if (filterState && filterState.filterVal) { + return filterState.filterVal.number; + } + if (defaultValue && defaultValue.number) { + return defaultValue.number; + } + return ''; + } + getComparatorOptions() { const optionTags = []; const { withoutEmptyComparatorOption } = this.props; @@ -148,7 +170,6 @@ class NumberFilter extends Component { render() { const { isSelected } = this.state; const { - defaultValue, column, options, style, @@ -184,7 +205,7 @@ class NumberFilter extends Component { id={ `number-filter-comparator-${column.text}` } className={ `number-filter-comparator form-control ${comparatorClassName}` } onChange={ this.onChangeComparator } - defaultValue={ defaultValue ? defaultValue.comparator : '' } + defaultValue={ this.getDefaultComparator() } > { this.getComparatorOptions() } @@ -202,7 +223,7 @@ class NumberFilter extends Component { style={ numberStyle } className={ selectClass } onChange={ this.onChangeNumberSet } - defaultValue={ defaultValue ? defaultValue.number : '' } + defaultValue={ this.getDefaultValue() } > { this.getNumberOptions() } @@ -217,7 +238,7 @@ class NumberFilter extends Component { className={ `number-filter-input form-control ${numberClassName}` } placeholder={ placeholder || `Enter ${column.text}...` } onChange={ this.onChangeNumber } - defaultValue={ defaultValue ? defaultValue.number : '' } + defaultValue={ this.getDefaultValue() } /> } @@ -229,6 +250,7 @@ class NumberFilter extends Component { NumberFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, options: PropTypes.arrayOf(PropTypes.number), defaultValue: PropTypes.shape({ number: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), @@ -273,6 +295,7 @@ NumberFilter.defaultProps = { number: undefined, comparator: '' }, + filterState: {}, withoutEmptyComparatorOption: false, withoutEmptyNumberOption: false, comparators: legalComparators, diff --git a/packages/react-bootstrap-table2-filter/src/components/select.js b/packages/react-bootstrap-table2-filter/src/components/select.js index db5348a..7b83827 100644 --- a/packages/react-bootstrap-table2-filter/src/components/select.js +++ b/packages/react-bootstrap-table2-filter/src/components/select.js @@ -44,7 +44,7 @@ class SelectFilter extends Component { constructor(props) { super(props); this.filter = this.filter.bind(this); - const isSelected = getOptionValue(props.options, props.defaultValue) !== undefined; + const isSelected = getOptionValue(props.options, this.getDefaultValue()) !== undefined; this.state = { isSelected }; } @@ -82,6 +82,14 @@ class SelectFilter extends Component { } } + getDefaultValue() { + const { filterState, defaultValue } = this.props; + if (filterState && typeof filterState.filterVal !== 'undefined') { + return filterState.filterVal; + } + return defaultValue; + } + getOptions() { const optionTags = []; const { options, placeholder, column, withoutEmptyOption } = this.props; @@ -132,6 +140,7 @@ class SelectFilter extends Component { withoutEmptyOption, caseSensitive, getFilter, + filterState, ...rest } = this.props; @@ -152,7 +161,7 @@ class SelectFilter extends Component { className={ selectClass } onChange={ this.filter } onClick={ e => e.stopPropagation() } - defaultValue={ defaultValue !== undefined ? defaultValue : '' } + defaultValue={ this.getDefaultValue() || '' } > { this.getOptions() } @@ -164,6 +173,7 @@ class SelectFilter extends Component { SelectFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, options: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired, comparator: PropTypes.oneOf([LIKE, EQ]), placeholder: PropTypes.string, @@ -177,6 +187,7 @@ SelectFilter.propTypes = { SelectFilter.defaultProps = { defaultValue: '', + filterState: {}, className: '', withoutEmptyOption: false, comparator: EQ, diff --git a/packages/react-bootstrap-table2-filter/src/components/text.js b/packages/react-bootstrap-table2-filter/src/components/text.js index 1995ebe..a359a95 100644 --- a/packages/react-bootstrap-table2-filter/src/components/text.js +++ b/packages/react-bootstrap-table2-filter/src/components/text.js @@ -13,8 +13,14 @@ class TextFilter extends Component { this.filter = this.filter.bind(this); this.handleClick = this.handleClick.bind(this); this.timeout = null; + function getDefaultValue() { + if (props.filterState && typeof props.filterState.filterVal !== 'undefined') { + return props.filterState.filterVal; + } + return props.defaultValue; + } this.state = { - value: props.defaultValue + value: getDefaultValue() }; } @@ -89,6 +95,7 @@ class TextFilter extends Component { caseSensitive, defaultValue, getFilter, + filterState, ...rest } = this.props; @@ -119,6 +126,7 @@ class TextFilter extends Component { TextFilter.propTypes = { onFilter: PropTypes.func.isRequired, column: PropTypes.object.isRequired, + filterState: PropTypes.object, comparator: PropTypes.oneOf([LIKE, EQ]), defaultValue: PropTypes.string, delay: PropTypes.number, @@ -131,6 +139,7 @@ TextFilter.propTypes = { TextFilter.defaultProps = { delay: FILTER_DELAY, + filterState: {}, defaultValue: '', caseSensitive: false }; diff --git a/packages/react-bootstrap-table2-filter/src/context.js b/packages/react-bootstrap-table2-filter/src/context.js index 3fa787b..013be6e 100644 --- a/packages/react-bootstrap-table2-filter/src/context.js +++ b/packages/react-bootstrap-table2-filter/src/context.js @@ -40,7 +40,7 @@ export default ( componentWillReceiveProps(nextProps) { // let nextData = nextProps.data; if (!isRemoteFiltering() && !_.isEqual(nextProps.data, this.data)) { - this.doFilter(nextProps, undefined, this.isEmitDataChange); + this.doFilter(nextProps, this.isEmitDataChange); } else { this.data = nextProps.data; } @@ -76,12 +76,7 @@ export default ( } return; } - - let result; - if (filter.props.onFilter) { - result = filter.props.onFilter(filterVal); - } - this.doFilter(this.props, result); + this.doFilter(this.props); }; } @@ -95,11 +90,9 @@ export default ( return this.data; } - doFilter(props, customResult, ignoreEmitDataChange = false) { - let result = customResult; - + doFilter(props, ignoreEmitDataChange = false) { const { dataChangeListener, data, columns } = props; - result = result || filters(data, columns, _)(this.currFilters); + const result = filters(data, columns, _)(this.currFilters); this.data = result; if (dataChangeListener && !ignoreEmitDataChange) { this.isEmitDataChange = true; @@ -115,7 +108,8 @@ export default ( { this.props.children } diff --git a/packages/react-bootstrap-table2-filter/src/filter.js b/packages/react-bootstrap-table2-filter/src/filter.js index de61577..0e2a72a 100644 --- a/packages/react-bootstrap-table2-filter/src/filter.js +++ b/packages/react-bootstrap-table2-filter/src/filter.js @@ -237,13 +237,21 @@ export const filters = (data, columns, _) => (currFilters) => { const filterObj = currFilters[dataField]; filterFn = factory(filterObj.filterType); let filterValue; + let customFilter; for (let i = 0; i < columns.length; i += 1) { if (columns[i].dataField === dataField) { filterValue = columns[i].filterValue; + if (columns[i].filter) { + customFilter = columns[i].filter.props.onFilter; + } break; } } - result = filterFn(result, dataField, filterObj, filterValue); + if (customFilter) { + result = customFilter(filterObj.filterVal, result); + } else { + result = filterFn(result, dataField, filterObj, filterValue); + } }); return result; }; diff --git a/packages/react-bootstrap-table2-filter/test/components/select.test.js b/packages/react-bootstrap-table2-filter/test/components/select.test.js index 80d30d7..baec4d9 100644 --- a/packages/react-bootstrap-table2-filter/test/components/select.test.js +++ b/packages/react-bootstrap-table2-filter/test/components/select.test.js @@ -25,7 +25,7 @@ describe('Select Filter', () => { 2: 'Unknown' }; - afterEach(() => { + beforeEach(() => { onFilter.reset(); onFilterFirstReturn.reset(); diff --git a/packages/react-bootstrap-table2-filter/test/context.test.js b/packages/react-bootstrap-table2-filter/test/context.test.js index fcad5dd..7b15a28 100644 --- a/packages/react-bootstrap-table2-filter/test/context.test.js +++ b/packages/react-bootstrap-table2-filter/test/context.test.js @@ -94,7 +94,8 @@ describe('FilterContext', () => { expect(mockBase).toHaveBeenCalledWith({ data, onFilter: wrapper.instance().onFilter, - onExternalFilter: wrapper.instance().onExternalFilter + onExternalFilter: wrapper.instance().onExternalFilter, + currFilters: wrapper.instance().currFilters }); }); }); @@ -103,7 +104,6 @@ describe('FilterContext', () => { beforeEach(() => { wrapper = shallow(shallowContext(true)); wrapper.render(); - wrapper.instance().currFilters = { price: { filterVal: 20, filterType: FILTER_TYPE.TEXT } }; }); it('should pass original data without internal filtering', () => { @@ -111,7 +111,8 @@ describe('FilterContext', () => { expect(mockBase).toHaveBeenCalledWith({ data, onFilter: wrapper.instance().onFilter, - onExternalFilter: wrapper.instance().onExternalFilter + onExternalFilter: wrapper.instance().onExternalFilter, + currFilters: wrapper.instance().currFilters }); }); }); @@ -228,33 +229,7 @@ describe('FilterContext', () => { }); }); - describe('if filter.props.onFilter is defined', () => { - const filterVal = '3'; - const onFilter = jest.fn(); - const customColumns = columns.map((column, i) => { - if (i === 1) { - return { - ...column, - filter: textFilter({ onFilter }) - }; - } - return column; - }); - - beforeEach(() => { - wrapper = shallow(shallowContext(false, customColumns)); - wrapper.render(); - instance = wrapper.instance(); - }); - - it('should call filter.props.onFilter correctly', () => { - instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal); - expect(onFilter).toHaveBeenCalledTimes(1); - expect(onFilter).toHaveBeenCalledWith(filterVal); - }); - }); - - describe('if filter.props.onFilter is defined and return an undefined data', () => { + describe('if filter.props.onFilter is defined and return data', () => { const mockReturn = [{ id: 1, name: 'A' @@ -280,7 +255,7 @@ describe('FilterContext', () => { it('should call filter.props.onFilter correctly', () => { instance.onFilter(customColumns[1], FILTER_TYPE.TEXT)(filterVal); expect(onFilter).toHaveBeenCalledTimes(1); - expect(onFilter).toHaveBeenCalledWith(filterVal); + expect(onFilter).toHaveBeenCalledWith(filterVal, data); }); it('should set data correctly', () => { diff --git a/packages/react-bootstrap-table2-paginator/package.json b/packages/react-bootstrap-table2-paginator/package.json index bdb0c22..f894020 100644 --- a/packages/react-bootstrap-table2-paginator/package.json +++ b/packages/react-bootstrap-table2-paginator/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap-table2-paginator", - "version": "2.0.6", + "version": "2.0.8", "description": "it's the pagination addon for react-bootstrap-table2", "main": "./lib/index.js", "repository": { diff --git a/packages/react-bootstrap-table2-paginator/src/page-button.js b/packages/react-bootstrap-table2-paginator/src/page-button.js index e2834ee..d3a81fb 100644 --- a/packages/react-bootstrap-table2-paginator/src/page-button.js +++ b/packages/react-bootstrap-table2-paginator/src/page-button.js @@ -39,7 +39,11 @@ class PageButton extends Component { PageButton.propTypes = { onPageChange: PropTypes.func.isRequired, - page: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, + page: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.number, + PropTypes.string + ]).isRequired, active: PropTypes.bool.isRequired, disabled: PropTypes.bool.isRequired, className: PropTypes.string, diff --git a/packages/react-bootstrap-table2-paginator/src/pagination-list.js b/packages/react-bootstrap-table2-paginator/src/pagination-list.js index 9954538..d9454c6 100644 --- a/packages/react-bootstrap-table2-paginator/src/pagination-list.js +++ b/packages/react-bootstrap-table2-paginator/src/pagination-list.js @@ -27,7 +27,11 @@ const PaginatonList = props => ( PaginatonList.propTypes = { pages: PropTypes.arrayOf(PropTypes.shape({ - page: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + page: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.number, + PropTypes.string + ]), active: PropTypes.bool, disable: PropTypes.bool, title: PropTypes.string diff --git a/packages/react-bootstrap-table2-paginator/src/pagination.js b/packages/react-bootstrap-table2-paginator/src/pagination.js index fa2bd86..d967642 100644 --- a/packages/react-bootstrap-table2-paginator/src/pagination.js +++ b/packages/react-bootstrap-table2-paginator/src/pagination.js @@ -100,10 +100,10 @@ Pagination.propTypes = { sizePerPageRenderer: PropTypes.func, paginationTotalRenderer: PropTypes.func, sizePerPageOptionRenderer: PropTypes.func, - firstPageText: PropTypes.string, - prePageText: PropTypes.string, - nextPageText: PropTypes.string, - lastPageText: PropTypes.string, + firstPageText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + prePageText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + nextPageText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + lastPageText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), nextPageTitle: PropTypes.string, prePageTitle: PropTypes.string, firstPageTitle: PropTypes.string, diff --git a/packages/react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss b/packages/react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss index a99007e..e2fb0a7 100644 --- a/packages/react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss +++ b/packages/react-bootstrap-table2-paginator/style/react-bootstrap-table2-paginator.scss @@ -1,6 +1,6 @@ .react-bootstrap-table-page-btns-ul { float: right; - margin-top: 0px; + margin-top: 0px !important; } .react-bootstrap-table-pagination-list-hidden { diff --git a/packages/react-bootstrap-table2-toolkit/README.md b/packages/react-bootstrap-table2-toolkit/README.md index a470133..444d10e 100644 --- a/packages/react-bootstrap-table2-toolkit/README.md +++ b/packages/react-bootstrap-table2-toolkit/README.md @@ -185,6 +185,9 @@ Default is `false`. Give true to avoid to attach the csv header. #### noAutoBOM - [bool] Default is `true`. +#### blobType - [string] +Default is `text/plain;charset=utf-8`. Change to update the blob type of the exported file. + #### exportAll - [bool] Default is `true`. `false` will only export current data which display on table. diff --git a/packages/react-bootstrap-table2-toolkit/context.js b/packages/react-bootstrap-table2-toolkit/context.js index 838f634..2a585e6 100644 --- a/packages/react-bootstrap-table2-toolkit/context.js +++ b/packages/react-bootstrap-table2-toolkit/context.js @@ -29,6 +29,7 @@ class ToolkitProvider extends statelessDecorator(React.Component) { separator: PropTypes.string, ignoreHeader: PropTypes.bool, noAutoBOM: PropTypes.bool, + blobType: PropTypes.string, exportAll: PropTypes.bool, onlyExportFiltered: PropTypes.bool, onlyExportSelection: PropTypes.bool diff --git a/packages/react-bootstrap-table2-toolkit/package.json b/packages/react-bootstrap-table2-toolkit/package.json index 29970a0..edbf5b2 100644 --- a/packages/react-bootstrap-table2-toolkit/package.json +++ b/packages/react-bootstrap-table2-toolkit/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap-table2-toolkit", - "version": "2.0.0", + "version": "2.0.1", "description": "The toolkit for react-bootstrap-table2", "main": "./lib/index.js", "repository": { diff --git a/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js b/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js index 4d4c5cd..193933c 100644 --- a/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js +++ b/packages/react-bootstrap-table2-toolkit/src/csv/exporter.js @@ -54,11 +54,12 @@ export const save = ( content, { noAutoBOM, - fileName + fileName, + blobType } ) => { FileSaver.saveAs( - new Blob([content], { type: 'text/plain;charset=utf-8' }), + new Blob([content], { type: blobType }), fileName, noAutoBOM ); diff --git a/packages/react-bootstrap-table2-toolkit/src/op/csv.js b/packages/react-bootstrap-table2-toolkit/src/op/csv.js index 76f5135..5724559 100644 --- a/packages/react-bootstrap-table2-toolkit/src/op/csv.js +++ b/packages/react-bootstrap-table2-toolkit/src/op/csv.js @@ -5,6 +5,7 @@ const csvDefaultOptions = { separator: ',', ignoreHeader: false, noAutoBOM: true, + blobType: 'text/plain;charset=utf-8', exportAll: true, onlyExportSelection: false }; diff --git a/packages/react-bootstrap-table2/package.json b/packages/react-bootstrap-table2/package.json index 213640a..4655e03 100644 --- a/packages/react-bootstrap-table2/package.json +++ b/packages/react-bootstrap-table2/package.json @@ -1,6 +1,6 @@ { "name": "react-bootstrap-table-next", - "version": "3.1.3", + "version": "3.1.8", "description": "Next generation of react-bootstrap-table", "main": "./lib/index.js", "repository": { diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js index 928b548..42d419e 100644 --- a/packages/react-bootstrap-table2/src/bootstrap-table.js +++ b/packages/react-bootstrap-table2/src/bootstrap-table.js @@ -91,6 +91,7 @@ class BootstrapTable extends PropsBaseResolver(Component) { sortOrder={ this.props.sortOrder } onSort={ this.props.onSort } onFilter={ this.props.onFilter } + currFilters={ this.props.currFilters } onExternalFilter={ this.props.onExternalFilter } selectRow={ selectRow } expandRow={ expandRow } @@ -168,7 +169,12 @@ BootstrapTable.propTypes = { hideSelectColumn: PropTypes.bool, selectionRenderer: PropTypes.func, selectionHeaderRenderer: PropTypes.func, - headerColumnStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]) + headerColumnStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), + selectColumnStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), + selectColumnPosition: PropTypes.oneOf([ + Const.INDICATOR_POSITION_LEFT, + Const.INDICATOR_POSITION_RIGHT + ]) }), expandRow: PropTypes.shape({ renderer: PropTypes.func, @@ -184,7 +190,8 @@ BootstrapTable.propTypes = { expandColumnPosition: PropTypes.oneOf([ Const.INDICATOR_POSITION_LEFT, Const.INDICATOR_POSITION_RIGHT - ]) + ]), + parentClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.func]) }), rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), rowEvents: PropTypes.object, diff --git a/packages/react-bootstrap-table2/src/cell-event-delegater.js b/packages/react-bootstrap-table2/src/cell-event-delegater.js index 41e0f71..14c2fa0 100644 --- a/packages/react-bootstrap-table2/src/cell-event-delegater.js +++ b/packages/react-bootstrap-table2/src/cell-event-delegater.js @@ -1,3 +1,5 @@ +import _ from './utils'; + const events = [ 'onClick', 'onDoubleClick', @@ -23,7 +25,7 @@ export default ExtendBase => delegate(attrs = {}) { const newAttrs = { ...attrs }; Object.keys(attrs).forEach((attr) => { - if (events.includes(attr)) { + if (_.contains(events, attr)) { newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]); } }); diff --git a/packages/react-bootstrap-table2/src/const.js b/packages/react-bootstrap-table2/src/const.js index ce1b7e4..9852215 100644 --- a/packages/react-bootstrap-table2/src/const.js +++ b/packages/react-bootstrap-table2/src/const.js @@ -8,5 +8,9 @@ export default { CHECKBOX_STATUS_INDETERMINATE: 'indeterminate', CHECKBOX_STATUS_UNCHECKED: 'unchecked', INDICATOR_POSITION_LEFT: 'left', - INDICATOR_POSITION_RIGHT: 'right' + INDICATOR_POSITION_RIGHT: 'right', + TYPE_STRING: 'string', + TYPE_NUMBER: 'number', + TYPE_BOOLEAN: 'bool', + TYPE_DATE: 'date' }; 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 eef22a7..e1c5e47 100644 --- a/packages/react-bootstrap-table2/src/contexts/row-expand-context.js +++ b/packages/react-bootstrap-table2/src/contexts/row-expand-context.js @@ -20,7 +20,7 @@ class RowExpandProvider extends React.Component { if (nextProps.expandRow) { const nextExpanded = nextProps.expandRow.expanded || this.state.expanded; const isClosing = this.state.expanded.reduce((acc, cur) => { - if (!nextExpanded.includes(cur)) { + if (!_.contains(nextExpanded, cur)) { acc.push(cur); } return acc; @@ -42,7 +42,7 @@ class RowExpandProvider extends React.Component { handleRowExpand = (rowKey, expanded, rowIndex, e) => { const { data, keyField, expandRow: { onExpand, onlyOneExpanding, nonExpandable } } = this.props; - if (nonExpandable && nonExpandable.includes(rowKey)) { + if (nonExpandable && _.contains(nonExpandable, rowKey)) { return; } diff --git a/packages/react-bootstrap-table2/src/footer.js b/packages/react-bootstrap-table2/src/footer.js index 1097d3c..5cb542d 100644 --- a/packages/react-bootstrap-table2/src/footer.js +++ b/packages/react-bootstrap-table2/src/footer.js @@ -11,9 +11,9 @@ const Footer = (props) => { const SelectionFooterCellComp = () => ; const ExpansionFooterCellComp = () => ; - const isRenderExpandColumnInLeft = ( - expandColumnPosition = Const.INDICATOR_POSITION_LEFT - ) => expandColumnPosition === Const.INDICATOR_POSITION_LEFT; + const isRenderFunctionColumnInLeft = ( + position = Const.INDICATOR_POSITION_LEFT + ) => position === Const.INDICATOR_POSITION_LEFT; const childrens = columns.map((column, i) => { if (column.footer === undefined || column.footer === null) { @@ -33,11 +33,15 @@ const Footer = (props) => { }); if (selectRow && selectRow.hideSelectColumn !== true) { - childrens.unshift(); + if (isRenderFunctionColumnInLeft(selectRow.selectColumnPosition)) { + childrens.unshift(); + } else { + childrens.push(); + } } if (expandRow.showExpandColumn) { - if (isRenderExpandColumnInLeft(expandRow.expandColumnPosition)) { + if (isRenderFunctionColumnInLeft(expandRow.expandColumnPosition)) { childrens.unshift(); } else { childrens.push(); diff --git a/packages/react-bootstrap-table2/src/header-cell.js b/packages/react-bootstrap-table2/src/header-cell.js index 708800b..18f69c0 100644 --- a/packages/react-bootstrap-table2/src/header-cell.js +++ b/packages/react-bootstrap-table2/src/header-cell.js @@ -18,6 +18,7 @@ const HeaderCell = (props) => { sortOrder, isLastSorting, onFilter, + currFilters, onExternalFilter } = props; @@ -99,7 +100,14 @@ const HeaderCell = (props) => { const onCustomFilter = onExternalFilter(column, filter.props.type); filterElm = filterRenderer(onCustomFilter, column); } else if (filter) { - filterElm = ; + filterElm = ( + + ); } const children = headerFormatter ? @@ -117,6 +125,12 @@ HeaderCell.propTypes = { column: PropTypes.shape({ dataField: PropTypes.string.isRequired, text: PropTypes.string.isRequired, + type: PropTypes.oneOf([ + Const.TYPE_STRING, + Const.TYPE_NUMBER, + Const.TYPE_BOOLEAN, + Const.TYPE_DATE + ]), isDummyField: PropTypes.bool, hidden: PropTypes.bool, headerFormatter: PropTypes.func, @@ -156,6 +170,7 @@ HeaderCell.propTypes = { sortCaret: PropTypes.func, isLastSorting: PropTypes.bool, onFilter: PropTypes.func, + currFilters: PropTypes.object, onExternalFilter: PropTypes.func }; diff --git a/packages/react-bootstrap-table2/src/header.js b/packages/react-bootstrap-table2/src/header.js index bf77637..4360b2c 100644 --- a/packages/react-bootstrap-table2/src/header.js +++ b/packages/react-bootstrap-table2/src/header.js @@ -18,6 +18,7 @@ const Header = (props) => { sortField, sortOrder, selectRow, + currFilters, onExternalFilter, expandRow } = props; @@ -33,9 +34,9 @@ const Header = (props) => { SelectionHeaderCellComp = withHeaderSelection(SelectionHeaderCell); } - const isRenderExpandColumnInLeft = ( - expandColumnPosition = Const.INDICATOR_POSITION_LEFT - ) => expandColumnPosition === Const.INDICATOR_POSITION_LEFT; + const isRenderFunctionColumnInLeft = ( + position = Const.INDICATOR_POSITION_LEFT + ) => position === Const.INDICATOR_POSITION_LEFT; const childrens = [ columns.map((column, i) => { @@ -50,6 +51,7 @@ const Header = (props) => { onSort={ onSort } sorting={ currSort } onFilter={ onFilter } + currFilters={ currFilters } onExternalFilter={ onExternalFilter } sortOrder={ sortOrder } isLastSorting={ isLastSorting } @@ -58,11 +60,15 @@ const Header = (props) => { ]; if (!selectRow.hideSelectColumn) { - childrens.unshift(); + if (isRenderFunctionColumnInLeft(selectRow.selectColumnPosition)) { + childrens.unshift(); + } else { + childrens.push(); + } } if (expandRow.showExpandColumn) { - if (isRenderExpandColumnInLeft(expandRow.expandColumnPosition)) { + if (isRenderFunctionColumnInLeft(expandRow.expandColumnPosition)) { childrens.unshift(); } else { childrens.push(); @@ -85,6 +91,7 @@ Header.propTypes = { sortField: PropTypes.string, sortOrder: PropTypes.string, selectRow: PropTypes.object, + currFilters: PropTypes.object, onExternalFilter: PropTypes.func, className: PropTypes.string, expandRow: PropTypes.object diff --git a/packages/react-bootstrap-table2/src/props-resolver/index.js b/packages/react-bootstrap-table2/src/props-resolver/index.js index 92ae62d..5e18b27 100644 --- a/packages/react-bootstrap-table2/src/props-resolver/index.js +++ b/packages/react-bootstrap-table2/src/props-resolver/index.js @@ -22,7 +22,7 @@ export default ExtendBase => if (!hiddenRows || hiddenRows.length === 0) return data; return data.filter((row) => { const key = _.get(row, keyField); - return !hiddenRows.includes(key); + return !_.contains(hiddenRows, key); }); } }; diff --git a/packages/react-bootstrap-table2/src/row-expand/row-consumer.js b/packages/react-bootstrap-table2/src/row-expand/row-consumer.js index 724b5d2..8f30dc1 100644 --- a/packages/react-bootstrap-table2/src/row-expand/row-consumer.js +++ b/packages/react-bootstrap-table2/src/row-expand/row-consumer.js @@ -1,15 +1,24 @@ /* eslint react/prop-types: 0 */ import React from 'react'; +import cs from 'classnames'; import ExpandRow from './expand-row'; +import _ from '../utils'; import ExpansionContext from '../contexts/row-expand-context'; export default (Component) => { const renderWithExpansion = (props, expandRow) => { + let parentClassName = ''; const key = props.value; - const expanded = expandRow.expanded.includes(key); - const isClosing = expandRow.isClosing.includes(key); - const expandable = !expandRow.nonExpandable || !expandRow.nonExpandable.includes(key); + const expanded = _.contains(expandRow.expanded, key); + const isClosing = _.contains(expandRow.isClosing, key); + const expandable = !expandRow.nonExpandable || !_.contains(expandRow.nonExpandable, key); + if (expanded) { + parentClassName = _.isFunction(expandRow.parentClassName) ? + expandRow.parentClassName(expanded, props.row, props.rowIndex) : + (expandRow.parentClassName || ''); + } + return [ { expanded={ expanded } expandable={ expandable } expandRow={ { ...expandRow } } + className={ cs(props.className, parentClassName) } />, expanded || isClosing ? { expanded={ expanded } onClosed={ () => expandRow.onClosed(key) } > - { expandRow.renderer(props.row) } + { expandRow.renderer(props.row, props.rowIndex) } : null ]; }; diff --git a/packages/react-bootstrap-table2/src/row-selection/row-consumer.js b/packages/react-bootstrap-table2/src/row-selection/row-consumer.js index 5a51534..a8b14db 100644 --- a/packages/react-bootstrap-table2/src/row-selection/row-consumer.js +++ b/packages/react-bootstrap-table2/src/row-selection/row-consumer.js @@ -7,8 +7,8 @@ import SelectionContext from '../contexts/selection-context'; export default (Component) => { const renderWithSelection = (props, selectRow) => { const key = props.value; - const selected = selectRow.selected.includes(key); - const selectable = !selectRow.nonSelectable || !selectRow.nonSelectable.includes(key); + const selected = _.contains(selectRow.selected, key); + const selectable = !selectRow.nonSelectable || !_.contains(selectRow.nonSelectable, key); let { style, 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 5e8b27c..d7f8ec8 100644 --- a/packages/react-bootstrap-table2/src/row-selection/selection-cell.js +++ b/packages/react-bootstrap-table2/src/row-selection/selection-cell.js @@ -5,6 +5,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Const from '../const'; +import _ from '../utils'; import { BootstrapContext } from '../contexts/bootstrap'; export default class SelectionCell extends Component { @@ -17,7 +18,8 @@ export default class SelectionCell extends Component { rowIndex: PropTypes.number, tabIndex: PropTypes.number, clickToSelect: PropTypes.bool, - selectionRenderer: PropTypes.func + selectionRenderer: PropTypes.func, + selectColumnStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]) } constructor() { @@ -31,7 +33,8 @@ export default class SelectionCell extends Component { this.props.selected !== nextProps.selected || this.props.disabled !== nextProps.disabled || this.props.rowKey !== nextProps.rowKey || - this.props.tabIndex !== nextProps.tabIndex; + this.props.tabIndex !== nextProps.tabIndex || + this.props.selectColumnStyle !== nextProps.selectColumnStyle; return shouldUpdate; } @@ -57,17 +60,28 @@ export default class SelectionCell extends Component { render() { const { + rowKey, mode: inputType, selected, disabled, tabIndex, rowIndex, - selectionRenderer + selectionRenderer, + selectColumnStyle } = this.props; const attrs = {}; if (tabIndex !== -1) attrs.tabIndex = tabIndex; + attrs.style = _.isFunction(selectColumnStyle) ? + selectColumnStyle({ + checked: selected, + disabled, + rowIndex, + rowKey + }) : + selectColumnStyle; + return ( { diff --git a/packages/react-bootstrap-table2/src/row/aggregate-row.js b/packages/react-bootstrap-table2/src/row/aggregate-row.js index 96b7dbe..613bb77 100644 --- a/packages/react-bootstrap-table2/src/row/aggregate-row.js +++ b/packages/react-bootstrap-table2/src/row/aggregate-row.js @@ -35,20 +35,21 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co this.props.expanded !== nextProps.expanded || this.props.expandable !== nextProps.expandable || this.props.selectable !== nextProps.selectable || + this.props.selectRow.hideSelectColumn !== nextProps.selectRow.hideSelectColumn || this.shouldUpdatedBySelfProps(nextProps) ) { - this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps); + this.shouldUpdateRowContent = this.shouldRowContentUpdate(nextProps); return true; } - this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps); + this.shouldUpdateRowContent = this.shouldRowContentUpdate(nextProps); return this.shouldUpdateRowContent; } - isRenderExpandColumnInLeft( - expandColumnPosition = Const.INDICATOR_POSITION_LEFT + isRenderFunctionColumnInLeft( + position = Const.INDICATOR_POSITION_LEFT ) { - return expandColumnPosition === Const.INDICATOR_POSITION_LEFT; + return position === Const.INDICATOR_POSITION_LEFT; } render() { @@ -71,7 +72,7 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co ...rest } = this.props; const key = _.get(row, keyField); - const { hideSelectColumn, clickToSelect } = selectRow; + const { hideSelectColumn, selectColumnPosition, clickToSelect } = selectRow; const { showExpandColumn, expandColumnPosition } = expandRow; const newAttrs = this.delegate({ ...attrs }); @@ -95,7 +96,7 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co )]; if (!hideSelectColumn) { - childrens.unshift(( + const selectCell = ( - )); + ); + if (this.isRenderFunctionColumnInLeft(selectColumnPosition)) { + childrens.unshift(selectCell); + } else { + childrens.push(selectCell); + } } if (showExpandColumn) { @@ -120,7 +126,7 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co tabIndex={ tabIndexCell ? tabIndexStart++ : -1 } /> ); - if (this.isRenderExpandColumnInLeft(expandColumnPosition)) { + if (this.isRenderFunctionColumnInLeft(expandColumnPosition)) { childrens.unshift(expandCell); } else { childrens.push(expandCell); diff --git a/packages/react-bootstrap-table2/src/row/event-delegater.js b/packages/react-bootstrap-table2/src/row/event-delegater.js index 0a17aa2..a6a1a1f 100644 --- a/packages/react-bootstrap-table2/src/row/event-delegater.js +++ b/packages/react-bootstrap-table2/src/row/event-delegater.js @@ -74,7 +74,7 @@ export default ExtendBase => delegate(attrs = {}) { const newAttrs = { ...attrs }; Object.keys(attrs).forEach((attr) => { - if (events.includes(attr)) { + if (_.contains(events, attr)) { newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]); } }); diff --git a/packages/react-bootstrap-table2/src/row/should-updater.js b/packages/react-bootstrap-table2/src/row/should-updater.js index c61ae5e..fb1f1be 100644 --- a/packages/react-bootstrap-table2/src/row/should-updater.js +++ b/packages/react-bootstrap-table2/src/row/should-updater.js @@ -48,4 +48,9 @@ export default ExtendBase => return this.shouldUpdateByCellEditing(nextProps) || this.shouldUpdatedByNormalProps(nextProps); } + + shouldRowContentUpdate(nextProps) { + return this.shouldUpdateChild(nextProps) || + this.shouldUpdateByColumnsForSimpleCheck(nextProps); + } }; diff --git a/packages/react-bootstrap-table2/src/row/simple-row.js b/packages/react-bootstrap-table2/src/row/simple-row.js index 96ca92f..0ad992f 100644 --- a/packages/react-bootstrap-table2/src/row/simple-row.js +++ b/packages/react-bootstrap-table2/src/row/simple-row.js @@ -15,8 +15,7 @@ class SimpleRow extends shouldUpdater(eventDelegater(Component)) { shouldComponentUpdate(nextProps) { this.shouldUpdateRowContent = false; - this.shouldUpdateRowContent = - this.shouldUpdateChild(nextProps) || this.shouldUpdateByColumnsForSimpleCheck(nextProps); + this.shouldUpdateRowContent = this.shouldRowContentUpdate(nextProps); if (this.shouldUpdateRowContent) return true; return this.shouldUpdatedBySelfProps(nextProps); diff --git a/packages/react-bootstrap-table2/src/store/expand.js b/packages/react-bootstrap-table2/src/store/expand.js index 31feb0a..ceefae7 100644 --- a/packages/react-bootstrap-table2/src/store/expand.js +++ b/packages/react-bootstrap-table2/src/store/expand.js @@ -20,7 +20,7 @@ export const expandableKeys = (data, keyField, skips = []) => { return data.map(row => _.get(row, keyField)); } return data - .filter(row => !skips.includes(_.get(row, keyField))) + .filter(row => !_.contains(skips, _.get(row, keyField))) .map(row => _.get(row, keyField)); }; diff --git a/packages/react-bootstrap-table2/src/store/operators.js b/packages/react-bootstrap-table2/src/store/operators.js index de055ee..b357006 100644 --- a/packages/react-bootstrap-table2/src/store/operators.js +++ b/packages/react-bootstrap-table2/src/store/operators.js @@ -3,11 +3,13 @@ import * as selection from './selection'; import * as expand from './expand'; import * as mutate from './mutate'; import * as sort from './sort'; +import * as type from './type'; export default { ...rows, ...selection, ...expand, ...mutate, - ...sort + ...sort, + ...type }; diff --git a/packages/react-bootstrap-table2/src/store/selection.js b/packages/react-bootstrap-table2/src/store/selection.js index af6a8c1..38ecd5f 100644 --- a/packages/react-bootstrap-table2/src/store/selection.js +++ b/packages/react-bootstrap-table2/src/store/selection.js @@ -29,7 +29,7 @@ export const selectableKeys = (data, keyField, skips = []) => { return data.map(row => _.get(row, keyField)); } return data - .filter(row => !skips.includes(_.get(row, keyField))) + .filter(row => !_.contains(skips, _.get(row, keyField))) .map(row => _.get(row, keyField)); }; @@ -37,7 +37,7 @@ export const unSelectableKeys = (selected, skips = []) => { if (skips.length === 0) { return []; } - return selected.filter(x => skips.includes(x)); + return selected.filter(x => _.contains(skips, x)); }; export const getSelectedRows = (data, keyField, selected) => diff --git a/packages/react-bootstrap-table2/src/store/type.js b/packages/react-bootstrap-table2/src/store/type.js new file mode 100644 index 0000000..5b98498 --- /dev/null +++ b/packages/react-bootstrap-table2/src/store/type.js @@ -0,0 +1,18 @@ +import Const from '../const'; + +export const typeConvert = (type, value) => { + if (type === Const.TYPE_STRING) { + return String(value); + } else if (type === Const.TYPE_NUMBER) { + return Number(value); + } else if (type === Const.TYPE_BOOLEAN) { + if (typeof value === 'boolean') { + return value; + } + return value === 'true'; + } else if (type === Const.TYPE_DATE) { + return new Date(value); + } + return value; +}; + diff --git a/packages/react-bootstrap-table2/test/header-cell.test.js b/packages/react-bootstrap-table2/test/header-cell.test.js index b0c4525..fa3861d 100644 --- a/packages/react-bootstrap-table2/test/header-cell.test.js +++ b/packages/react-bootstrap-table2/test/header-cell.test.js @@ -728,7 +728,9 @@ describe('HeaderCell', () => { Filter } }; - wrapper = shallow(); + wrapper = shallow( + + ); }); it('should render successfully', () => {