From 4c89f91a83199a708abd8083ae482fa2b90f3523 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sun, 24 Dec 2017 16:56:05 +0800 Subject: [PATCH 1/3] implement remote resolver --- .../react-bootstrap-table2/src/container.js | 28 +------------- .../src/props-resolver/remote-resolver.js | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js diff --git a/packages/react-bootstrap-table2/src/container.js b/packages/react-bootstrap-table2/src/container.js index 8fb7959cf..4ba44d366 100644 --- a/packages/react-bootstrap-table2/src/container.js +++ b/packages/react-bootstrap-table2/src/container.js @@ -11,46 +11,22 @@ import { wrapWithPagination } from './table-factory'; +import remoteResolver from './props-resolver/remote-resolver'; import _ from './utils'; const withDataStore = Base => - class BootstrapTableContainer extends Component { + class BootstrapTableContainer extends remoteResolver(Component) { constructor(props) { super(props); this.store = new Store(props.keyField); this.store.data = props.data; this.handleUpdateCell = this.handleUpdateCell.bind(this); - this.handleRemotePageChange = this.handleRemotePageChange.bind(this); - this.handleRemoteFilterChange = this.handleRemoteFilterChange.bind(this); } componentWillReceiveProps(nextProps) { this.store.data = nextProps.data; } - getNewestState(state = {}) { - return { - page: this.store.page, - sizePerPage: this.store.sizePerPage, - filters: this.store.filters, - ...state - }; - } - - handleRemotePageChange() { - this.props.onTableChange('pagination', this.getNewestState()); - } - - // refactoring later for isRemotePagination - handleRemoteFilterChange(isRemotePagination) { - const newState = {}; - if (isRemotePagination) { - const options = this.props.pagination.options || {}; - newState.page = _.isDefined(options.pageStartIndex) ? options.pageStartIndex : 1; - } - this.props.onTableChange('filter', this.getNewestState(newState)); - } - handleUpdateCell(rowId, dataField, newValue) { const { cellEdit } = this.props; // handle cell editing internal diff --git a/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js b/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js new file mode 100644 index 000000000..ddadafc0e --- /dev/null +++ b/packages/react-bootstrap-table2/src/props-resolver/remote-resolver.js @@ -0,0 +1,37 @@ +import _ from '../utils'; + +export default ExtendBase => + class RemoteResolver extends ExtendBase { + getNewestState(state = {}) { + const store = this.store || this.props.store; + return { + page: store.page, + sizePerPage: store.sizePerPage, + filters: store.filters, + ...state + }; + } + + isRemotePagination() { + const { remote } = this.props; + return remote === true || (_.isObject(remote) && remote.pagination); + } + + isRemoteFiltering() { + const { remote } = this.props; + return remote === true || (_.isObject(remote) && remote.filter); + } + + handleRemotePageChange() { + this.props.onTableChange('pagination', this.getNewestState()); + } + + handleRemoteFilterChange() { + const newState = {}; + if (this.isRemotePagination()) { + const options = this.props.pagination.options || {}; + newState.page = _.isDefined(options.pageStartIndex) ? options.pageStartIndex : 1; + } + this.props.onTableChange('filter', this.getNewestState(newState)); + } + }; From 3bfeec7946e92b3cdae6bb0f4d7ca82b0dcbb768 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Mon, 25 Dec 2017 17:21:12 +0800 Subject: [PATCH 2/3] fix bug for onTableChange argument change --- .../examples/loading-overlay/empty-table-overlay.js | 2 +- .../examples/loading-overlay/table-overlay.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-bootstrap-table2-example/examples/loading-overlay/empty-table-overlay.js b/packages/react-bootstrap-table2-example/examples/loading-overlay/empty-table-overlay.js index 6366f16d2..aa62c2044 100644 --- a/packages/react-bootstrap-table2-example/examples/loading-overlay/empty-table-overlay.js +++ b/packages/react-bootstrap-table2-example/examples/loading-overlay/empty-table-overlay.js @@ -116,7 +116,7 @@ class EmptyTableOverlay extends React.Component { }; } - handleTableChange = ({ page, sizePerPage }) => { + handleTableChange = (type, { page, sizePerPage }) => { const currentIndex = (page - 1) * sizePerPage; setTimeout(() => { this.setState(() => ({ diff --git a/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js b/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js index b0dca0482..c3c954636 100644 --- a/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js +++ b/packages/react-bootstrap-table2-example/examples/loading-overlay/table-overlay.js @@ -127,7 +127,7 @@ class Container extends React.Component { }; } - handleTableChange = ({ page, sizePerPage }) => { + handleTableChange = (type, { page, sizePerPage }) => { const currentIndex = (page - 1) * sizePerPage; setTimeout(() => { this.setState(() => ({ From fb81595f73738c88177187ec44064b35956ae5dd Mon Sep 17 00:00:00 2001 From: AllenFang Date: Mon, 25 Dec 2017 17:31:49 +0800 Subject: [PATCH 3/3] refactoring wrapper mechanism --- .../src/index.js | 4 +- .../src/wrapper.js | 116 ++++--- .../test/wrapper.test.js | 18 +- .../src/index.js | 4 +- .../src/page.js | 8 +- .../src/wrapper.js | 292 ++++++++---------- .../test/page.test.js | 8 +- .../test/wrapper.test.js | 84 +---- .../src/cell-edit/wrapper.js | 152 ++++----- .../react-bootstrap-table2/src/container.js | 73 +++-- .../src/row-selection/wrapper.js | 151 +++++---- .../src/sort/wrapper.js | 91 +++--- .../src/table-factory.js | 43 --- .../test/cell-edit/wrapper.test.js | 28 +- .../test/container.test.js | 186 +++++------ .../props-resolver/remote-resolver.test.js | 180 +++++++++++ .../test/row-selection/wrapper.test.js | 3 +- .../test/sort/wrapper.test.js | 13 +- 18 files changed, 725 insertions(+), 729 deletions(-) delete mode 100644 packages/react-bootstrap-table2/src/table-factory.js create mode 100644 packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js diff --git a/packages/react-bootstrap-table2-filter/src/index.js b/packages/react-bootstrap-table2-filter/src/index.js index 4bba49df0..4b3855062 100644 --- a/packages/react-bootstrap-table2-filter/src/index.js +++ b/packages/react-bootstrap-table2-filter/src/index.js @@ -1,9 +1,9 @@ import TextFilter from './components/text'; -import FilterWrapper from './wrapper'; +import wrapperFactory from './wrapper'; import * as Comparison from './comparison'; export default (options = {}) => ({ - FilterWrapper, + wrapperFactory, options }); diff --git a/packages/react-bootstrap-table2-filter/src/wrapper.js b/packages/react-bootstrap-table2-filter/src/wrapper.js index 48e4d7343..780c4d859 100644 --- a/packages/react-bootstrap-table2-filter/src/wrapper.js +++ b/packages/react-bootstrap-table2-filter/src/wrapper.js @@ -1,76 +1,66 @@ -/* eslint react/prop-types: 0 */ -import { Component } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { filters } from './filter'; import { LIKE } from './comparison'; -export default class FilterWrapper extends Component { - static propTypes = { - store: PropTypes.object.isRequired, - columns: PropTypes.array.isRequired, - baseElement: PropTypes.func.isRequired, - onRemoteFilterChange: PropTypes.func.isRequired, - // refactoring later - _: PropTypes.object.isRequired - } - - constructor(props) { - super(props); - this.state = { currFilters: {}, isDataChanged: false }; - this.onFilter = this.onFilter.bind(this); - } - - componentWillReceiveProps(nextProps) { - // consider to use lodash.isEqual - if (JSON.stringify(this.state.currFilters) !== JSON.stringify(nextProps.store.filters)) { - this.setState(() => ({ isDataChanged: true, currFilters: nextProps.store.filters })); - } else { - this.setState(() => ({ isDataChanged: false })); +export default (Base, { + _, + remoteResolver +}) => + class FilterWrapper extends remoteResolver(Component) { + static propTypes = { + store: PropTypes.object.isRequired, + columns: PropTypes.array.isRequired } - } - - onFilter(column, filterVal, filterType) { - const { store, columns, _, onRemoteFilterChange } = this.props; - const currFilters = Object.assign({}, this.state.currFilters); - const { dataField, filter } = column; - if (!_.isDefined(filterVal) || filterVal === '') { - delete currFilters[dataField]; - } else { - const { comparator = LIKE } = filter.props; - currFilters[dataField] = { filterVal, filterType, comparator }; + constructor(props) { + super(props); + this.state = { currFilters: {}, isDataChanged: false }; + this.onFilter = this.onFilter.bind(this); } - store.filters = currFilters; - if (this.isRemote() || this.isPaginationRemote()) { - onRemoteFilterChange(this.isPaginationRemote()); - // when remote filtering is enable, dont set currFilters state - // in the componentWillReceiveProps, it's the key point that we can know the filter is changed - return; + componentWillReceiveProps(nextProps) { + // consider to use lodash.isEqual + if (JSON.stringify(this.state.currFilters) !== JSON.stringify(nextProps.store.filters)) { + this.setState(() => ({ isDataChanged: true, currFilters: nextProps.store.filters })); + } else { + this.setState(() => ({ isDataChanged: false })); + } } - store.filteredData = filters(store, columns, _)(currFilters); - this.setState(() => ({ currFilters, isDataChanged: true })); - } + onFilter(column, filterVal, filterType) { + const { store, columns } = this.props; + const currFilters = Object.assign({}, this.state.currFilters); + const { dataField, filter } = column; - // refactoring later - isRemote() { - const { remote } = this.props; - return remote === true || (typeof remote === 'object' && remote.filter); - } + if (!_.isDefined(filterVal) || filterVal === '') { + delete currFilters[dataField]; + } else { + const { comparator = LIKE } = filter.props; + currFilters[dataField] = { filterVal, filterType, comparator }; + } + store.filters = currFilters; - // refactoring later - isPaginationRemote() { - const { remote } = this.props; - return remote === true || (typeof remote === 'object' && remote.pagination); - } + if (this.isRemoteFiltering() || this.isRemotePagination()) { + this.handleRemoteFilterChange(); + // when remote filtering is enable, dont set currFilters state + // in the componentWillReceiveProps, + // it's the key point that we can know the filter is changed + return; + } - render() { - return this.props.baseElement({ - ...this.props, - key: 'table', - onFilter: this.onFilter, - isDataChanged: this.state.isDataChanged - }); - } -} + store.filteredData = filters(store, columns, _)(currFilters); + this.setState(() => ({ currFilters, isDataChanged: true })); + } + + render() { + return ( + + ); + } + }; diff --git a/packages/react-bootstrap-table2-filter/test/wrapper.test.js b/packages/react-bootstrap-table2-filter/test/wrapper.test.js index 9541c8dbd..f250c462f 100644 --- a/packages/react-bootstrap-table2-filter/test/wrapper.test.js +++ b/packages/react-bootstrap-table2-filter/test/wrapper.test.js @@ -3,10 +3,11 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import _ from 'react-bootstrap-table2/src/utils'; +import remoteResolver from 'react-bootstrap-table2/src/props-resolver/remote-resolver'; import BootstrapTable from 'react-bootstrap-table2/src/bootstrap-table'; import Store from 'react-bootstrap-table2/src/store'; import filter, { textFilter } from '../src'; -import FilterWrapper from '../src/wrapper'; +import wrapperFactory from '../src/wrapper'; import { FILTER_TYPE } from '../src/const'; const data = []; @@ -21,10 +22,10 @@ for (let i = 0; i < 20; i += 1) { describe('Wrapper', () => { let wrapper; let instance; - const onRemoteFilterChangeCB = sinon.stub(); + const onTableChangeCB = sinon.stub(); afterEach(() => { - onRemoteFilterChangeCB.reset(); + onTableChangeCB.reset(); }); const createTableProps = () => { @@ -46,16 +47,19 @@ describe('Wrapper', () => { filter: filter(), _, store: new Store('id'), - onRemoteFilterChange: onRemoteFilterChangeCB + onTableChange: onTableChangeCB }; tableProps.store.data = data; return tableProps; }; - const pureTable = props => (); + const FilterWrapper = wrapperFactory(BootstrapTable, { + _, + remoteResolver + }); const createFilterWrapper = (props, renderFragment = true) => { - wrapper = shallow(); + wrapper = shallow(); instance = wrapper.instance(); if (renderFragment) { const fragment = instance.render(); @@ -177,7 +181,7 @@ describe('Wrapper', () => { }); it('should calling props.onRemoteFilterChange correctly', () => { - expect(onRemoteFilterChangeCB.calledOnce).toBeTruthy(); + expect(onTableChangeCB.calledOnce).toBeTruthy(); }); }); diff --git a/packages/react-bootstrap-table2-paginator/src/index.js b/packages/react-bootstrap-table2-paginator/src/index.js index 375f1fabc..fd468cb5e 100644 --- a/packages/react-bootstrap-table2-paginator/src/index.js +++ b/packages/react-bootstrap-table2-paginator/src/index.js @@ -1,6 +1,6 @@ -import PaginationWrapper from './wrapper'; +import wrapperFactory from './wrapper'; export default (options = {}) => ({ - PaginationWrapper, + wrapperFactory, options }); diff --git a/packages/react-bootstrap-table2-paginator/src/page.js b/packages/react-bootstrap-table2-paginator/src/page.js index 1ea54e571..aa3085aaa 100644 --- a/packages/react-bootstrap-table2-paginator/src/page.js +++ b/packages/react-bootstrap-table2-paginator/src/page.js @@ -1,12 +1,12 @@ -export const getByCurrPage = store => (page, sizePerPage, pageStartIndex) => { +export const getByCurrPage = (store, pageStartIndex) => { const dataSize = store.data.length; if (!dataSize) return []; const getNormalizedPage = () => { const offset = Math.abs(1 - pageStartIndex); - return page + offset; + return store.page + offset; }; - const end = (getNormalizedPage() * sizePerPage) - 1; - const start = end - (sizePerPage - 1); + const end = (getNormalizedPage() * store.sizePerPage) - 1; + const start = end - (store.sizePerPage - 1); const result = []; for (let i = start; i <= end; i += 1) { diff --git a/packages/react-bootstrap-table2-paginator/src/wrapper.js b/packages/react-bootstrap-table2-paginator/src/wrapper.js index e01cd516b..5393a794d 100644 --- a/packages/react-bootstrap-table2-paginator/src/wrapper.js +++ b/packages/react-bootstrap-table2-paginator/src/wrapper.js @@ -1,6 +1,4 @@ /* eslint react/prop-types: 0 */ -/* eslint arrow-body-style: 0 */ - import React, { Component } from 'react'; import PropTypes from 'prop-types'; @@ -8,172 +6,154 @@ import Const from './const'; import Pagination from './pagination'; import { getByCurrPage } from './page'; -class PaginationWrapper extends Component { - static propTypes = { - store: PropTypes.object.isRequired, - baseElement: PropTypes.func.isRequired, - onRemotePageChange: PropTypes.func.isRequired - } - - constructor(props) { - super(props); - this.handleChangePage = this.handleChangePage.bind(this); - this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this); - - let currPage; - let currSizePerPage; - const { options } = props.pagination; - const sizePerPageList = options.sizePerPageList || Const.SIZE_PER_PAGE_LIST; - - // initialize current page - if (typeof options.page !== 'undefined') { - currPage = options.page; - } else if (typeof options.pageStartIndex !== 'undefined') { - currPage = options.pageStartIndex; - } else { - currPage = Const.PAGE_START_INDEX; +export default (Base, { + remoteResolver +}) => + class PaginationWrapper extends remoteResolver(Component) { + static propTypes = { + store: PropTypes.object.isRequired } - // initialize current sizePerPage - if (typeof options.sizePerPage !== 'undefined') { - currSizePerPage = options.sizePerPage; - } else if (typeof sizePerPageList[0] === 'object') { - currSizePerPage = sizePerPageList[0].value; - } else { - currSizePerPage = sizePerPageList[0]; - } + constructor(props) { + super(props); + this.handleChangePage = this.handleChangePage.bind(this); + this.handleChangeSizePerPage = this.handleChangeSizePerPage.bind(this); + + let currPage; + let currSizePerPage; + const { options } = props.pagination; + const sizePerPageList = options.sizePerPageList || Const.SIZE_PER_PAGE_LIST; + + // initialize current page + if (typeof options.page !== 'undefined') { + currPage = options.page; + } else if (typeof options.pageStartIndex !== 'undefined') { + currPage = options.pageStartIndex; + } else { + currPage = Const.PAGE_START_INDEX; + } - this.state = { currPage, currSizePerPage }; - this.saveToStore(currPage, currSizePerPage); - } - - componentWillReceiveProps(nextProps) { - let needNewState = false; - let { currPage, currSizePerPage } = this.state; - const { page, sizePerPage, pageStartIndex, onPageChange } = nextProps.pagination.options; - - if (typeof page !== 'undefined' && currPage !== page) { // user defined page - currPage = page; - needNewState = true; - } else if (nextProps.isDataChanged) { // user didn't defined page but data change - currPage = typeof pageStartIndex !== 'undefined' ? pageStartIndex : Const.PAGE_START_INDEX; - needNewState = true; - } + // initialize current sizePerPage + if (typeof options.sizePerPage !== 'undefined') { + currSizePerPage = options.sizePerPage; + } else if (typeof sizePerPageList[0] === 'object') { + currSizePerPage = sizePerPageList[0].value; + } else { + currSizePerPage = sizePerPageList[0]; + } - if (typeof sizePerPage !== 'undefined') { - currSizePerPage = sizePerPage; - needNewState = true; + this.state = { currPage, currSizePerPage }; + this.saveToStore(currPage, currSizePerPage); } - this.saveToStore(currPage, currSizePerPage); - - if (needNewState) { - if (onPageChange) { - onPageChange(currPage, currSizePerPage); + componentWillReceiveProps(nextProps) { + let needNewState = false; + let { currPage, currSizePerPage } = this.state; + const { page, sizePerPage, pageStartIndex, onPageChange } = nextProps.pagination.options; + + if (typeof page !== 'undefined' && currPage !== page) { // user defined page + currPage = page; + needNewState = true; + } else if (nextProps.isDataChanged) { // user didn't defined page but data change + currPage = typeof pageStartIndex !== 'undefined' ? pageStartIndex : Const.PAGE_START_INDEX; + needNewState = true; } - this.setState(() => ({ currPage, currSizePerPage })); - } - } - - saveToStore(page, sizePerPage) { - this.props.store.page = page; - this.props.store.sizePerPage = sizePerPage; - } - isRemote() { - const { remote } = this.props; - return remote === true || (typeof remote === 'object' && remote.pagination); - } + if (typeof sizePerPage !== 'undefined') { + currSizePerPage = sizePerPage; + needNewState = true; + } - handleChangePage(currPage) { - const { currSizePerPage } = this.state; - const { pagination: { options }, onRemotePageChange } = this.props; - this.saveToStore(currPage, currSizePerPage); + this.saveToStore(currPage, currSizePerPage); - if (options.onPageChange) { - options.onPageChange(currPage, currSizePerPage); - } - if (this.isRemote()) { - onRemotePageChange(); - return; + if (needNewState) { + if (onPageChange) { + onPageChange(currPage, currSizePerPage); + } + this.setState(() => ({ currPage, currSizePerPage })); + } } - this.setState(() => { - return { - currPage - }; - }); - } - - handleChangeSizePerPage(currSizePerPage, currPage) { - const { pagination: { options }, onRemotePageChange } = this.props; - this.saveToStore(currPage, currSizePerPage); - - if (options.onSizePerPageChange) { - options.onSizePerPageChange(currSizePerPage, currPage); + + saveToStore(page, sizePerPage) { + this.props.store.page = page; + this.props.store.sizePerPage = sizePerPage; } - if (this.isRemote()) { - onRemotePageChange(); - return; + + handleChangePage(currPage) { + const { currSizePerPage } = this.state; + const { pagination: { options } } = this.props; + this.saveToStore(currPage, currSizePerPage); + + if (options.onPageChange) { + options.onPageChange(currPage, currSizePerPage); + } + if (this.isRemotePagination()) { + this.handleRemotePageChange(); + return; + } + this.setState(() => ({ currPage })); } - this.setState(() => { - return { + + handleChangeSizePerPage(currSizePerPage, currPage) { + const { pagination: { options } } = this.props; + this.saveToStore(currPage, currSizePerPage); + + if (options.onSizePerPageChange) { + options.onSizePerPageChange(currSizePerPage, currPage); + } + if (this.isRemotePagination()) { + this.handleRemotePageChange(); + return; + } + this.setState(() => ({ currPage, currSizePerPage - }; - }); - } - - render() { - const { pagination: { options }, store, baseElement } = this.props; - const { currPage, currSizePerPage } = this.state; - const withFirstAndLast = typeof options.withFirstAndLast === 'undefined' ? - Const.With_FIRST_AND_LAST : options.withFirstAndLast; - const alwaysShowAllBtns = typeof options.alwaysShowAllBtns === 'undefined' ? - Const.SHOW_ALL_PAGE_BTNS : options.alwaysShowAllBtns; - const hideSizePerPage = typeof options.hideSizePerPage === 'undefined' ? - Const.HIDE_SIZE_PER_PAGE : options.hideSizePerPage; - const hidePageListOnlyOnePage = typeof options.hidePageListOnlyOnePage === 'undefined' ? - Const.HIDE_PAGE_LIST_ONLY_ONE_PAGE : options.hidePageListOnlyOnePage; - const pageStartIndex = typeof options.pageStartIndex === 'undefined' ? - Const.PAGE_START_INDEX : options.pageStartIndex; - - const data = this.isRemote() ? - this.props.data : - getByCurrPage(store)(currPage, currSizePerPage, pageStartIndex); - - const base = baseElement({ - ...this.props, - key: 'table', - data - }); - - return [ - base, - - ]; - } -} - -export default PaginationWrapper; + })); + } + + render() { + const { pagination: { options }, store } = this.props; + const { currPage, currSizePerPage } = this.state; + const withFirstAndLast = typeof options.withFirstAndLast === 'undefined' ? + Const.With_FIRST_AND_LAST : options.withFirstAndLast; + const alwaysShowAllBtns = typeof options.alwaysShowAllBtns === 'undefined' ? + Const.SHOW_ALL_PAGE_BTNS : options.alwaysShowAllBtns; + const hideSizePerPage = typeof options.hideSizePerPage === 'undefined' ? + Const.HIDE_SIZE_PER_PAGE : options.hideSizePerPage; + const hidePageListOnlyOnePage = typeof options.hidePageListOnlyOnePage === 'undefined' ? + Const.HIDE_PAGE_LIST_ONLY_ONE_PAGE : options.hidePageListOnlyOnePage; + const pageStartIndex = typeof options.pageStartIndex === 'undefined' ? + Const.PAGE_START_INDEX : options.pageStartIndex; + + const data = this.isRemotePagination() ? + this.props.data : + getByCurrPage(store, pageStartIndex); + + return [ + , + + ]; + } + }; diff --git a/packages/react-bootstrap-table2-paginator/test/page.test.js b/packages/react-bootstrap-table2-paginator/test/page.test.js index f073c865d..e4ef53ab2 100644 --- a/packages/react-bootstrap-table2-paginator/test/page.test.js +++ b/packages/react-bootstrap-table2-paginator/test/page.test.js @@ -29,7 +29,9 @@ describe('Page Functions', () => { it('should always return correct data', () => { params.forEach(([page, sizePerPage, pageStartIndex]) => { - const rows = getByCurrPage(store)(page, sizePerPage, pageStartIndex); + store.page = page; + store.sizePerPage = sizePerPage; + const rows = getByCurrPage(store, pageStartIndex); expect(rows).toBeDefined(); expect(Array.isArray(rows)).toBeTruthy(); expect(rows.every(row => !!row)).toBeTruthy(); @@ -39,7 +41,9 @@ describe('Page Functions', () => { it('should return empty array when store.data is empty', () => { store.data = []; params.forEach(([page, sizePerPage, pageStartIndex]) => { - const rows = getByCurrPage(store)(page, sizePerPage, pageStartIndex); + store.page = page; + store.sizePerPage = sizePerPage; + const rows = getByCurrPage(store, pageStartIndex); expect(rows).toHaveLength(0); }); }); diff --git a/packages/react-bootstrap-table2-paginator/test/wrapper.test.js b/packages/react-bootstrap-table2-paginator/test/wrapper.test.js index 947971da0..661f372f2 100644 --- a/packages/react-bootstrap-table2-paginator/test/wrapper.test.js +++ b/packages/react-bootstrap-table2-paginator/test/wrapper.test.js @@ -4,9 +4,10 @@ import { shallow } from 'enzyme'; import BootstrapTable from 'react-bootstrap-table2/src/bootstrap-table'; +import remoteResolver from 'react-bootstrap-table2/src/props-resolver/remote-resolver'; import Store from 'react-bootstrap-table2/src/store'; import paginator from '../src'; -import PaginationWrapper from '../src/wrapper'; +import wrapperFactory from '../src/wrapper'; import Pagination from '../src/pagination'; import Const from '../src/const'; @@ -21,10 +22,10 @@ for (let i = 0; i < 100; i += 1) { describe('Wrapper', () => { let wrapper; let instance; - const onRemotePageChangeCB = sinon.stub(); + const onTableChangeCB = sinon.stub(); afterEach(() => { - onRemotePageChangeCB.reset(); + onTableChangeCB.reset(); }); const createTableProps = (props = {}) => { @@ -40,16 +41,18 @@ describe('Wrapper', () => { data, pagination: paginator(props.options), store: new Store('id'), - onRemotePageChange: onRemotePageChangeCB + onTableChange: onTableChangeCB }; tableProps.store.data = data; return tableProps; }; - const pureTable = props => (); + const PaginationWrapper = wrapperFactory(BootstrapTable, { + remoteResolver + }); const createPaginationWrapper = (props, renderFragment = true) => { - wrapper = shallow(); + wrapper = shallow(); instance = wrapper.instance(); if (renderFragment) { const fragment = instance.render(); @@ -504,7 +507,7 @@ describe('Wrapper', () => { beforeEach(() => { props.remote = true; createPaginationWrapper(props, false); - onRemotePageChangeCB.reset(); + onTableChangeCB.reset(); instance.handleChangePage(newPage); }); @@ -513,7 +516,7 @@ describe('Wrapper', () => { }); it('should calling props.onRemotePageChange correctly', () => { - expect(onRemotePageChangeCB.calledOnce).toBeTruthy(); + expect(onTableChangeCB.calledOnce).toBeTruthy(); }); }); }); @@ -551,7 +554,7 @@ describe('Wrapper', () => { beforeEach(() => { props.remote = true; createPaginationWrapper(props, false); - onRemotePageChangeCB.reset(); + onTableChangeCB.reset(); instance.handleChangeSizePerPage(newSizePerPage, newPage); }); @@ -561,68 +564,7 @@ describe('Wrapper', () => { }); it('should calling props.onRemotePageChange correctly', () => { - expect(onRemotePageChangeCB.calledOnce).toBeTruthy(); - }); - }); - }); - - describe('isRemote', () => { - let result; - describe('when options.remote is true', () => { - const props = createTableProps(); - props.remote = true; - - beforeEach(() => { - createPaginationWrapper(props, false); - result = instance.isRemote(); - }); - - it('should return true', () => { - expect(result).toBeTruthy(); - }); - }); - - describe('when options.remote is false', () => { - const props = createTableProps(); - props.remote = false; - - beforeEach(() => { - createPaginationWrapper(props, false); - result = instance.isRemote(); - }); - - it('should return false', () => { - expect(result).toBeFalsy(); - }); - }); - - describe('when options.remote.pagination is defined as true', () => { - const props = createTableProps(); - props.remote = {}; - props.remote.pagination = true; - - beforeEach(() => { - createPaginationWrapper(props, false); - result = instance.isRemote(); - }); - - it('should return true', () => { - expect(result).toBeTruthy(); - }); - }); - - describe('when options.remote.pagination is defined as false', () => { - const props = createTableProps(); - props.remote = {}; - props.remote.pagination = false; - - beforeEach(() => { - createPaginationWrapper(props, false); - result = instance.isRemote(); - }); - - it('should return false', () => { - expect(result).toBeFalsy(); + expect(onTableChangeCB.calledOnce).toBeTruthy(); }); }); }); diff --git a/packages/react-bootstrap-table2/src/cell-edit/wrapper.js b/packages/react-bootstrap-table2/src/cell-edit/wrapper.js index 60e313487..ec62fd23d 100644 --- a/packages/react-bootstrap-table2/src/cell-edit/wrapper.js +++ b/packages/react-bootstrap-table2/src/cell-edit/wrapper.js @@ -1,112 +1,96 @@ -/* eslint arrow-body-style: 0 */ /* eslint react/prop-types: 0 */ -import { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import _ from '../utils'; -import { cellEditElement } from '../table-factory'; - -class CellEditWrapper extends Component { - constructor(props) { - super(props); - this.startEditing = this.startEditing.bind(this); - this.escapeEditing = this.escapeEditing.bind(this); - this.completeEditing = this.completeEditing.bind(this); - this.handleCellUpdate = this.handleCellUpdate.bind(this); - this.updateEditingWithErr = this.updateEditingWithErr.bind(this); - this.state = { - ridx: null, - cidx: null, - message: null, - editing: false - }; - } +export default (Base, parentProps) => + class CellEditWrapper extends Component { + constructor(props) { + super(props); + this.startEditing = this.startEditing.bind(this); + this.escapeEditing = this.escapeEditing.bind(this); + this.completeEditing = this.completeEditing.bind(this); + this.handleCellUpdate = this.handleCellUpdate.bind(this); + this.updateEditingWithErr = this.updateEditingWithErr.bind(this); + this.state = { + ridx: null, + cidx: null, + message: null, + editing: false + }; + } - componentWillReceiveProps(nextProps) { - if (nextProps.cellEdit) { - if (nextProps.cellEdit.editing) { - this.setState(() => { - return { + componentWillReceiveProps(nextProps) { + if (nextProps.cellEdit) { + if (nextProps.cellEdit.editing) { + this.setState(() => ({ ...this.state, message: nextProps.cellEdit.errorMessage - }; - }); - } else { - this.escapeEditing(); + })); + } else { + this.escapeEditing(); + } } } - } - handleCellUpdate(row, column, newValue) { - const { keyField, cellEdit, onUpdateCell } = this.props; - const { beforeSaveCell, afterSaveCell } = cellEdit; - const oldValue = _.get(row, column.dataField); - const rowId = _.get(row, keyField); - if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column); - if (onUpdateCell(rowId, column.dataField, newValue)) { - if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column); - this.completeEditing(); + handleCellUpdate(row, column, newValue) { + const { keyField, cellEdit } = this.props; + const { beforeSaveCell, afterSaveCell } = cellEdit; + const oldValue = _.get(row, column.dataField); + const rowId = _.get(row, keyField); + if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column); + if (parentProps.onUpdateCell(rowId, column.dataField, newValue)) { + if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column); + this.completeEditing(); + } } - } - completeEditing() { - this.setState(() => { - return { + completeEditing() { + this.setState(() => ({ ridx: null, cidx: null, message: null, editing: false - }; - }); - } + })); + } - startEditing(ridx, cidx) { - const editing = () => { - this.setState(() => { - return { + startEditing(ridx, cidx) { + const editing = () => { + this.setState(() => ({ ridx, cidx, editing: true - }; - }); - }; + })); + }; - const { selectRow } = this.props; - if (!selectRow || (selectRow.clickToEdit || !selectRow.clickToSelect)) editing(); - } + const { selectRow } = this.props; + if (!selectRow || (selectRow.clickToEdit || !selectRow.clickToSelect)) editing(); + } - escapeEditing() { - this.setState(() => { - return { + escapeEditing() { + this.setState(() => ({ ridx: null, cidx: null, editing: false - }; - }); - } + })); + } - updateEditingWithErr(message) { - this.setState(() => { - return { + updateEditingWithErr(message) { + this.setState(() => ({ ...this.state, message - }; - }); - } - - render() { - return cellEditElement({ - ...this.props, - onCellUpdate: this.handleCellUpdate, - onStartEditing: this.startEditing, - onEscapeEditing: this.escapeEditing, - currEditCell: { ...this.state } - }); - } -} - -CellEditWrapper.propTypes = { - onUpdateCell: PropTypes.func.isRequired -}; + })); + } -export default CellEditWrapper; + render() { + return ( + + ); + } + }; diff --git a/packages/react-bootstrap-table2/src/container.js b/packages/react-bootstrap-table2/src/container.js index 4ba44d366..04fb9080b 100644 --- a/packages/react-bootstrap-table2/src/container.js +++ b/packages/react-bootstrap-table2/src/container.js @@ -2,14 +2,9 @@ /* eslint react/prop-types: 0 */ import React, { Component } from 'react'; import Store from './store'; - -import { - wrapWithCellEdit, - wrapWithSelection, - wrapWithFilter, - wrapWithSort, - wrapWithPagination -} from './table-factory'; +import withSort from './sort/wrapper'; +import withCellEdit from './cell-edit/wrapper'; +import withSelection from './row-selection/wrapper'; import remoteResolver from './props-resolver/remote-resolver'; import _ from './utils'; @@ -20,6 +15,7 @@ const withDataStore = Base => super(props); this.store = new Store(props.keyField); this.store.data = props.data; + this.wrapComponents(); this.handleUpdateCell = this.handleUpdateCell.bind(this); } @@ -27,6 +23,40 @@ const withDataStore = Base => this.store.data = nextProps.data; } + wrapComponents() { + this.BaseComponent = Base; + const { pagination, columns, filter, selectRow, cellEdit } = this.props; + if (pagination) { + const { wrapperFactory } = pagination; + this.BaseComponent = wrapperFactory(this.BaseComponent, { + remoteResolver + }); + } + + if (columns.filter(col => col.sort).length > 0) { + this.BaseComponent = withSort(this.BaseComponent); + } + + if (filter) { + const { wrapperFactory } = filter; + this.BaseComponent = wrapperFactory(this.BaseComponent, { + _, + remoteResolver + }); + } + + if (selectRow) { + this.BaseComponent = withSelection(this.BaseComponent); + } + + if (cellEdit) { + this.BaseComponent = withCellEdit(this.BaseComponent, { + ref: node => this.cellEditWrapper = node, + onUpdateCell: this.handleUpdateCell + }); + } + } + handleUpdateCell(rowId, dataField, newValue) { const { cellEdit } = this.props; // handle cell editing internal @@ -58,30 +88,9 @@ const withDataStore = Base => store: this.store }; - if (this.props.cellEdit) { - return wrapWithCellEdit({ - ref: node => this.cellEditWrapper = node, - onUpdateCell: this.handleUpdateCell, - ...baseProps - }); - } else if (this.props.selectRow) { - return wrapWithSelection(baseProps); - } else if (this.props.filter) { - return wrapWithFilter({ - ...baseProps, - onRemoteFilterChange: this.handleRemoteFilterChange, - onRemotePageChange: this.handleRemotePageChange - }); - } else if (this.props.columns.filter(col => col.sort).length > 0) { - return wrapWithSort(baseProps); - } else if (this.props.pagination) { - return wrapWithPagination({ - ...baseProps, - onRemotePageChange: this.handleRemotePageChange - }); - } - - return React.createElement(Base, baseProps); + return ( + + ); } }; diff --git a/packages/react-bootstrap-table2/src/row-selection/wrapper.js b/packages/react-bootstrap-table2/src/row-selection/wrapper.js index 3a73e9582..442cb18cb 100644 --- a/packages/react-bootstrap-table2/src/row-selection/wrapper.js +++ b/packages/react-bootstrap-table2/src/row-selection/wrapper.js @@ -1,8 +1,5 @@ -/* eslint arrow-body-style: 0 */ -/* eslint react/prop-types: 0 */ -import { Component } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { selectionElement } from '../table-factory'; import Const from '../const'; import { @@ -13,88 +10,90 @@ import { } from '../store/selection'; import { getRowByRowId } from '../store/rows'; -class RowSelectionWrapper extends Component { - constructor(props) { - super(props); - this.handleRowSelect = this.handleRowSelect.bind(this); - this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this); - this.state = { - selectedRowKeys: props.store.selected - }; - } - - /** - * row selection handler - * @param {String} rowKey - row key of what was selected. - * @param {Boolean} checked - next checked status of input button. - */ - handleRowSelect(rowKey, checked, rowIndex) { - const { selectRow: { mode, onSelect }, store } = this.props; - const { ROW_SELECT_SINGLE } = Const; - - let currSelected = [...store.selected]; - - if (mode === ROW_SELECT_SINGLE) { // when select mode is radio - currSelected = [rowKey]; - } else if (checked) { // when select mode is checkbox - currSelected.push(rowKey); - } else { - currSelected = currSelected.filter(value => value !== rowKey); +export default Base => + class RowSelectionWrapper extends Component { + static propTypes = { + store: PropTypes.object.isRequired, + selectRow: PropTypes.object.isRequired } - store.selected = currSelected; + constructor(props) { + super(props); + this.handleRowSelect = this.handleRowSelect.bind(this); + this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this); + this.state = { + selectedRowKeys: props.store.selected + }; + } - if (onSelect) { - const row = getRowByRowId(store)(rowKey); - onSelect(row, checked, rowIndex); + /** + * row selection handler + * @param {String} rowKey - row key of what was selected. + * @param {Boolean} checked - next checked status of input button. + */ + handleRowSelect(rowKey, checked, rowIndex) { + const { selectRow: { mode, onSelect }, store } = this.props; + const { ROW_SELECT_SINGLE } = Const; + + let currSelected = [...store.selected]; + + if (mode === ROW_SELECT_SINGLE) { // when select mode is radio + currSelected = [rowKey]; + } else if (checked) { // when select mode is checkbox + currSelected.push(rowKey); + } else { + currSelected = currSelected.filter(value => value !== rowKey); + } + + store.selected = currSelected; + + if (onSelect) { + const row = getRowByRowId(store)(rowKey); + onSelect(row, checked, rowIndex); + } + + this.setState(() => ({ + selectedRowKeys: currSelected + })); } - this.setState(() => ({ - selectedRowKeys: currSelected - })); - } + /** + * handle all rows selection on header cell by store.selected or given specific result. + * @param {Boolean} option - customized result for all rows selection + */ + handleAllRowsSelect(option) { + const { store, selectRow: { + onSelectAll, + nonSelectable + } } = this.props; + const selected = isAnySelectedRow(store)(nonSelectable); - /** - * handle all rows selection on header cell by store.selected or given specific result. - * @param {Boolean} option - customized result for all rows selection - */ - handleAllRowsSelect(option) { - const { store, selectRow: { - onSelectAll, - nonSelectable - } } = this.props; - const selected = isAnySelectedRow(store)(nonSelectable); + // set next status of all row selected by store.selected or customizing by user. + const result = option || !selected; - // set next status of all row selected by store.selected or customizing by user. - const result = option || !selected; + const currSelected = result ? + selectableKeys(store)(nonSelectable) : + unSelectableKeys(store)(nonSelectable); - const currSelected = result ? - selectableKeys(store)(nonSelectable) : - unSelectableKeys(store)(nonSelectable); + store.selected = currSelected; - store.selected = currSelected; + if (onSelectAll) { + onSelectAll(result, getSelectedRows(store)); + } - if (onSelectAll) { - onSelectAll(result, getSelectedRows(store)); + this.setState(() => ({ + selectedRowKeys: currSelected + })); } - this.setState(() => ({ - selectedRowKeys: currSelected - })); - } - - render() { - return selectionElement({ - ...this.props, - onRowSelect: this.handleRowSelect, - onAllRowsSelect: this.handleAllRowsSelect - }); - } -} - -RowSelectionWrapper.propTypes = { - store: PropTypes.object.isRequired -}; - -export default RowSelectionWrapper; + render() { + return ( + + ); + } + }; diff --git a/packages/react-bootstrap-table2/src/sort/wrapper.js b/packages/react-bootstrap-table2/src/sort/wrapper.js index 82f00b208..40486fb47 100644 --- a/packages/react-bootstrap-table2/src/sort/wrapper.js +++ b/packages/react-bootstrap-table2/src/sort/wrapper.js @@ -1,60 +1,55 @@ -/* eslint arrow-body-style: 0 */ /* eslint react/prop-types: 0 */ -/* eslint no-return-assign: 0 */ -import { Component } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { sortableElement } from '../table-factory'; +export default Base => + class SortWrapper extends Component { + static propTypes = { + store: PropTypes.object.isRequired + } -class SortWrapper extends Component { - constructor(props) { - super(props); - this.handleSort = this.handleSort.bind(this); - } + constructor(props) { + super(props); + this.handleSort = this.handleSort.bind(this); + } - componentWillMount() { - const { columns, defaultSorted, store } = this.props; - // defaultSorted is an array, it's ready to use as multi / single sort - // when we start to support multi sort, please update following code to use array.forEach - if (defaultSorted && defaultSorted.length > 0) { - const dataField = defaultSorted[0].dataField; - const order = defaultSorted[0].order; - const column = columns.filter(col => col.dataField === dataField); - if (column.length > 0) { - store.sortBy(column[0], order); + componentWillMount() { + const { columns, defaultSorted, store } = this.props; + // defaultSorted is an array, it's ready to use as multi / single sort + // when we start to support multi sort, please update following code to use array.forEach + if (defaultSorted && defaultSorted.length > 0) { + const dataField = defaultSorted[0].dataField; + const order = defaultSorted[0].order; + const column = columns.filter(col => col.dataField === dataField); + if (column.length > 0) { + store.sortBy(column[0], order); + } } } - } - componentWillReceiveProps(nextProps) { - if (nextProps.isDataChanged) { - const sortedColumn = nextProps.columns.find( - column => column.dataField === nextProps.store.sortField); - if (sortedColumn) { - nextProps.store.sortBy(sortedColumn, nextProps.store.sortOrder); + componentWillReceiveProps(nextProps) { + if (nextProps.isDataChanged) { + const sortedColumn = nextProps.columns.find( + column => column.dataField === nextProps.store.sortField); + if (sortedColumn) { + nextProps.store.sortBy(sortedColumn, nextProps.store.sortOrder); + } } } - } - - handleSort(column) { - const { store } = this.props; - store.sortBy(column); - - this.table.setState({ data: store.data }); - } - render() { - return sortableElement({ - ...this.props, - ref: node => this.table = node, - onSort: this.handleSort, - data: this.props.store.data - }); - } -} - -SortWrapper.propTypes = { - store: PropTypes.object.isRequired -}; + handleSort(column) { + const { store } = this.props; + store.sortBy(column); + this.forceUpdate(); + } -export default SortWrapper; + render() { + return ( + + ); + } + }; diff --git a/packages/react-bootstrap-table2/src/table-factory.js b/packages/react-bootstrap-table2/src/table-factory.js deleted file mode 100644 index 10ff86ee9..000000000 --- a/packages/react-bootstrap-table2/src/table-factory.js +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint react/prop-types: 0 */ -import React from 'react'; - -import _ from './utils'; -import BootstrapTable from './bootstrap-table'; -import SortWrapper from './sort/wrapper'; -import RowSelectionWrapper from './row-selection/wrapper'; -import CellEditWrapper from './cell-edit/wrapper'; - - -export const wrapWithCellEdit = props => - React.createElement(CellEditWrapper, { ...props }); - -export const wrapWithSelection = props => - React.createElement(RowSelectionWrapper, { ...props }); - -export const wrapWithSort = props => - React.createElement(SortWrapper, { ...props }); - -export const pureTable = props => - React.createElement(BootstrapTable, { ...props }); - -export const wrapWithFilter = (props) => { - if (props.filter) { - const { FilterWrapper } = props.filter; - return React.createElement(FilterWrapper, { ...props, baseElement: wrapWithSort, _ }); - } - return wrapWithSort(props); -}; - -export const wrapWithPagination = (props) => { - if (props.pagination) { - const { PaginationWrapper } = props.pagination; - return React.createElement(PaginationWrapper, { ...props, baseElement: pureTable }); - } - return pureTable(props); -}; - -export const sortableElement = props => wrapWithPagination(props); - -export const selectionElement = props => wrapWithFilter(props); - -export const cellEditElement = props => wrapWithSelection(props); diff --git a/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js b/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js index c8065b611..8f21f5fc9 100644 --- a/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js +++ b/packages/react-bootstrap-table2/test/cell-edit/wrapper.test.js @@ -3,8 +3,9 @@ import sinon from 'sinon'; import { shallow } from 'enzyme'; import Store from '../../src/store'; +import Container from '../../src'; import BootstrapTable from '../../src/bootstrap-table'; -import CellEditWrapper from '../../src/cell-edit/wrapper'; +import wrapperFactory from '../../src/cell-edit/wrapper'; describe('CellEditWrapper', () => { let wrapper; @@ -30,10 +31,14 @@ describe('CellEditWrapper', () => { }; const keyField = 'id'; - + let onUpdateCellCB = sinon.stub(); const store = new Store(keyField); store.data = data; + const CellEditWrapper = wrapperFactory(Container, { + onUpdateCell: onUpdateCellCB + }); + beforeEach(() => { wrapper = shallow( { columns={ columns } cellEdit={ cellEdit } store={ store } - onUpdateCell={ sinon.stub() } /> ); }); @@ -82,7 +86,6 @@ describe('CellEditWrapper', () => { columns={ columns } cellEdit={ cellEdit } store={ store } - onUpdateCell={ sinon.stub() } /> ); wrapper.setProps({ cellEdit: { ...cellEdit, editing: false } }); @@ -113,7 +116,6 @@ describe('CellEditWrapper', () => { columns={ columns } cellEdit={ cellEdit } store={ store } - onUpdateCell={ sinon.stub() } /> ); wrapper.setState({ ridx, cidx, editing: true }); @@ -167,7 +169,6 @@ describe('CellEditWrapper', () => { cellEdit={ cellEdit } selectRow={ selectRow } store={ store } - onUpdateCell={ sinon.stub() } /> ); }); @@ -190,7 +191,6 @@ describe('CellEditWrapper', () => { cellEdit={ cellEdit } selectRow={ selectRow } store={ store } - onUpdateCell={ sinon.stub() } /> ); }); @@ -215,13 +215,11 @@ describe('CellEditWrapper', () => { }); describe('call handleCellUpdate function', () => { - let onUpdateCellCallBack; const row = data[0]; const column = columns[1]; const newValue = 'new name'; beforeEach(() => { - onUpdateCellCallBack = sinon.stub().returns(true); wrapper = shallow( { columns={ columns } cellEdit={ cellEdit } store={ store } - onUpdateCell={ onUpdateCellCallBack } /> ); wrapper.instance().handleCellUpdate(row, column, newValue); }); - afterEach(() => { onUpdateCellCallBack.reset(); }); - it('should calling onUpdateCell callback correctly', () => { - expect(onUpdateCellCallBack.callCount).toBe(1); - expect(onUpdateCellCallBack.calledWith(row.id, column.dataField, newValue)).toBe(true); + expect(onUpdateCellCB.callCount).toBe(1); + expect(onUpdateCellCB.calledWith(row.id, column.dataField, newValue)).toBe(true); }); describe('when onUpdateCell function return true', () => { @@ -260,7 +255,6 @@ describe('CellEditWrapper', () => { columns={ columns } cellEdit={ cellEdit } store={ store } - onUpdateCell={ onUpdateCellCallBack } /> ); wrapper.instance().handleCellUpdate(row, column, newValue); @@ -279,7 +273,7 @@ describe('CellEditWrapper', () => { const spy = jest.spyOn(CellEditWrapper.prototype, 'completeEditing'); beforeEach(() => { - onUpdateCellCallBack = sinon.stub().returns(false); + onUpdateCellCB = sinon.stub().returns(false); wrapper = shallow( { columns={ columns } cellEdit={ cellEdit } store={ store } - onUpdateCell={ onUpdateCellCallBack } /> ); wrapper.instance().handleCellUpdate(row, column, newValue); @@ -309,7 +302,6 @@ describe('CellEditWrapper', () => { columns={ columns } cellEdit={ cellEdit } store={ store } - onUpdateCell={ onUpdateCellCallBack } /> ); wrapper.instance().handleCellUpdate(row, column, newValue); diff --git a/packages/react-bootstrap-table2/test/container.test.js b/packages/react-bootstrap-table2/test/container.test.js index fd4068ad8..42c67bf7b 100644 --- a/packages/react-bootstrap-table2/test/container.test.js +++ b/packages/react-bootstrap-table2/test/container.test.js @@ -1,14 +1,14 @@ +/* eslint react/prefer-stateless-function: 0 */ +/* eslint react/no-multi-comp: 0 */ import React from 'react'; import sinon from 'sinon'; import { shallow } from 'enzyme'; -import BootstrapTable from '../src'; -import SortWrapper from '../src/sort/wrapper'; -import CellEditWrapper from '../src/cell-edit/wrapper'; -import RowSelectionWrapper from '../src/row-selection/wrapper'; +import BootstrapTable from '../src/bootstrap-table'; +import Container from '../src'; import { getRowByRowId } from '../src/store/rows'; -describe('withDataStore', () => { +describe('container', () => { let wrapper; const keyField = 'id'; @@ -32,12 +32,16 @@ describe('withDataStore', () => { describe('initialization', () => { beforeEach(() => { wrapper = shallow( - + ); }); + it('should initialize BaseComponent', () => { + expect(wrapper.instance().BaseComponent.name).toBe('BootstrapTable'); + }); + it('should render BootstrapTable successfully', () => { - expect(wrapper.length).toBe(1); + expect(wrapper.find(BootstrapTable)).toHaveLength(1); }); it('should creating store successfully', () => { @@ -55,7 +59,7 @@ describe('withDataStore', () => { beforeEach(() => { wrapper = shallow( - { ); }); + it('should initialize BaseComponent correctly', () => { + expect(wrapper.instance().BaseComponent.name).toBe('CellEditWrapper'); + }); + it('should render CellEditWrapper component successfully', () => { - const component = wrapper.find(CellEditWrapper); - expect(component.length).toBe(1); + expect(wrapper.find('CellEditWrapper')).toHaveLength(1); }); - it('should injecting correct props to CellEditWrapper', () => { - const component = wrapper.find(CellEditWrapper); - expect(component.props().onUpdateCell).toBeDefined(); + it('should render BootstrapTable component successfully', () => { + expect(wrapper.dive().find(BootstrapTable)).toHaveLength(1); }); describe('for handleUpdateCell function', () => { @@ -100,7 +106,7 @@ describe('withDataStore', () => { beforeEach(() => { cellEdit.onUpdate = sinon.stub().returns(false); wrapper = shallow( - { beforeEach(() => { wrapper = shallow( - { ); }); + it('should initialize BaseComponent correctly', () => { + expect(wrapper.instance().BaseComponent.name).toBe('RowSelectionWrapper'); + }); + + it('should render BootstrapTable component successfully', () => { + expect(wrapper.dive().find(BootstrapTable)).toHaveLength(1); + }); + it('should render RowSelectionWrapper component successfully', () => { - expect(wrapper.find(RowSelectionWrapper).length).toBe(1); + expect(wrapper.find('RowSelectionWrapper').length).toBe(1); }); }); describe('when pagination prop is defined', () => { - const PaginationWrapper = () =>
test
; + const wrapperFactory = Base => class PaginationWrapper extends React.Component { + render() { return ; } + }; const pagination = { - PaginationWrapper + wrapperFactory }; beforeEach(() => { wrapper = shallow( - { ); }); - it('should render Pagination wrapper successfully', () => { - expect(wrapper.find(PaginationWrapper).length).toBe(1); + it('should initialize BaseComponent correctly', () => { + expect(wrapper.instance().BaseComponent.name).toBe('PaginationWrapper'); }); - it('should injecting correct props to Pagination wrapper', () => { - const component = wrapper.find(PaginationWrapper); - expect(component.props().onRemotePageChange).toBeDefined(); - expect(component.props().baseElement).toBeDefined(); - }); - }); - - describe('when any column.sort is defined', () => { - beforeEach(() => { - const columnsWithSort = [{ - dataField: keyField, - text: 'ID', - sort: true - }]; - wrapper = shallow( - - ); + it('should render BootstrapTable component successfully', () => { + expect(wrapper.dive().find(BootstrapTable)).toHaveLength(1); }); - it('should render SortWrapper component successfully', () => { - expect(wrapper.find(SortWrapper).length).toBe(1); + it('should render PaginationWrapper component successfully', () => { + expect(wrapper.find('PaginationWrapper').length).toBe(1); }); }); - describe('handleRemotePageChange', () => { - const onTableChangeCallBack = sinon.stub(); + describe('when filter prop is defined', () => { + const wrapperFactory = Base => class FilterWrapper extends React.Component { + render() { return ; } + }; + + const filter = { wrapperFactory }; beforeEach(() => { wrapper = shallow( - ); - wrapper.instance().handleRemotePageChange(); }); - it('should calling onTableChange correctly', () => { - expect(onTableChangeCallBack.calledOnce).toBeTruthy(); - expect(onTableChangeCallBack.calledWith('pagination', wrapper.instance().getNewestState())).toBeTruthy(); + it('should initialize BaseComponent correctly', () => { + expect(wrapper.instance().BaseComponent.name).toBe('FilterWrapper'); + }); + + it('should render BootstrapTable component successfully', () => { + expect(wrapper.dive().find(BootstrapTable)).toHaveLength(1); }); - }); - describe('handleRemoteFilterChange', () => { - const onTableChangeCallBack = sinon.stub(); + it('should render FilterWrapper component successfully', () => { + expect(wrapper.find('FilterWrapper').length).toBe(1); + }); + }); + describe('when any column.sort is defined', () => { beforeEach(() => { - onTableChangeCallBack.reset(); + const columnsWithSort = [{ + dataField: keyField, + text: 'ID', + sort: true + }]; wrapper = shallow( - ); }); - describe('when isRemotePagination argument is false', () => { - beforeEach(() => { - wrapper.instance().handleRemoteFilterChange(false); - }); - - it('should calling onTableChange correctly', () => { - expect(onTableChangeCallBack.calledOnce).toBeTruthy(); - expect(onTableChangeCallBack.calledWith('filter', wrapper.instance().getNewestState())).toBeTruthy(); - }); + it('should initialize BaseComponent correctly', () => { + expect(wrapper.instance().BaseComponent.name).toBe('SortWrapper'); }); - describe('when isRemotePagination argument is false', () => { - describe('and pagination.options.pageStartIndex is defined', () => { - const options = { pageStartIndex: 0 }; - beforeEach(() => { - wrapper = shallow( - {} } } - onTableChange={ onTableChangeCallBack } - /> - ); - wrapper.instance().handleRemoteFilterChange(true); - }); - - it('should calling onTableChange correctly', () => { - expect(onTableChangeCallBack.calledOnce).toBeTruthy(); - const newState = wrapper.instance().getNewestState(); - newState.page = options.pageStartIndex; - expect(onTableChangeCallBack.calledWith('filter', newState)).toBeTruthy(); - }); - }); - - describe('and pagination.options.pageStartIndex is not defined', () => { - beforeEach(() => { - wrapper = shallow( - {} } } - onTableChange={ onTableChangeCallBack } - /> - ); - wrapper.instance().handleRemoteFilterChange(true); - }); + it('should render BootstrapTable component successfully', () => { + expect(wrapper.dive().find(BootstrapTable)).toHaveLength(1); + }); - it('should calling onTableChange correctly', () => { - expect(onTableChangeCallBack.calledOnce).toBeTruthy(); - const newState = wrapper.instance().getNewestState(); - newState.page = 1; - expect(onTableChangeCallBack.calledWith('filter', newState)).toBeTruthy(); - }); - }); + it('should render SortWrapper component successfully', () => { + expect(wrapper.find('SortWrapper').length).toBe(1); }); }); }); diff --git a/packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js b/packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js new file mode 100644 index 000000000..fa7d8b305 --- /dev/null +++ b/packages/react-bootstrap-table2/test/props-resolver/remote-resolver.test.js @@ -0,0 +1,180 @@ +/* eslint react/prefer-stateless-function: 0 */ +import React from 'react'; +import sinon from 'sinon'; +import { shallow } from 'enzyme'; + +import Container from '../../src/'; +// import remoteResolver from '../../src/props-resolver/remote-resolver'; + +describe('remoteResolver', () => { + let wrapper; + + const keyField = 'id'; + + const columns = [{ + dataField: keyField, + text: 'ID' + }, { + dataField: 'name', + text: 'Name' + }]; + + const data = [{ + id: 1, + name: 'A' + }, { + id: 2, + name: 'B' + }]; + + const shallowContainer = (props) => { + wrapper = shallow( + + ); + }; + + describe('isRemotePagination', () => { + describe('when remote is false', () => { + beforeEach(() => { + shallowContainer(); + }); + + it('should return false', () => { + expect(wrapper.instance().isRemotePagination()).toBeFalsy(); + }); + }); + + describe('when remote is true', () => { + beforeEach(() => { + shallowContainer({ remote: true }); + }); + + it('should return true', () => { + expect(wrapper.instance().isRemotePagination()).toBeTruthy(); + }); + }); + + describe('when remote.pagination is true', () => { + beforeEach(() => { + shallowContainer({ remote: { pagination: true } }); + }); + + it('should return true', () => { + expect(wrapper.instance().isRemotePagination()).toBeTruthy(); + }); + }); + }); + + describe('isRemoteFiltering', () => { + describe('when remote is false', () => { + beforeEach(() => { + shallowContainer(); + }); + + it('should return false', () => { + expect(wrapper.instance().isRemoteFiltering()).toBeFalsy(); + }); + }); + + describe('when remote is true', () => { + beforeEach(() => { + shallowContainer({ remote: true }); + }); + + it('should return true', () => { + expect(wrapper.instance().isRemoteFiltering()).toBeTruthy(); + }); + }); + + describe('when remote.filter is true', () => { + beforeEach(() => { + shallowContainer({ remote: { filter: true } }); + }); + + it('should return true', () => { + expect(wrapper.instance().isRemoteFiltering()).toBeTruthy(); + }); + }); + }); + + describe('handleRemotePageChange', () => { + const onTableChangeCB = sinon.stub(); + beforeEach(() => { + onTableChangeCB.reset(); + shallowContainer({ onTableChange: onTableChangeCB }); + wrapper.instance().handleRemotePageChange(); + }); + + it('should calling props.onTableChange correctly', () => { + expect(onTableChangeCB.calledOnce).toBeTruthy(); + expect(onTableChangeCB.calledWith('pagination', wrapper.instance().getNewestState())).toBeTruthy(); + }); + }); + + describe('handleRemoteFilterChange', () => { + const onTableChangeCB = sinon.stub(); + + beforeEach(() => { + onTableChangeCB.reset(); + shallowContainer({ onTableChange: onTableChangeCB }); + }); + + describe('when remote pagination is disabled', () => { + it('should calling props.onTableChange correctly', () => { + wrapper.instance().handleRemoteFilterChange(); + expect(onTableChangeCB.calledOnce).toBeTruthy(); + expect(onTableChangeCB.calledWith('filter', wrapper.instance().getNewestState())).toBeTruthy(); + }); + }); + + describe('when remote pagination is enabled', () => { + const wrapperFactory = Base => class FilterWrapper extends React.Component { + render() { return ; } + }; + + describe('and pagination.options.pageStartIndex is defined', () => { + const options = { pageStartIndex: 0 }; + beforeEach(() => { + shallowContainer({ + remote: true, + onTableChange: onTableChangeCB, + pagination: { options, wrapperFactory } + }); + wrapper.instance().store.page = 1; + wrapper.instance().store.sizePerPage = 10; + wrapper.instance().handleRemoteFilterChange(); + }); + + it('should calling onTableChange correctly', () => { + expect(onTableChangeCB.calledOnce).toBeTruthy(); + const newState = wrapper.instance().getNewestState(); + newState.page = options.pageStartIndex; + expect(onTableChangeCB.calledWith('filter', newState)).toBeTruthy(); + }); + }); + + describe('and pagination.options.pageStartIndex is not defined', () => { + beforeEach(() => { + shallowContainer({ + remote: true, + onTableChange: onTableChangeCB, + pagination: { wrapperFactory } + }); + wrapper.instance().handleRemoteFilterChange(); + }); + + it('should calling onTableChange correctly', () => { + expect(onTableChangeCB.calledOnce).toBeTruthy(); + const newState = wrapper.instance().getNewestState(); + newState.page = 1; + expect(onTableChangeCB.calledWith('filter', newState)).toBeTruthy(); + }); + }); + }); + }); +}); diff --git a/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js b/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js index 84b2c7798..fba2c676c 100644 --- a/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js +++ b/packages/react-bootstrap-table2/test/row-selection/wrapper.test.js @@ -4,7 +4,7 @@ import { shallow } from 'enzyme'; import Store from '../../src/store'; import BootstrapTable from '../../src/bootstrap-table'; -import RowSelectionWrapper from '../../src/row-selection/wrapper'; +import wrapperFactory from '../../src/row-selection/wrapper'; describe('RowSelectionWrapper', () => { let wrapper; @@ -35,6 +35,7 @@ describe('RowSelectionWrapper', () => { const store = new Store(keyField); store.data = data; + const RowSelectionWrapper = wrapperFactory(BootstrapTable); beforeEach(() => { wrapper = shallow( diff --git a/packages/react-bootstrap-table2/test/sort/wrapper.test.js b/packages/react-bootstrap-table2/test/sort/wrapper.test.js index 733f67d2c..208058d6b 100644 --- a/packages/react-bootstrap-table2/test/sort/wrapper.test.js +++ b/packages/react-bootstrap-table2/test/sort/wrapper.test.js @@ -1,12 +1,12 @@ import 'jsdom-global/register'; import React from 'react'; import sinon from 'sinon'; -import { shallow, mount } from 'enzyme'; +import { shallow } from 'enzyme'; import Const from '../../src/const'; import Store from '../../src/store'; import BootstrapTable from '../../src/bootstrap-table'; -import SortWrapper from '../../src/sort/wrapper'; +import wrapperFactory from '../../src/sort/wrapper'; describe('SortWrapper', () => { let wrapper; @@ -34,6 +34,8 @@ describe('SortWrapper', () => { let store = new Store(keyField); store.data = data; + const SortWrapper = wrapperFactory(BootstrapTable); + beforeEach(() => { wrapper = shallow( { describe('call handleSort function', () => { const sortColumn = columns[0]; - beforeEach(() => { store = new Store(keyField); store.data = data; - wrapper = mount( + wrapper = shallow( { wrapper.instance().handleSort(sortColumn); }); - it('should sorting data correctly', () => { - expect(wrapper.instance().table.state.data[0][keyField]).toEqual(data[1][keyField]); - }); - it('should operating on store correctly', () => { expect(store.sortOrder).toEqual(Const.SORT_DESC); expect(store.sortField).toEqual(sortColumn.dataField);