Compare commits

...

8 Commits

Author SHA1 Message Date
AllenFang
416fcf08d4 Publish
- react-bootstrap-table2-example@1.0.14
 - react-bootstrap-table2-filter@1.1.1
 - react-bootstrap-table2-paginator@2.0.1
 - react-bootstrap-table2-toolkit@1.1.2
 - react-bootstrap-table-next@2.0.1
2019-01-06 17:06:20 +08:00
Allen
0c650c0682 Merge pull request #747 from react-bootstrap-table/develop
20190106 release
2019-01-06 17:03:34 +08:00
Allen
7d30804da9 fix #636 (#746) 2019-01-06 16:25:02 +08:00
Allen
782c630e58 fix #734, fix #620 (#745) 2019-01-06 14:47:22 +08:00
Allen
22cc79961f fix #715 (#743) 2019-01-05 20:45:18 +08:00
Allen
a83b3d0d78 fix #729 (#742) 2019-01-05 17:00:52 +08:00
Allen
340ddb8722 fix #719 (#741) 2019-01-05 16:09:12 +08:00
AllenFang
32e455e65f add remote standalone pagination story 2019-01-05 14:37:07 +08:00
28 changed files with 645 additions and 117 deletions

View File

@@ -15,6 +15,7 @@
* [showExpandColumn](#showExpandColumn) * [showExpandColumn](#showExpandColumn)
* [onlyOneExpanding](#onlyOneExpanding) * [onlyOneExpanding](#onlyOneExpanding)
* [expandByColumnOnly](#expandByColumnOnly) * [expandByColumnOnly](#expandByColumnOnly)
* [expandColumnPosition](#expandColumnPosition)
* [expandColumnRenderer](#expandColumnRenderer) * [expandColumnRenderer](#expandColumnRenderer)
* [expandHeaderColumnRenderer](#expandHeaderColumnRenderer) * [expandHeaderColumnRenderer](#expandHeaderColumnRenderer)
@@ -153,3 +154,14 @@ const expandRow = {
expandByColumnOnly: true expandByColumnOnly: true
}; };
``` ```
### <a name='expandColumnPosition'>expandRow.expandColumnPosition - [String]</a>
Default is `left`. You can give this as `right` for rendering expand column in the right side.
```js
const expandRow = {
renderer: (row) => ...,
showExpandColumn: true,
expandColumnPosition: 'right'
};
```

View File

@@ -0,0 +1,57 @@
/* eslint no-console: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(8);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter({
onFilter: filterVal => console.log(`Filter Value: ${filterVal}`)
})
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name',
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
filter: textFilter({
onFilter: filterVal => console.log(\`Filter Value: $\{filterVal}\`)
})
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } filter={ filterFactory() } />
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,190 @@
/* eslint react/no-multi-comp: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(87);
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 paginationFactory, { PaginationProvider, PaginationListStandalone } from 'react-bootstrap-table2-paginator';
// ...
const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div>
<PaginationProvider
pagination={
paginationFactory({
custom: true,
page,
sizePerPage,
totalSize
})
}
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<div>
<p>Current Page: { paginationProps.page }</p>
<p>Current SizePerPage: { paginationProps.sizePerPage }</p>
</div>
<div>
<PaginationListStandalone
{ ...paginationProps }
/>
</div>
<BootstrapTable
remote
keyField="id"
data={ data }
columns={ columns }
onTableChange={ onTableChange }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
</div>
);
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1,
data: products.slice(0, 10),
sizePerPage: 10
};
}
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({
page,
data: products.slice(currentIndex, currentIndex + sizePerPage),
sizePerPage
}));
}, 2000);
}
render() {
const { data, sizePerPage, page } = this.state;
return (
<RemotePagination
data={ data }
page={ page }
sizePerPage={ sizePerPage }
totalSize={ products.length }
onTableChange={ this.handleTableChange }
/>
);
}
}
`;
const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div>
<PaginationProvider
pagination={
paginationFactory({
custom: true,
page,
sizePerPage,
totalSize
})
}
>
{
({
paginationProps,
paginationTableProps
}) => (
<div>
<div>
<p>Current Page: { paginationProps.page }</p>
<p>Current SizePerPage: { paginationProps.sizePerPage }</p>
</div>
<div>
<PaginationListStandalone
{ ...paginationProps }
/>
</div>
<BootstrapTable
remote
keyField="id"
data={ data }
columns={ columns }
onTableChange={ onTableChange }
{ ...paginationTableProps }
/>
</div>
)
}
</PaginationProvider>
<Code>{ sourceCode }</Code>
</div>
);
RemotePagination.propTypes = {
data: PropTypes.array.isRequired,
page: PropTypes.number.isRequired,
totalSize: PropTypes.number.isRequired,
sizePerPage: PropTypes.number.isRequired,
onTableChange: PropTypes.func.isRequired
};
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1,
data: products.slice(0, 10),
sizePerPage: 10
};
}
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({
page,
data: products.slice(currentIndex, currentIndex + sizePerPage),
sizePerPage
}));
}, 2000);
}
render() {
const { data, sizePerPage, page } = this.state;
return (
<RemotePagination
data={ data }
page={ page }
sizePerPage={ sizePerPage }
totalSize={ products.length }
onTableChange={ this.handleTableChange }
/>
);
}
}
export default Container;

View File

@@ -0,0 +1,76 @@
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 expandRow = {
renderer: row => (
<div>
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true,
expandColumnPosition: '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 expandRow = {
renderer: row => (
<div>
<p>{ \`This Expand row is belong to rowKey $\{row.id}\` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true,
expandColumnPosition: 'right'
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-example", "name": "react-bootstrap-table2-example",
"version": "1.0.13", "version": "1.0.14",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"private": true, "private": true,

View File

@@ -74,6 +74,7 @@ import ProgrammaticallyMultiSelectFilter from 'examples/column-filter/programmat
import CustomFilter from 'examples/column-filter/custom-filter'; import CustomFilter from 'examples/column-filter/custom-filter';
import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter'; import AdvanceCustomFilter from 'examples/column-filter/advance-custom-filter';
import ClearAllFilters from 'examples/column-filter/clear-all-filters'; import ClearAllFilters from 'examples/column-filter/clear-all-filters';
import FilterHooks from 'examples/column-filter/filter-hooks';
// work on rows // work on rows
import RowStyleTable from 'examples/rows/row-style'; import RowStyleTable from 'examples/rows/row-style';
@@ -141,6 +142,7 @@ import ExpandColumn from 'examples/row-expand/expand-column';
import OnlyExpandByColumn from 'examples/row-expand/expand-by-column-only.js'; import OnlyExpandByColumn from 'examples/row-expand/expand-by-column-only.js';
import ExpandOnlyOne from 'examples/row-expand/expand-only-one'; import ExpandOnlyOne from 'examples/row-expand/expand-only-one';
import CustomExpandColumn from 'examples/row-expand/custom-expand-column'; 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 ExpandHooks from 'examples/row-expand/expand-hooks';
// pagination // pagination
@@ -154,6 +156,7 @@ import CustomPageListTable from 'examples/pagination/custom-page-list';
import StandalonePaginationList from 'examples/pagination/standalone-pagination-list'; import StandalonePaginationList from 'examples/pagination/standalone-pagination-list';
import StandaloneSizePerPage from 'examples/pagination/standalone-size-per-page'; import StandaloneSizePerPage from 'examples/pagination/standalone-size-per-page';
import FullyCustomPaginationTable from 'examples/pagination/fully-custom-pagination'; import FullyCustomPaginationTable from 'examples/pagination/fully-custom-pagination';
import RemoteStandalonePaginationTable from 'examples/pagination/remote-standalone-pagination';
// search // search
import SearchTable from 'examples/search'; import SearchTable from 'examples/search';
@@ -273,7 +276,8 @@ storiesOf('Column Filter', module)
.add('Custom Filter', () => <CustomFilter />) .add('Custom Filter', () => <CustomFilter />)
.add('Advance Custom Filter', () => <AdvanceCustomFilter />) .add('Advance Custom Filter', () => <AdvanceCustomFilter />)
.add('Preserved Option Order on Select Filter', () => <SelectFilterWithPreservedOptionsOrder />) .add('Preserved Option Order on Select Filter', () => <SelectFilterWithPreservedOptionsOrder />)
.add('Clear All Filters', () => <ClearAllFilters />); .add('Clear All Filters', () => <ClearAllFilters />)
.add('Filter Hooks', () => <FilterHooks />);
storiesOf('Work on Rows', module) storiesOf('Work on Rows', module)
.addDecorator(bootstrapStyle()) .addDecorator(bootstrapStyle())
@@ -346,6 +350,7 @@ storiesOf('Row Expand', module)
.add('Only Expand by Indicator', () => <OnlyExpandByColumn />) .add('Only Expand by Indicator', () => <OnlyExpandByColumn />)
.add('Expand Only One Row at The Same Time', () => <ExpandOnlyOne />) .add('Expand Only One Row at The Same Time', () => <ExpandOnlyOne />)
.add('Custom Expand Indicator', () => <CustomExpandColumn />) .add('Custom Expand Indicator', () => <CustomExpandColumn />)
.add('Expand Column Position', () => <ExpandColumnPosition />)
.add('Expand Hooks', () => <ExpandHooks />); .add('Expand Hooks', () => <ExpandHooks />);
storiesOf('Pagination', module) storiesOf('Pagination', module)
@@ -359,7 +364,8 @@ storiesOf('Pagination', module)
.add('Custom SizePerPage', () => <CustomSizePerPageTable />) .add('Custom SizePerPage', () => <CustomSizePerPageTable />)
.add('Standalone Pagination List', () => <StandalonePaginationList />) .add('Standalone Pagination List', () => <StandalonePaginationList />)
.add('Standalone SizePerPage Dropdown', () => <StandaloneSizePerPage />) .add('Standalone SizePerPage Dropdown', () => <StandaloneSizePerPage />)
.add('Fully Custom Pagination', () => <FullyCustomPaginationTable />); .add('Fully Custom Pagination', () => <FullyCustomPaginationTable />)
.add('Remote Fully Custom Pagination', () => <RemoteStandalonePaginationTable />);
storiesOf('Table Search', module) storiesOf('Table Search', module)
.addDecorator(bootstrapStyle()) .addDecorator(bootstrapStyle())

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-filter", "name": "react-bootstrap-table2-filter",
"version": "1.1.0", "version": "1.1.1",
"description": "it's a column filter addon for react-bootstrap-table2", "description": "it's a column filter addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -64,6 +64,10 @@ export default (
return; return;
} }
if (filter.props.onFilter) {
filter.props.onFilter(filterVal);
}
this.forceUpdate(); this.forceUpdate();
}; };
} }

View File

@@ -44,7 +44,8 @@ describe('FilterContext', () => {
const handleFilterChange = jest.fn(); const handleFilterChange = jest.fn();
function shallowContext( function shallowContext(
enableRemote = false enableRemote = false,
tableColumns = columns
) { ) {
mockBase.mockReset(); mockBase.mockReset();
handleFilterChange.mockReset(); handleFilterChange.mockReset();
@@ -56,7 +57,7 @@ describe('FilterContext', () => {
return ( return (
<FilterContext.Provider <FilterContext.Provider
columns={ columns } columns={ tableColumns }
data={ data } data={ data }
> >
<FilterContext.Consumer> <FilterContext.Consumer>
@@ -225,6 +226,32 @@ 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('combination', () => { describe('combination', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallow(shallowContext()); wrapper = shallow(shallowContext());

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-paginator", "name": "react-bootstrap-table2-paginator",
"version": "2.0.0", "version": "2.0.1",
"description": "it's the pagination addon for react-bootstrap-table2", "description": "it's the pagination addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -135,8 +135,8 @@ export default ExtendBase =>
calculateSizePerPageStatus() { calculateSizePerPageStatus() {
const { sizePerPageList } = this.props; const { sizePerPageList } = this.props;
return sizePerPageList.map((_sizePerPage) => { return sizePerPageList.map((_sizePerPage) => {
const pageText = _sizePerPage.text || _sizePerPage; const pageText = typeof _sizePerPage.text !== 'undefined' ? _sizePerPage.text : _sizePerPage;
const pageNumber = _sizePerPage.value || _sizePerPage; const pageNumber = typeof _sizePerPage.value !== 'undefined' ? _sizePerPage.value : _sizePerPage;
return { return {
text: `${pageText}`, text: `${pageText}`,
page: pageNumber page: pageNumber

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-toolkit", "name": "react-bootstrap-table2-toolkit",
"version": "1.1.1", "version": "1.1.2",
"description": "The toolkit for react-bootstrap-table2", "description": "The toolkit for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -25,13 +25,19 @@ export default Base =>
let data; let data;
if (typeof source !== 'undefined') { if (typeof source !== 'undefined') {
data = source; data = source;
} else if (options.exportAll) {
data = this.props.data;
} else { } else {
data = options.exportAll ? this.props.data : this.getData(); const payload = {};
this.tableExposedAPIEmitter.emit('get.table.data', payload);
data = payload.result;
} }
// filter data // filter data
if (options.onlyExportSelection) { if (options.onlyExportSelection) {
const selections = this.getSelected(); const payload = {};
this.tableExposedAPIEmitter.emit('get.selected.rows', payload);
const selections = payload.result;
data = data.filter(row => !!selections.find(sel => row[keyField] === sel)); data = data.filter(row => !!selections.find(sel => row[keyField] === sel));
} }
const content = transform(data, meta, this._.get, options); const content = transform(data, meta, this._.get, options);

View File

@@ -2,9 +2,7 @@ import Operation from './src/op';
export default Base => export default Base =>
class StatelessOperation extends Operation.csvOperation(Base) { class StatelessOperation extends Operation.csvOperation(Base) {
registerExposedAPI = (...exposedFuncs) => { registerExposedAPI = (tableExposedAPIEmitter) => {
exposedFuncs.forEach((func) => { this.tableExposedAPIEmitter = tableExposedAPIEmitter;
this[func.name] = func;
});
} }
}; };

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table-next", "name": "react-bootstrap-table-next",
"version": "2.0.0", "version": "2.0.1",
"description": "Next generation of react-bootstrap-table", "description": "Next generation of react-bootstrap-table",
"main": "./lib/index.js", "main": "./lib/index.js",
"repository": { "repository": {

View File

@@ -14,13 +14,10 @@ class BootstrapTable extends PropsBaseResolver(Component) {
constructor(props) { constructor(props) {
super(props); super(props);
this.validateProps(); this.validateProps();
if (props.registerExposedAPI) {
props.registerExposedAPI(this.getData);
}
} }
// Exposed APIs // Exposed APIs
getData() { getData = () => {
return this.visibleRows(); return this.visibleRows();
} }
@@ -161,7 +158,11 @@ BootstrapTable.propTypes = {
onlyOneExpanding: PropTypes.bool, onlyOneExpanding: PropTypes.bool,
expandByColumnOnly: PropTypes.bool, expandByColumnOnly: PropTypes.bool,
expandColumnRenderer: PropTypes.func, expandColumnRenderer: PropTypes.func,
expandHeaderColumnRenderer: PropTypes.func expandHeaderColumnRenderer: PropTypes.func,
expandColumnPosition: PropTypes.oneOf([
Const.INDICATOR_POSITION_LEFT,
Const.INDICATOR_POSITION_RIGHT
])
}), }),
rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
rowEvents: PropTypes.object, rowEvents: PropTypes.object,

View File

@@ -6,5 +6,7 @@ export default {
ROW_SELECT_DISABLED: 'ROW_SELECT_DISABLED', ROW_SELECT_DISABLED: 'ROW_SELECT_DISABLED',
CHECKBOX_STATUS_CHECKED: 'checked', CHECKBOX_STATUS_CHECKED: 'checked',
CHECKBOX_STATUS_INDETERMINATE: 'indeterminate', CHECKBOX_STATUS_INDETERMINATE: 'indeterminate',
CHECKBOX_STATUS_UNCHECKED: 'unchecked' CHECKBOX_STATUS_UNCHECKED: 'unchecked',
INDICATOR_POSITION_LEFT: 'left',
INDICATOR_POSITION_RIGHT: 'right'
}; };

View File

@@ -1,6 +1,8 @@
/* eslint no-return-assign: 0 */ /* eslint no-return-assign: 0 */
/* eslint no-param-reassign: 0 */
/* eslint class-methods-use-this: 0 */ /* eslint class-methods-use-this: 0 */
import React, { Component } from 'react'; import React, { Component } from 'react';
import EventEmitter from 'events';
import _ from '../utils'; import _ from '../utils';
import createDataContext from './data-context'; import createDataContext from './data-context';
import createSortContext from './sort-context'; import createSortContext from './sort-context';
@@ -16,6 +18,13 @@ const withContext = Base =>
super(props); super(props);
this.DataContext = createDataContext(); this.DataContext = createDataContext();
if (props.registerExposedAPI) {
const exposedAPIEmitter = new EventEmitter();
exposedAPIEmitter.on('get.table.data', payload => payload.result = this.table.getData());
exposedAPIEmitter.on('get.selected.rows', payload => payload.result = this.selectionContext.getSelected());
props.registerExposedAPI(exposedAPIEmitter);
}
if (props.columns.filter(col => col.sort).length > 0) { if (props.columns.filter(col => col.sort).length > 0) {
this.SortContext = createSortContext( this.SortContext = createSortContext(
dataOperator, this.isRemoteSort, this.handleRemoteSortChange); dataOperator, this.isRemoteSort, this.handleRemoteSortChange);
@@ -255,9 +264,8 @@ const withContext = Base =>
} }
render() { render() {
const { keyField, columns, bootstrap4, registerExposedAPI } = this.props; const { keyField, columns, bootstrap4 } = this.props;
const baseProps = { keyField, columns }; const baseProps = { keyField, columns };
if (registerExposedAPI) baseProps.registerExposedAPI = registerExposedAPI;
let base = this.renderBase(); let base = this.renderBase();

View File

@@ -14,14 +14,6 @@ class SelectionProvider extends React.Component {
keyField: PropTypes.string.isRequired keyField: PropTypes.string.isRequired
} }
constructor(props) {
super(props);
if (props.registerExposedAPI) {
const getSelected = () => this.getSelected();
props.registerExposedAPI(getSelected);
}
}
state = { selected: this.props.selectRow.selected || [] }; state = { selected: this.props.selectRow.selected || [] };
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {

View File

@@ -7,6 +7,7 @@ import SelectionHeaderCell from './row-selection/selection-header-cell';
import ExpandHeaderCell from './row-expand/expand-header-cell'; import ExpandHeaderCell from './row-expand/expand-header-cell';
import withHeaderSelection from './row-selection/selection-header-cell-consumer'; import withHeaderSelection from './row-selection/selection-header-cell-consumer';
import withHeaderExpansion from './row-expand/expand-header-cell-consumer'; import withHeaderExpansion from './row-expand/expand-header-cell-consumer';
import Const from './const';
const Header = (props) => { const Header = (props) => {
const { const {
@@ -32,15 +33,11 @@ const Header = (props) => {
SelectionHeaderCellComp = withHeaderSelection(SelectionHeaderCell); SelectionHeaderCellComp = withHeaderSelection(SelectionHeaderCell);
} }
return ( const isRenderExpandColumnInLeft = (
<thead> expandColumnPosition = Const.INDICATOR_POSITION_LEFT
<tr className={ className }> ) => expandColumnPosition === Const.INDICATOR_POSITION_LEFT;
<ExpansionHeaderCellComp />
{ const childrens = [
!selectRow.hideSelectColumn ?
<SelectionHeaderCellComp /> : null
}
{
columns.map((column, i) => { columns.map((column, i) => {
if (!column.hidden) { if (!column.hidden) {
const currSort = column.dataField === sortField; const currSort = column.dataField === sortField;
@@ -61,7 +58,24 @@ const Header = (props) => {
} }
return false; return false;
}) })
];
if (!selectRow.hideSelectColumn) {
childrens.unshift(<SelectionHeaderCellComp key="selection" />);
} }
if (expandRow.showExpandColumn) {
if (isRenderExpandColumnInLeft(expandRow.expandColumnPosition)) {
childrens.unshift(<ExpansionHeaderCellComp key="expansion" />);
} else {
childrens.push(<ExpansionHeaderCellComp key="expansion" />);
}
}
return (
<thead>
<tr className={ className }>
{ childrens }
</tr> </tr>
</thead> </thead>
); );

View File

@@ -1,3 +1,4 @@
/* eslint class-methods-use-this: 0 */
/* eslint react/prop-types: 0 */ /* eslint react/prop-types: 0 */
/* eslint no-plusplus: 0 */ /* eslint no-plusplus: 0 */
import React from 'react'; import React from 'react';
@@ -8,6 +9,7 @@ import SelectionCell from '../row-selection/selection-cell';
import shouldUpdater from './should-updater'; import shouldUpdater from './should-updater';
import eventDelegater from './event-delegater'; import eventDelegater from './event-delegater';
import RowPureContent from './row-pure-content'; import RowPureContent from './row-pure-content';
import Const from '../const';
export default class RowAggregator extends shouldUpdater(eventDelegater(React.Component)) { export default class RowAggregator extends shouldUpdater(eventDelegater(React.Component)) {
static propTypes = { static propTypes = {
@@ -43,6 +45,12 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
return this.shouldUpdateRowContent; return this.shouldUpdateRowContent;
} }
isRenderExpandColumnInLeft(
expandColumnPosition = Const.INDICATOR_POSITION_LEFT
) {
return expandColumnPosition === Const.INDICATOR_POSITION_LEFT;
}
render() { render() {
const { const {
row, row,
@@ -64,7 +72,7 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
} = this.props; } = this.props;
const key = _.get(row, keyField); const key = _.get(row, keyField);
const { hideSelectColumn, clickToSelect } = selectRow; const { hideSelectColumn, clickToSelect } = selectRow;
const { showExpandColumn } = expandRow; const { showExpandColumn, expandColumnPosition } = expandRow;
const newAttrs = this.delegate({ ...attrs }); const newAttrs = this.delegate({ ...attrs });
if (clickToSelect || !!expandRow.renderer) { if (clickToSelect || !!expandRow.renderer) {
@@ -73,39 +81,9 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
let tabIndexStart = (rowIndex * visibleColumnSize) + 1; let tabIndexStart = (rowIndex * visibleColumnSize) + 1;
return ( const childrens = [(
<tr
style={ style }
className={ className }
{ ...newAttrs }
>
{
showExpandColumn ? (
<ExpandCell
{ ...expandRow }
rowKey={ key }
rowIndex={ rowIndex }
expanded={ expanded }
expandable={ expandable }
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
/>
) : null
}
{
!hideSelectColumn
? (
<SelectionCell
{ ...selectRow }
rowKey={ key }
rowIndex={ rowIndex }
selected={ selected }
disabled={ !selectable }
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
/>
)
: null
}
<RowPureContent <RowPureContent
key="row"
row={ row } row={ row }
columns={ columns } columns={ columns }
keyField={ keyField } keyField={ keyField }
@@ -114,6 +92,48 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
tabIndexStart={ tabIndexCell ? tabIndexStart : -1 } tabIndexStart={ tabIndexCell ? tabIndexStart : -1 }
{ ...rest } { ...rest }
/> />
)];
if (!hideSelectColumn) {
childrens.unshift((
<SelectionCell
{ ...selectRow }
key="selection-cell"
rowKey={ key }
rowIndex={ rowIndex }
selected={ selected }
disabled={ !selectable }
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
/>
));
}
if (showExpandColumn) {
const expandCell = (
<ExpandCell
{ ...expandRow }
key="expand-cell"
rowKey={ key }
rowIndex={ rowIndex }
expanded={ expanded }
expandable={ expandable }
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
/>
);
if (this.isRenderExpandColumnInLeft(expandColumnPosition)) {
childrens.unshift(expandCell);
} else {
childrens.push(expandCell);
}
}
return (
<tr
style={ style }
className={ className }
{ ...newAttrs }
>
{ childrens }
</tr> </tr>
); );
} }

View File

@@ -20,6 +20,19 @@ export default ExtendBase =>
); );
} }
// Only use for simple-row
shouldUpdateByColumnsForSimpleCheck(nextProps) {
if (this.props.columns.length !== nextProps.columns.length) {
return true;
}
for (let i = 0; i < this.props.columns.length; i += 1) {
if (this.props.columns[i].hidden !== nextProps.columns[i].hidden) {
return true;
}
}
return false;
}
shouldUpdatedByNormalProps(nextProps) { shouldUpdatedByNormalProps(nextProps) {
const shouldUpdate = const shouldUpdate =
this.props.rowIndex !== nextProps.rowIndex || this.props.rowIndex !== nextProps.rowIndex ||

View File

@@ -15,7 +15,8 @@ class SimpleRow extends shouldUpdater(eventDelegater(Component)) {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
this.shouldUpdateRowContent = false; this.shouldUpdateRowContent = false;
this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps); this.shouldUpdateRowContent =
this.shouldUpdateChild(nextProps) || this.shouldUpdateByColumnsForSimpleCheck(nextProps);
if (this.shouldUpdateRowContent) return true; if (this.shouldUpdateRowContent) return true;
return this.shouldUpdatedBySelfProps(nextProps); return this.shouldUpdatedBySelfProps(nextProps);

View File

@@ -60,26 +60,6 @@ describe('BootstrapTable', () => {
}); });
}); });
describe('when props.registerExposedAPI is defined', () => {
const registerExposedAPI = jest.fn();
beforeEach(() => {
registerExposedAPI.mockClear();
wrapper = shallow(
<BootstrapTable
keyField="id"
columns={ columns }
data={ data }
registerExposedAPI={ registerExposedAPI }
/>
);
});
it('should call props.registerExposedAPI correctly', () => {
expect(registerExposedAPI).toHaveBeenCalledTimes(1);
expect(registerExposedAPI.mock.calls[0][0].name).toEqual('getData');
});
});
describe('when props.classes was defined', () => { describe('when props.classes was defined', () => {
const classes = 'foo'; const classes = 'foo';

View File

@@ -225,4 +225,31 @@ describe('Context', () => {
expect(wrapper.instance().PaginationContext).toBeDefined(); expect(wrapper.instance().PaginationContext).toBeDefined();
}); });
}); });
describe('if registerExposedAPI props is defined', () => {
const registerExposedAPI = jest.fn();
beforeEach(() => {
const PaginationContext = React.createContext();
const paginator = {
createContext: jest.fn().mockReturnValue({
Provider: PaginationContext.Provider,
Consumer: PaginationContext.Consumer
})
};
wrapper = shallow(
<BootstrapTable
keyField={ keyField }
data={ data }
columns={ columns }
pagination={ paginator }
registerExposedAPI={ registerExposedAPI }
/>
);
wrapper.render();
});
it('should call props.registerExposedAPI correctly', () => {
expect(registerExposedAPI).toHaveBeenCalledTimes(1);
});
});
}); });

View File

@@ -254,5 +254,33 @@ describe('Header', () => {
expect(wrapper.find(ExpandHeaderCell).length).toBe(1); expect(wrapper.find(ExpandHeaderCell).length).toBe(1);
}); });
}); });
describe('if props.expandRow.showExpandColumn is true but props.expandRow.expandColumnPosition is "right"', () => {
beforeEach(() => {
const expandRow = {
renderer: jest.fn(),
showExpandColumn: true,
expandColumnPosition: Const.INDICATOR_POSITION_RIGHT
};
wrapper = mount(
<ExpansionContext.Provider
data={ data }
keyField={ keyField }
expandRow={ expandRow }
>
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
expandRow={ expandRow }
/>
</ExpansionContext.Provider>
);
});
it('should render expansion column correctly', () => {
const header = wrapper.find(Header).children();
expect(header.children().children().last().find(ExpandHeaderCell)).toHaveLength(1);
});
});
}); });
}); });

View File

@@ -9,6 +9,7 @@ import bindExpansion from '../../src/row-expand/row-consumer';
import ExpandCell from '../../src/row-expand/expand-cell'; import ExpandCell from '../../src/row-expand/expand-cell';
import SelectionCell from '../../src/row-selection/selection-cell'; import SelectionCell from '../../src/row-selection/selection-cell';
import RowAggregator from '../../src/row/aggregate-row'; import RowAggregator from '../../src/row/aggregate-row';
import Const from '../../src/const';
describe('Row Aggregator', () => { describe('Row Aggregator', () => {
let wrapper; let wrapper;
@@ -157,6 +158,27 @@ describe('Row Aggregator', () => {
expect(expandCell.props().expanded).toEqual(rowAggregator.props().expanded); expect(expandCell.props().expanded).toEqual(rowAggregator.props().expanded);
}); });
}); });
describe('if props.expandRow.showExpandColumn is true but props.expandRow.expandColumnPosition is "right"', () => {
beforeEach(() => {
const expandRow = {
renderer: jest.fn(),
showExpandColumn: true,
expandColumnPosition: Const.INDICATOR_POSITION_RIGHT
};
wrapper = mount(
<ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<RowAggregatorWithExpansion { ...getBaseProps() } />
</ExpansionContext.Provider>
);
});
it('should render expansion column correctly', () => {
rowAggregator = wrapper.find(RowAggregator);
expect(rowAggregator).toHaveLength(1);
expect(rowAggregator.children().children().last().type()).toEqual(ExpandCell);
});
});
}); });
describe('createClickEventHandler', () => { describe('createClickEventHandler', () => {

View File

@@ -104,6 +104,50 @@ describe('Row shouldUpdater', () => {
}); });
}); });
describe('shouldUpdateByColumnsForSimpleCheck', () => {
describe('when nextProps.columns.length is not eq props.columns.length', () => {
beforeEach(() => {
props = {
columns: [{ dataField: 'price', text: 'Price' }]
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, columns: [...props.columns, { dataField: 'name', text: 'Name' }] };
expect(wrapper.instance().shouldUpdateByColumnsForSimpleCheck(nextProps)).toBeTruthy();
});
});
describe('when any nextProps.columns.hidden is change', () => {
beforeEach(() => {
props = {
columns: [{ dataField: 'price', text: 'Price' }]
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, columns: [{ dataField: 'price', text: 'Price', hidden: true }] };
expect(wrapper.instance().shouldUpdateByColumnsForSimpleCheck(nextProps)).toBeTruthy();
});
});
describe('if any nextProps.columns.hidden is not change and column length is same', () => {
beforeEach(() => {
props = {
columns: [{ dataField: 'price', text: 'Price' }]
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return false', () => {
nextProps = { ...props, columns: [...props.columns] };
expect(wrapper.instance().shouldUpdateByColumnsForSimpleCheck(nextProps)).toBeFalsy();
});
});
});
describe('shouldUpdatedByNormalProps', () => { describe('shouldUpdatedByNormalProps', () => {
describe('when nextProps.rowIndex is not eq props.rowIndex', () => { describe('when nextProps.rowIndex is not eq props.rowIndex', () => {
beforeEach(() => { beforeEach(() => {