Merge pull request #800 from react-bootstrap-table/feat/723

Implement Column Toggle(723)
This commit is contained in:
Allen 2019-02-17 15:24:17 +08:00 committed by GitHub
commit cd27ff98ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 948 additions and 158 deletions

View File

@ -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.

View File

@ -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'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -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
}) => (
<div className="btn-group btn-group-toggle btn-group-vertical" data-toggle="buttons">
{
columns
.map(column => ({
...column,
toggle: toggles[column.dataField]
}))
.map(column => (
<button
type="button"
key={ column.dataField }
className={ \`btn btn-warning \${column.toggle ? 'active' : ''}\` }
data-toggle="button"
aria-pressed={ column.toggle ? 'true' : 'false' }
onClick={ () => onColumnToggle(column.dataField) }
>
{ column.text }
</button>
))
}
</div>
);
<ToolkitProvider
keyField="id"
data={ products }
columns={ columnsdt }
columnToggle
>
{
props => (
<div>
<CustomToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
const CustomToggleList = ({
columns,
onColumnToggle,
toggles
}) => (
<div className="btn-group btn-group-toggle btn-group-vertical" data-toggle="buttons">
{
columns
.map(column => ({
...column,
toggle: toggles[column.dataField]
}))
.map(column => (
<button
type="button"
key={ column.dataField }
className={ `btn btn-warning ${column.toggle ? 'active' : ''}` }
data-toggle="button"
aria-pressed={ column.toggle ? 'true' : 'false' }
onClick={ () => onColumnToggle(column.dataField) }
>
{ column.text }
</button>
))
}
</div>
);
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columnsdt }
columnToggle
>
{
props => (
<div>
<CustomToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -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
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable { ...props.baseProps } />
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -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'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -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'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList
contextual="success"
className="list-custom-class"
btnClassName="list-btn-custom-class"
{ ...props.columnToggleProps }
/>
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList
contextual="success"
className="list-custom-class"
btnClassName="list-btn-custom-class"
{ ...props.columnToggleProps }
/>
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</div>
);

View File

@ -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', () => <Bootstrap4DefaultSortTable />)
.add('Row selection table with bootstrap 4', () => <Bootstrap4RowSelectionTable />)
.add('Pagination table with bootstrap 4', () => <Bootstrap4PaginationTable />);
.add('Pagination table with bootstrap 4', () => <Bootstrap4PaginationTable />)
.add('Column Toggle with bootstrap 4', () => <Bootstrap4ColumnToggleTable />);
storiesOf('Work on Columns', module)
.addDecorator(bootstrapStyle())
@ -415,6 +423,13 @@ storiesOf('Table Search', module)
.add('Search Fromatted Value', () => <SearchFormattedData />)
.add('Custom Search Value', () => <CustomSearchValue />);
storiesOf('Column Toggle', module)
.addDecorator(bootstrapStyle())
.add('Basic Column Toggle', () => <BasicColumnToggle />)
.add('Default Visibility', () => <DefaultVisibility />)
.add('Styling Column Toggle', () => <StylingColumnToggle />)
.add('Custom Column Toggle', () => <CustomToggleList />);
storiesOf('Export CSV', module)
.addDecorator(bootstrapStyle())
.add('Basic Export CSV', () => <ExportCSV />)

View File

@ -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.
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';
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
columnToggle
>
{
props => (
<div>
<ToggleList { ...props.columnToggleProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
```
> `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`.

View File

@ -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 (
<ToolkitContext.Provider value={ {
searchProps: {
@ -98,6 +121,11 @@ class ToolkitProvider extends statelessDrcorator(React.Component) {
csvProps: {
onExport: this.handleExportCSV
},
columnToggleProps: {
columns: this.props.columns,
toggles: this.state.columnToggle,
onColumnToggle: this.onColumnToggle
},
baseProps
} }
>

View File

@ -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';

View File

@ -0,0 +1,3 @@
import ToggleList from './toggle-list';
export default { ToggleList };

View File

@ -0,0 +1,50 @@
import React from 'react';
import PropTypes from 'prop-types';
const ToggleList = ({
columns,
onColumnToggle,
toggles,
contextual,
className,
btnClassName
}) => (
<div className={ `btn-group btn-group-toggle ${className}` } data-toggle="buttons">
{
columns
.map(column => ({
...column,
toggle: toggles[column.dataField]
}))
.map(column => (
<button
type="button"
key={ column.dataField }
className={ `${btnClassName} btn btn-${contextual} ${column.toggle ? 'active' : ''}` }
data-toggle="button"
aria-pressed={ column.toggle ? 'true' : 'false' }
onClick={ () => onColumnToggle(column.dataField) }
>
{ column.text }
</button>
))
}
</div>
);
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;

View File

@ -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 (
<ColumnManagementContext.Provider value={ { columns: toggleColumn } }>
{ this.props.children }
</ColumnManagementContext.Provider>
);
}
}
return {
Provider: ColumnManagementProvider,
Consumer: ColumnManagementContext.Consumer
};
};

View File

@ -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
) => (
<Base
ref={ n => 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
) => (
<this.ColumnManagementContext.Provider
{ ...baseProps }
toggles={ this.props.columnToggle ? this.props.columnToggle.toggles : null }
>
<this.ColumnManagementContext.Consumer>
{
columnToggleProps => base(
rootProps,
filterProps,
searchProps,
sortProps,
paginationProps,
columnToggleProps
)
}
</this.ColumnManagementContext.Consumer>
</this.ColumnManagementContext.Provider>
);
}
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);
}

View File

@ -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;
}

View File

@ -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 (
<HeaderCell
index={ i }
key={ column.dataField }
column={ column }
onSort={ onSort }
sorting={ currSort }
onFilter={ onFilter }
onExternalFilter={ onExternalFilter }
sortOrder={ sortOrder }
isLastSorting={ isLastSorting }
/>);
}
return false;
return (
<HeaderCell
index={ i }
key={ column.dataField }
column={ column }
onSort={ onSort }
sorting={ currSort }
onFilter={ onFilter }
onExternalFilter={ onExternalFilter }
sortOrder={ sortOrder }
isLastSorting={ isLastSorting }
/>);
})
];

View File

@ -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 (
<EditingCellComponent
key={ `${content}-${index}-editing` }
row={ row }
rowIndex={ rowIndex }
column={ column }
columnIndex={ index }
/>
);
}
// 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 (
<Cell
key={ `${content}-${index}` }
<EditingCellComponent
key={ `${content}-${index}-editing` }
row={ row }
editable={ editableCell }
rowIndex={ rowIndex }
columnIndex={ index }
column={ column }
onStart={ onStart }
clickToEdit={ clickToEdit }
dbclickToEdit={ dbclickToEdit }
{ ...cellAttrs }
columnIndex={ index }
/>
);
}
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 (
<Cell
key={ `${content}-${index}` }
row={ row }
editable={ editableCell }
rowIndex={ rowIndex }
columnIndex={ index }
column={ column }
onStart={ onStart }
clickToEdit={ clickToEdit }
dbclickToEdit={ dbclickToEdit }
{ ...cellAttrs }
/>
);
});
}
}

View File

@ -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 => (
<BootstrapTable
data={ data }
columns={ columns }
keyField="id"
{ ...props }
/>
)));
const ColumnManagementContext = createColumnManagementContext();
function shallowContext(options = {}) {
return (
<ColumnManagementContext.Provider
data={ data }
columns={ columns }
{ ...options }
>
<ColumnManagementContext.Consumer>
{
columnToggleProps => mockBase(columnToggleProps)
}
</ColumnManagementContext.Consumer>
</ColumnManagementContext.Provider>
);
}
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');
});
});
});

View File

@ -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(
<BootstrapTable keyField={ keyField } data={ data } columns={ columnsWithHidden } />
);
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(
<BootstrapTable
keyField={ keyField }
data={ data }
columns={ columns }
columnToggle={ columnToggle }
/>
);
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();
});
});

View File

@ -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(
<Header
{ ...mockHeaderResolvedProps }
columns={ newColumns }
/>
);
});
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' };

View File

@ -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(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ newColumns }
row={ row }
/>);
});
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;