diff --git a/docs/migration.md b/docs/migration.md index 4f379f8..89f5088 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -23,7 +23,7 @@ Currently, **I still can't implement all the mainly features in legacy `react-bo * [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay) * Overlay/Loading Addons * [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit) - * Table Toolkits, like search, csv etc. + * Table Toolkits, like search, csv, column toggle etc. This can help your application with less bundled size and also help `react-bootstrap-table2` have clean design to avoid handling to much logic in kernel module(SRP). Hence, which means you probably need to install above addons when you need specific features. diff --git a/packages/react-bootstrap-table2-example/examples/bootstrap4/column-toggle.js b/packages/react-bootstrap-table2-example/examples/bootstrap4/column-toggle.js new file mode 100644 index 0000000..2060fdd --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/bootstrap4/column-toggle.js @@ -0,0 +1,81 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const { ToggleList } = ColumnToggle; +const products = productsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; + +const { ToggleList } = ColumnToggle; +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + + + { + props => ( +
+ +
+ +
+ ) + } +
+`; + +export default () => ( +
+ + { + props => ( +
+ +
+ +
+ ) + } +
+ { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-toggle/custom-toggle-list.js b/packages/react-bootstrap-table2-example/examples/column-toggle/custom-toggle-list.js new file mode 100644 index 0000000..a4ea390 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-toggle/custom-toggle-list.js @@ -0,0 +1,135 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider from 'react-bootstrap-table2-toolkit'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const products = productsGenerator(); + +const columnsdt = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider from 'react-bootstrap-table2-toolkit'; + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const CustomToggleList = ({ + columns, + onColumnToggle, + toggles +}) => ( +
+ { + columns + .map(column => ({ + ...column, + toggle: toggles[column.dataField] + })) + .map(column => ( + + )) + } +
+); + + + { + props => ( +
+ +
+ +
+ ) + } +
+`; + +const CustomToggleList = ({ + columns, + onColumnToggle, + toggles +}) => ( +
+ { + columns + .map(column => ({ + ...column, + toggle: toggles[column.dataField] + })) + .map(column => ( + + )) + } +
+); + +export default () => ( +
+ + { + props => ( +
+ +
+ +
+ ) + } +
+ { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-toggle/default-visibility.js b/packages/react-bootstrap-table2-example/examples/column-toggle/default-visibility.js new file mode 100644 index 0000000..b4b72d2 --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-toggle/default-visibility.js @@ -0,0 +1,81 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const { ToggleList } = ColumnToggle; +const products = productsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + hidden: true +}, { + dataField: 'price', + text: 'Product Price', + hidden: true +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; + +const { ToggleList } = ColumnToggle; +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name', + hidden: true +}, { + dataField: 'price', + text: 'Product Price', + hidden: true +}]; + + + { + props => ( +
+ +
+ +
+ ) + } +
+`; + +export default () => ( +
+ + { + props => ( +
+ +
+ +
+ ) + } +
+ { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-toggle/index.js b/packages/react-bootstrap-table2-example/examples/column-toggle/index.js new file mode 100644 index 0000000..2060fdd --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-toggle/index.js @@ -0,0 +1,81 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const { ToggleList } = ColumnToggle; +const products = productsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; + +const { ToggleList } = ColumnToggle; +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + + + { + props => ( +
+ +
+ +
+ ) + } +
+`; + +export default () => ( +
+ + { + props => ( +
+ +
+ +
+ ) + } +
+ { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/examples/column-toggle/styling-toggle-list.js b/packages/react-bootstrap-table2-example/examples/column-toggle/styling-toggle-list.js new file mode 100644 index 0000000..34cd3ac --- /dev/null +++ b/packages/react-bootstrap-table2-example/examples/column-toggle/styling-toggle-list.js @@ -0,0 +1,91 @@ +/* eslint react/prop-types: 0 */ +import React from 'react'; + +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; +import Code from 'components/common/code-block'; +import { productsGenerator } from 'utils/common'; + +const { ToggleList } = ColumnToggle; +const products = productsGenerator(); + +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + +const sourceCode = `\ +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; + +const { ToggleList } = ColumnToggle; +const columns = [{ + dataField: 'id', + text: 'Product ID' +}, { + dataField: 'name', + text: 'Product Name' +}, { + dataField: 'price', + text: 'Product Price' +}]; + + + { + props => ( +
+ +
+ +
+ ) + } +
+`; + +export default () => ( +
+ + { + props => ( +
+ +
+ +
+ ) + } +
+ { sourceCode } +
+); diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js index 788b083..e26f29d 100644 --- a/packages/react-bootstrap-table2-example/stories/index.js +++ b/packages/react-bootstrap-table2-example/stories/index.js @@ -20,6 +20,7 @@ import TabIndexCellTable from 'examples/basic/tabindex-column'; import Bootstrap4DefaultSortTable from 'examples/bootstrap4/sort'; import Bootstrap4RowSelectionTable from 'examples/bootstrap4/row-selection'; import Bootstrap4PaginationTable from 'examples/bootstrap4/pagination'; +import Bootstrap4ColumnToggleTable from 'examples/bootstrap4/column-toggle'; // work on columns import NestedDataTable from 'examples/columns/nested-data-table'; @@ -193,6 +194,12 @@ import CustomCSVButton from 'examples/csv/custom-csv-button'; import ExportCustomData from 'examples/csv/export-custom-data'; import CustomCSV from 'examples/csv/custom-csv'; +// Column toggle +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'; + // loading overlay import EmptyTableOverlay from 'examples/loading-overlay/empty-table-overlay'; import TableOverlay from 'examples/loading-overlay/table-overlay'; @@ -240,7 +247,8 @@ storiesOf('Bootstrap 4', module) .addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR)) .add('Sort table with bootstrap 4', () => ) .add('Row selection table with bootstrap 4', () => ) - .add('Pagination table with bootstrap 4', () => ); + .add('Pagination table with bootstrap 4', () => ) + .add('Column Toggle with bootstrap 4', () => ); storiesOf('Work on Columns', module) .addDecorator(bootstrapStyle()) @@ -415,6 +423,13 @@ storiesOf('Table Search', module) .add('Search Fromatted Value', () => ) .add('Custom Search Value', () => ); +storiesOf('Column Toggle', module) + .addDecorator(bootstrapStyle()) + .add('Basic Column Toggle', () => ) + .add('Default Visibility', () => ) + .add('Styling Column Toggle', () => ) + .add('Custom Column Toggle', () => ); + storiesOf('Export CSV', module) .addDecorator(bootstrapStyle()) .add('Basic Export CSV', () => ) diff --git a/packages/react-bootstrap-table2-toolkit/README.md b/packages/react-bootstrap-table2-toolkit/README.md index dcf7738..78fb3a0 100644 --- a/packages/react-bootstrap-table2-toolkit/README.md +++ b/packages/react-bootstrap-table2-toolkit/README.md @@ -2,9 +2,15 @@ `react-bootstrap-table2` support some additional features in [`react-bootstrap-table2-toolkit`](https://github.com/react-bootstrap-table/react-bootstrap-table2/tree/develop/packages/react-bootstrap-table2-toolkit). -In the future, this toolkit will support other feature like row delete, insert etc. Right now we only support Table Search and CSV export. +In the future, this toolkit will support other feature like row delete, insert etc. Right now we only following features: + +* Table Search +* Export CSV +* Column Toggle **[Live Demo For Table Search](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html?selectedKind=Table%20Search)** +**[Live Demo For Export CSV](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html?selectedKind=Export%20CSV&selectedStory=Basic%20Export%20CSV)** +**[Live Demo For Column Toggle](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html?selectedKind=Column%20Toggle&selectedStory=Basic%20Column%20Toggle)** **[API&Props Definitation](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/pagination-props.html)** @@ -168,4 +174,36 @@ Default is `true`. Default is `true`. `false` will only export current data which display on table. #### onlyExportSelection - [bool] -Default is `false`. `true` will only export the data which is selected. \ No newline at end of file +Default is `false`. `true` will only export the data which is selected. + +## Column Toggle + +Let's see how to render the column toggle in your react component: + +```js +import BootstrapTable from 'react-bootstrap-table-next'; +import ToolkitProvider, { ColumnToggle } from 'react-bootstrap-table2-toolkit'; + + + { + props => ( +
+ +
+ +
+ ) + } +
+``` + +> `columnToggleProps` props have enough information to let you custom the toggle list: [demo]([Live Demo For Export CSV](https://react-bootstrap-table.github.io/react-bootstrap-table2/storybook/index.html?selectedKind=Export%20CSV&selectedStory=Custom%20Column%20Toggle)) + +If you want to have default visibility on specified column, you can just give `true` or `false` on `column.hidden`. \ No newline at end of file diff --git a/packages/react-bootstrap-table2-toolkit/context.js b/packages/react-bootstrap-table2-toolkit/context.js index c1664c3..322106e 100644 --- a/packages/react-bootstrap-table2-toolkit/context.js +++ b/packages/react-bootstrap-table2-toolkit/context.js @@ -1,13 +1,14 @@ +/* eslint no-param-reassign: 0 */ import React from 'react'; import PropTypes from 'prop-types'; -import statelessDrcorator from './statelessOp'; +import statelessDecorator from './statelessOp'; -import createContext from './src/search/context'; +import createSearchContext from './src/search/context'; const ToolkitContext = React.createContext(); -class ToolkitProvider extends statelessDrcorator(React.Component) { +class ToolkitProvider extends statelessDecorator(React.Component) { static propTypes = { keyField: PropTypes.string.isRequired, data: PropTypes.array.isRequired, @@ -42,13 +43,22 @@ class ToolkitProvider extends statelessDrcorator(React.Component) { constructor(props) { super(props); - this.state = { - searchText: typeof props.search === 'object' ? (props.search.defaultSearch || '') : '' - }; + const state = {}; this._ = null; this.onClear = this.onClear.bind(this); this.onSearch = this.onSearch.bind(this); + this.onColumnToggle = this.onColumnToggle.bind(this); this.setDependencyModules = this.setDependencyModules.bind(this); + + if (props.columnToggle) { + state.columnToggle = props.columns + .reduce((obj, column) => { + obj[column.dataField] = !column.hidden; + return obj; + }, {}); + } + state.searchText = typeof props.search === 'object' ? (props.search.defaultSearch || '') : ''; + this.state = state; } onSearch(searchText) { @@ -61,6 +71,14 @@ class ToolkitProvider extends statelessDrcorator(React.Component) { this.setState({ searchText: '' }); } + onColumnToggle(dataField) { + const { columnToggle } = this.state; + columnToggle[dataField] = !columnToggle[dataField]; + this.setState(({ + ...this.state, + columnToggle + })); + } /** * * @param {*} _ @@ -84,10 +102,15 @@ class ToolkitProvider extends statelessDrcorator(React.Component) { }; if (this.props.search) { baseProps.search = { - searchContext: createContext(this.props.search), + searchContext: createSearchContext(this.props.search), searchText: this.state.searchText }; } + if (this.props.columnToggle) { + baseProps.columnToggle = { + toggles: this.state.columnToggle + }; + } return ( diff --git a/packages/react-bootstrap-table2-toolkit/index.js b/packages/react-bootstrap-table2-toolkit/index.js index f9553ac..a5ea381 100644 --- a/packages/react-bootstrap-table2-toolkit/index.js +++ b/packages/react-bootstrap-table2-toolkit/index.js @@ -5,3 +5,4 @@ export default ToolkitProvider; export const ToolkitContext = Context; export { default as Search } from './src/search'; export { default as CSVExport } from './src/csv'; +export { default as ColumnToggle } from './src/column-toggle'; diff --git a/packages/react-bootstrap-table2-toolkit/src/column-toggle/index.js b/packages/react-bootstrap-table2-toolkit/src/column-toggle/index.js new file mode 100644 index 0000000..afc5dae --- /dev/null +++ b/packages/react-bootstrap-table2-toolkit/src/column-toggle/index.js @@ -0,0 +1,3 @@ +import ToggleList from './toggle-list'; + +export default { ToggleList }; diff --git a/packages/react-bootstrap-table2-toolkit/src/column-toggle/toggle-list.js b/packages/react-bootstrap-table2-toolkit/src/column-toggle/toggle-list.js new file mode 100644 index 0000000..b5039f8 --- /dev/null +++ b/packages/react-bootstrap-table2-toolkit/src/column-toggle/toggle-list.js @@ -0,0 +1,50 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const ToggleList = ({ + columns, + onColumnToggle, + toggles, + contextual, + className, + btnClassName +}) => ( +
+ { + columns + .map(column => ({ + ...column, + toggle: toggles[column.dataField] + })) + .map(column => ( + + )) + } +
+); + +ToggleList.propTypes = { + columns: PropTypes.array.isRequired, + toggles: PropTypes.object.isRequired, + onColumnToggle: PropTypes.func.isRequired, + btnClassName: PropTypes.string, + className: PropTypes.string, + contextual: PropTypes.string +}; + +ToggleList.defaultProps = { + btnClassName: '', + className: '', + contextual: 'primary' +}; + +export default ToggleList; diff --git a/packages/react-bootstrap-table2/src/contexts/column-context.js b/packages/react-bootstrap-table2/src/contexts/column-context.js new file mode 100644 index 0000000..70ff454 --- /dev/null +++ b/packages/react-bootstrap-table2/src/contexts/column-context.js @@ -0,0 +1,39 @@ +/* eslint react/prop-types: 0 */ +/* eslint react/prefer-stateless-function: 0 */ +import React from 'react'; +import PropTypes from 'prop-types'; + +export default () => { + const ColumnManagementContext = React.createContext(); + + class ColumnManagementProvider extends React.Component { + static propTypes = { + columns: PropTypes.array.isRequired, + toggles: PropTypes.object + } + + static defaultProps = { + toggles: null + } + + render() { + let toggleColumn; + const { columns, toggles } = this.props; + if (toggles) { + toggleColumn = columns.filter(column => toggles[column.dataField]); + } else { + toggleColumn = columns.filter(column => !column.hidden); + } + return ( + + { this.props.children } + + ); + } + } + + return { + Provider: ColumnManagementProvider, + Consumer: ColumnManagementContext.Consumer + }; +}; diff --git a/packages/react-bootstrap-table2/src/contexts/index.js b/packages/react-bootstrap-table2/src/contexts/index.js index 4367ae1..3288d0a 100644 --- a/packages/react-bootstrap-table2/src/contexts/index.js +++ b/packages/react-bootstrap-table2/src/contexts/index.js @@ -5,6 +5,7 @@ import React, { Component } from 'react'; import EventEmitter from 'events'; import _ from '../utils'; import createDataContext from './data-context'; +import createColumnMgtContext from './column-context'; import createSortContext from './sort-context'; import SelectionContext from './selection-context'; import RowExpandContext from './row-expand-context'; @@ -30,6 +31,13 @@ const withContext = Base => dataOperator, this.isRemoteSort, this.handleRemoteSortChange); } + if ( + props.columnToggle || + props.columns.filter(col => col.hidden).length > 0 + ) { + this.ColumnManagementContext = createColumnMgtContext(); + } + if (props.selectRow) { this.SelectionContext = SelectionContext; } @@ -83,6 +91,7 @@ const withContext = Base => searchProps, sortProps, paginationProps, + columnToggleProps ) => ( this.table = n } @@ -91,11 +100,40 @@ const withContext = Base => { ...filterProps } { ...searchProps } { ...paginationProps } + { ...columnToggleProps } data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) } /> ); } + renderWithColumnManagementCtx(base, baseProps) { + return ( + rootProps, + filterProps, + searchProps, + sortProps, + paginationProps + ) => ( + + + { + columnToggleProps => base( + rootProps, + filterProps, + searchProps, + sortProps, + paginationProps, + columnToggleProps + ) + } + + + ); + } + renderWithSelectionCtx(base, baseProps) { return ( rootProps, @@ -271,6 +309,10 @@ const withContext = Base => let base = this.renderBase(); + if (this.ColumnManagementContext) { + base = this.renderWithColumnManagementCtx(base, baseProps); + } + if (this.SelectionContext) { base = this.renderWithSelectionCtx(base, baseProps); } diff --git a/packages/react-bootstrap-table2/src/footer.js b/packages/react-bootstrap-table2/src/footer.js index 2c29994..1097d3c 100644 --- a/packages/react-bootstrap-table2/src/footer.js +++ b/packages/react-bootstrap-table2/src/footer.js @@ -16,7 +16,7 @@ const Footer = (props) => { ) => expandColumnPosition === Const.INDICATOR_POSITION_LEFT; const childrens = columns.map((column, i) => { - if (column.footer === undefined || column.footer === null || column.hidden) { + if (column.footer === undefined || column.footer === null) { return false; } diff --git a/packages/react-bootstrap-table2/src/header.js b/packages/react-bootstrap-table2/src/header.js index 2606852..bf77637 100644 --- a/packages/react-bootstrap-table2/src/header.js +++ b/packages/react-bootstrap-table2/src/header.js @@ -39,24 +39,21 @@ const Header = (props) => { const childrens = [ columns.map((column, i) => { - if (!column.hidden) { - const currSort = column.dataField === sortField; - const isLastSorting = column.dataField === sortField; + const currSort = column.dataField === sortField; + const isLastSorting = column.dataField === sortField; - return ( - ); - } - return false; + return ( + ); }) ]; diff --git a/packages/react-bootstrap-table2/src/row/row-pure-content.js b/packages/react-bootstrap-table2/src/row/row-pure-content.js index 95ab857..3344da8 100644 --- a/packages/react-bootstrap-table2/src/row/row-pure-content.js +++ b/packages/react-bootstrap-table2/src/row/row-pure-content.js @@ -33,92 +33,89 @@ export default class RowPureContent extends React.Component { let tabIndex = tabIndexStart; return columns.map((column, index) => { - if (!column.hidden) { - const { dataField } = column; - const content = _.get(row, dataField); - if (rowIndex === editingRowIdx && index === editingColIdx) { - return ( - - ); - } - // render cell - let cellTitle; - let cellStyle = {}; - let cellAttrs = { - ..._.isFunction(column.attrs) - ? column.attrs(content, row, rowIndex, index) - : column.attrs - }; - - if (column.events) { - const events = Object.assign({}, column.events); - Object.keys(Object.assign({}, column.events)).forEach((key) => { - const originFn = events[key]; - events[key] = (...rest) => originFn(...rest, row, rowIndex); - }); - cellAttrs = { ...cellAttrs, ...events }; - } - - const cellClasses = _.isFunction(column.classes) - ? column.classes(content, row, rowIndex, index) - : column.classes; - - if (column.style) { - cellStyle = _.isFunction(column.style) - ? column.style(content, row, rowIndex, index) - : column.style; - cellStyle = Object.assign({}, cellStyle) || {}; - } - - if (column.title) { - cellTitle = _.isFunction(column.title) - ? column.title(content, row, rowIndex, index) - : content; - cellAttrs.title = cellTitle; - } - - if (column.align) { - cellStyle.textAlign = - _.isFunction(column.align) - ? column.align(content, row, rowIndex, index) - : column.align; - } - - if (cellClasses) cellAttrs.className = cellClasses; - if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle; - - let editableCell = _.isDefined(column.editable) ? column.editable : true; - if (column.dataField === keyField || !editable) editableCell = false; - if (_.isFunction(column.editable)) { - editableCell = column.editable(content, row, rowIndex, index); - } - - if (tabIndexStart !== -1) { - cellAttrs.tabIndex = tabIndex++; - } - + const { dataField } = column; + const content = _.get(row, dataField); + if (rowIndex === editingRowIdx && index === editingColIdx) { return ( - ); } - return false; + // render cell + let cellTitle; + let cellStyle = {}; + let cellAttrs = { + ..._.isFunction(column.attrs) + ? column.attrs(content, row, rowIndex, index) + : column.attrs + }; + + if (column.events) { + const events = Object.assign({}, column.events); + Object.keys(Object.assign({}, column.events)).forEach((key) => { + const originFn = events[key]; + events[key] = (...rest) => originFn(...rest, row, rowIndex); + }); + cellAttrs = { ...cellAttrs, ...events }; + } + + const cellClasses = _.isFunction(column.classes) + ? column.classes(content, row, rowIndex, index) + : column.classes; + + if (column.style) { + cellStyle = _.isFunction(column.style) + ? column.style(content, row, rowIndex, index) + : column.style; + cellStyle = Object.assign({}, cellStyle) || {}; + } + + if (column.title) { + cellTitle = _.isFunction(column.title) + ? column.title(content, row, rowIndex, index) + : content; + cellAttrs.title = cellTitle; + } + + if (column.align) { + cellStyle.textAlign = + _.isFunction(column.align) + ? column.align(content, row, rowIndex, index) + : column.align; + } + + if (cellClasses) cellAttrs.className = cellClasses; + if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle; + + let editableCell = _.isDefined(column.editable) ? column.editable : true; + if (column.dataField === keyField || !editable) editableCell = false; + if (_.isFunction(column.editable)) { + editableCell = column.editable(content, row, rowIndex, index); + } + + if (tabIndexStart !== -1) { + cellAttrs.tabIndex = tabIndex++; + } + + return ( + + ); }); } } diff --git a/packages/react-bootstrap-table2/test/contexts/column-context.test.js b/packages/react-bootstrap-table2/test/contexts/column-context.test.js new file mode 100644 index 0000000..8781542 --- /dev/null +++ b/packages/react-bootstrap-table2/test/contexts/column-context.test.js @@ -0,0 +1,104 @@ +import 'jsdom-global/register'; +import React from 'react'; +import { shallow } from 'enzyme'; + +import BootstrapTable from '../../src/bootstrap-table'; +import createColumnManagementContext from '../../src/contexts/column-context'; + +describe('ColumnManagementContext', () => { + let wrapper; + + const data = [{ + id: 1, + name: 'A' + }, { + id: 2, + name: 'B' + }]; + + const columns = [{ + dataField: 'id', + text: 'ID' + }, { + dataField: 'name', + text: 'Name' + }]; + + const mockBase = jest.fn((props => ( + + ))); + + const ColumnManagementContext = createColumnManagementContext(); + + function shallowContext(options = {}) { + return ( + + + { + columnToggleProps => mockBase(columnToggleProps) + } + + + ); + } + + describe('default render', () => { + beforeEach(() => { + wrapper = shallow(shallowContext()); + wrapper.render(); + }); + + it('should have correct Provider property after calling createColumnManagementContext', () => { + expect(ColumnManagementContext.Provider).toBeDefined(); + }); + + it('should have correct Consumer property after calling createColumnManagementContext', () => { + expect(ColumnManagementContext.Consumer).toBeDefined(); + }); + }); + + describe('when toggles props exist', () => { + beforeEach(() => { + wrapper = shallow(shallowContext({ + toggles: { + id: true, + name: false + } + })); + }); + + it('should render component with correct columns props', () => { + expect(wrapper.prop('value').columns).toHaveLength(columns.length - 1); + expect(wrapper.prop('value').columns[0].dataField).toEqual('id'); + }); + }); + + describe('if there is any column.hidden is true', () => { + beforeEach(() => { + wrapper = shallow(shallowContext({ + columns: [{ + dataField: 'id', + text: 'ID' + }, { + dataField: 'name', + text: 'Name', + hidden: true + }] + })); + }); + + it('should render component with correct columns props', () => { + expect(wrapper.prop('value').columns).toHaveLength(columns.length - 1); + expect(wrapper.prop('value').columns[0].dataField).toEqual('id'); + }); + }); +}); diff --git a/packages/react-bootstrap-table2/test/contexts/index.test.js b/packages/react-bootstrap-table2/test/contexts/index.test.js index 737fcb9..8ff590e 100644 --- a/packages/react-bootstrap-table2/test/contexts/index.test.js +++ b/packages/react-bootstrap-table2/test/contexts/index.test.js @@ -47,6 +47,7 @@ describe('Context', () => { expect(wrapper.instance().CellEditContext).not.toBeDefined(); expect(wrapper.instance().FilterContext).not.toBeDefined(); expect(wrapper.instance().PaginationContext).not.toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).not.toBeDefined(); }); it('should render correctly', () => { @@ -77,6 +78,57 @@ describe('Context', () => { expect(wrapper.instance().CellEditContext).not.toBeDefined(); expect(wrapper.instance().FilterContext).not.toBeDefined(); expect(wrapper.instance().PaginationContext).not.toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).not.toBeDefined(); + }); + }); + + describe('if thers\'s any column hidden', () => { + beforeEach(() => { + const columnsWithHidden = [{ + dataField: keyField, + text: 'ID' + }, { + dataField: 'name', + text: 'Name', + hidden: true + }]; + wrapper = shallow( + + ); + wrapper.render(); + }); + + it('should create contexts correctly', () => { + expect(wrapper.instance().DataContext).toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).toBeDefined(); + expect(wrapper.instance().SelectionContext).not.toBeDefined(); + expect(wrapper.instance().CellEditContext).not.toBeDefined(); + expect(wrapper.instance().FilterContext).not.toBeDefined(); + expect(wrapper.instance().PaginationContext).not.toBeDefined(); + }); + }); + + describe('if columnToggle is enable', () => { + beforeEach(() => { + const columnToggle = { toggles: { id: true, name: true } }; + wrapper = shallow( + + ); + wrapper.render(); + }); + + it('should create contexts correctly', () => { + expect(wrapper.instance().DataContext).toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).toBeDefined(); + expect(wrapper.instance().SelectionContext).not.toBeDefined(); + expect(wrapper.instance().CellEditContext).not.toBeDefined(); + expect(wrapper.instance().FilterContext).not.toBeDefined(); + expect(wrapper.instance().PaginationContext).not.toBeDefined(); }); }); @@ -101,6 +153,7 @@ describe('Context', () => { expect(wrapper.instance().CellEditContext).not.toBeDefined(); expect(wrapper.instance().FilterContext).not.toBeDefined(); expect(wrapper.instance().PaginationContext).not.toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).not.toBeDefined(); }); }); @@ -134,6 +187,7 @@ describe('Context', () => { expect(wrapper.instance().CellEditContext).toBeDefined(); expect(wrapper.instance().FilterContext).not.toBeDefined(); expect(wrapper.instance().PaginationContext).not.toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).not.toBeDefined(); }); }); @@ -163,6 +217,7 @@ describe('Context', () => { expect(wrapper.instance().CellEditContext).not.toBeDefined(); expect(wrapper.instance().FilterContext).not.toBeDefined(); expect(wrapper.instance().PaginationContext).not.toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).not.toBeDefined(); }); }); @@ -193,6 +248,7 @@ describe('Context', () => { expect(wrapper.instance().CellEditContext).not.toBeDefined(); expect(wrapper.instance().FilterContext).toBeDefined(); expect(wrapper.instance().PaginationContext).not.toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).not.toBeDefined(); }); }); @@ -223,6 +279,7 @@ describe('Context', () => { expect(wrapper.instance().CellEditContext).not.toBeDefined(); expect(wrapper.instance().FilterContext).not.toBeDefined(); expect(wrapper.instance().PaginationContext).toBeDefined(); + expect(wrapper.instance().ColumnManagementContext).not.toBeDefined(); }); }); diff --git a/packages/react-bootstrap-table2/test/header.test.js b/packages/react-bootstrap-table2/test/header.test.js index 263ac54..43e7dd6 100644 --- a/packages/react-bootstrap-table2/test/header.test.js +++ b/packages/react-bootstrap-table2/test/header.test.js @@ -146,29 +146,6 @@ describe('Header', () => { }); }); - describe('when column.hidden is true', () => { - beforeEach(() => { - const newColumns = [{ - dataField: 'id', - text: 'ID', - hidden: true - }, { - dataField: 'name', - text: 'Name' - }]; - wrapper = shallow( -
- ); - }); - - it('should not render column with hidden value true', () => { - expect(wrapper.find(HeaderCell).length).toBe(1); - }); - }); - describe('when selectRow.mode is checkbox (multiple selection)', () => { beforeEach(() => { const selectRow = { mode: 'checkbox' }; diff --git a/packages/react-bootstrap-table2/test/row/row-pure-content.test.js b/packages/react-bootstrap-table2/test/row/row-pure-content.test.js index 3000f93..6ff0c96 100644 --- a/packages/react-bootstrap-table2/test/row/row-pure-content.test.js +++ b/packages/react-bootstrap-table2/test/row/row-pure-content.test.js @@ -173,33 +173,6 @@ describe('RowPureContent', () => { }); }); - describe('when column.hidden is true', () => { - beforeEach(() => { - const newColumns = [{ - dataField: 'id', - text: 'ID', - hidden: true - }, { - dataField: 'name', - text: 'Name' - }, { - dataField: 'price', - text: 'Price' - }]; - wrapper = shallow( - ); - }); - - it('should not render column with hidden value true', () => { - expect(wrapper.find(Cell).length).toBe(2); - }); - }); - describe('when column.style prop is defined', () => { let columns; const columnIndex = 1;