Merge pull request #158 from react-bootstrap-table/enhance/refactoring

Refarcotring Wrapper
This commit is contained in:
Allen
2017-12-25 17:46:23 +08:00
committed by GitHub
21 changed files with 780 additions and 771 deletions

View File

@@ -116,7 +116,7 @@ class EmptyTableOverlay extends React.Component {
};
}
handleTableChange = ({ page, sizePerPage }) => {
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({

View File

@@ -127,7 +127,7 @@ class Container extends React.Component {
};
}
handleTableChange = ({ page, sizePerPage }) => {
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({

View File

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

View File

@@ -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 }));
}
}
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 };
}
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;
export default (Base, {
_,
remoteResolver
}) =>
class FilterWrapper extends remoteResolver(Component) {
static propTypes = {
store: PropTypes.object.isRequired,
columns: PropTypes.array.isRequired
}
store.filteredData = filters(store, columns, _)(currFilters);
this.setState(() => ({ currFilters, isDataChanged: true }));
}
constructor(props) {
super(props);
this.state = { currFilters: {}, isDataChanged: false };
this.onFilter = this.onFilter.bind(this);
}
// refactoring later
isRemote() {
const { remote } = this.props;
return remote === true || (typeof remote === 'object' && remote.filter);
}
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 }));
}
}
// refactoring later
isPaginationRemote() {
const { remote } = this.props;
return remote === true || (typeof remote === 'object' && remote.pagination);
}
onFilter(column, filterVal, filterType) {
const { store, columns } = this.props;
const currFilters = Object.assign({}, this.state.currFilters);
const { dataField, filter } = column;
render() {
return this.props.baseElement({
...this.props,
key: 'table',
onFilter: this.onFilter,
isDataChanged: this.state.isDataChanged
});
}
}
if (!_.isDefined(filterVal) || filterVal === '') {
delete currFilters[dataField];
} else {
const { comparator = LIKE } = filter.props;
currFilters[dataField] = { filterVal, filterType, comparator };
}
store.filters = currFilters;
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;
}
store.filteredData = filters(store, columns, _)(currFilters);
this.setState(() => ({ currFilters, isDataChanged: true }));
}
render() {
return (
<Base
{ ...this.props }
data={ this.props.store.data }
onFilter={ this.onFilter }
isDataChanged={ this.state.isDataChanged }
/>
);
}
};

View File

@@ -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 => (<BootstrapTable { ...props } />);
const FilterWrapper = wrapperFactory(BootstrapTable, {
_,
remoteResolver
});
const createFilterWrapper = (props, renderFragment = true) => {
wrapper = shallow(<FilterWrapper { ...props } baseElement={ pureTable } />);
wrapper = shallow(<FilterWrapper { ...props } />);
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();
});
});

View File

@@ -1,6 +1,6 @@
import PaginationWrapper from './wrapper';
import wrapperFactory from './wrapper';
export default (options = {}) => ({
PaginationWrapper,
wrapperFactory,
options
});

View File

@@ -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) {

View File

@@ -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);
this.state = { currPage, currSizePerPage };
this.saveToStore(currPage, currSizePerPage);
}
let currPage;
let currSizePerPage;
const { options } = props.pagination;
const sizePerPageList = options.sizePerPageList || Const.SIZE_PER_PAGE_LIST;
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;
}
if (typeof sizePerPage !== 'undefined') {
currSizePerPage = sizePerPage;
needNewState = true;
}
this.saveToStore(currPage, currSizePerPage);
if (needNewState) {
if (onPageChange) {
onPageChange(currPage, currSizePerPage);
// 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.setState(() => ({ currPage, currSizePerPage }));
// 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];
}
this.state = { currPage, currSizePerPage };
this.saveToStore(currPage, currSizePerPage);
}
}
saveToStore(page, sizePerPage) {
this.props.store.page = page;
this.props.store.sizePerPage = sizePerPage;
}
componentWillReceiveProps(nextProps) {
let needNewState = false;
let { currPage, currSizePerPage } = this.state;
const { page, sizePerPage, pageStartIndex, onPageChange } = nextProps.pagination.options;
isRemote() {
const { remote } = this.props;
return remote === true || (typeof remote === 'object' && remote.pagination);
}
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;
}
handleChangePage(currPage) {
const { currSizePerPage } = this.state;
const { pagination: { options }, onRemotePageChange } = this.props;
this.saveToStore(currPage, currSizePerPage);
if (typeof sizePerPage !== 'undefined') {
currSizePerPage = sizePerPage;
needNewState = true;
}
if (options.onPageChange) {
options.onPageChange(currPage, currSizePerPage);
this.saveToStore(currPage, currSizePerPage);
if (needNewState) {
if (onPageChange) {
onPageChange(currPage, currSizePerPage);
}
this.setState(() => ({ currPage, currSizePerPage }));
}
}
if (this.isRemote()) {
onRemotePageChange();
return;
}
this.setState(() => {
return {
currPage
};
});
}
handleChangeSizePerPage(currSizePerPage, currPage) {
const { pagination: { options }, onRemotePageChange } = this.props;
this.saveToStore(currPage, currSizePerPage);
saveToStore(page, sizePerPage) {
this.props.store.page = page;
this.props.store.sizePerPage = sizePerPage;
}
if (options.onSizePerPageChange) {
options.onSizePerPageChange(currSizePerPage, currPage);
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 }));
}
if (this.isRemote()) {
onRemotePageChange();
return;
}
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;
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.isRemote() ?
this.props.data :
getByCurrPage(store)(currPage, currSizePerPage, pageStartIndex);
const data = this.isRemotePagination() ?
this.props.data :
getByCurrPage(store, pageStartIndex);
const base = baseElement({
...this.props,
key: 'table',
data
});
return [
base,
<Pagination
key="pagination"
dataSize={ options.totalSize || store.data.length }
currPage={ currPage }
currSizePerPage={ currSizePerPage }
onPageChange={ this.handleChangePage }
onSizePerPageChange={ this.handleChangeSizePerPage }
sizePerPageList={ options.sizePerPageList || Const.SIZE_PER_PAGE_LIST }
paginationSize={ options.paginationSize || Const.PAGINATION_SIZE }
pageStartIndex={ pageStartIndex }
withFirstAndLast={ withFirstAndLast }
alwaysShowAllBtns={ alwaysShowAllBtns }
hideSizePerPage={ hideSizePerPage }
hidePageListOnlyOnePage={ hidePageListOnlyOnePage }
firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT }
prePageText={ options.prePageText || Const.PRE_PAGE_TEXT }
nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT }
lastPageText={ options.lastPageText || Const.LAST_PAGE_TEXT }
prePageTitle={ options.prePageTitle || Const.PRE_PAGE_TITLE }
nextPageTitle={ options.nextPageTitle || Const.NEXT_PAGE_TITLE }
firstPageTitle={ options.firstPageTitle || Const.FIRST_PAGE_TITLE }
lastPageTitle={ options.lastPageTitle || Const.LAST_PAGE_TITLE }
/>
];
}
}
export default PaginationWrapper;
return [
<Base key="table" { ...this.props } data={ data } />,
<Pagination
key="pagination"
dataSize={ options.totalSize || store.data.length }
currPage={ currPage }
currSizePerPage={ currSizePerPage }
onPageChange={ this.handleChangePage }
onSizePerPageChange={ this.handleChangeSizePerPage }
sizePerPageList={ options.sizePerPageList || Const.SIZE_PER_PAGE_LIST }
paginationSize={ options.paginationSize || Const.PAGINATION_SIZE }
pageStartIndex={ pageStartIndex }
withFirstAndLast={ withFirstAndLast }
alwaysShowAllBtns={ alwaysShowAllBtns }
hideSizePerPage={ hideSizePerPage }
hidePageListOnlyOnePage={ hidePageListOnlyOnePage }
firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT }
prePageText={ options.prePageText || Const.PRE_PAGE_TEXT }
nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT }
lastPageText={ options.lastPageText || Const.LAST_PAGE_TEXT }
prePageTitle={ options.prePageTitle || Const.PRE_PAGE_TITLE }
nextPageTitle={ options.nextPageTitle || Const.NEXT_PAGE_TITLE }
firstPageTitle={ options.firstPageTitle || Const.FIRST_PAGE_TITLE }
lastPageTitle={ options.lastPageTitle || Const.LAST_PAGE_TITLE }
/>
];
}
};

View File

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

View File

@@ -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 => (<BootstrapTable { ...props } />);
const PaginationWrapper = wrapperFactory(BootstrapTable, {
remoteResolver
});
const createPaginationWrapper = (props, renderFragment = true) => {
wrapper = shallow(<PaginationWrapper { ...props } baseElement={ pureTable } />);
wrapper = shallow(<PaginationWrapper { ...props } />);
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();
});
});
});

View File

@@ -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
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.cellEdit) {
if (nextProps.cellEdit.editing) {
this.setState(() => {
return {
...this.state,
message: nextProps.cellEdit.errorMessage
};
});
} 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();
}
}
completeEditing() {
this.setState(() => {
return {
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
};
});
}
}
startEditing(ridx, cidx) {
const editing = () => {
this.setState(() => {
return {
componentWillReceiveProps(nextProps) {
if (nextProps.cellEdit) {
if (nextProps.cellEdit.editing) {
this.setState(() => ({
...this.state,
message: nextProps.cellEdit.errorMessage
}));
} else {
this.escapeEditing();
}
}
}
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(() => ({
ridx: null,
cidx: null,
message: null,
editing: false
}));
}
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 (
<Base
{ ...this.props }
data={ this.props.store.data }
onCellUpdate={ this.handleCellUpdate }
onStartEditing={ this.startEditing }
onEscapeEditing={ this.escapeEditing }
currEditCell={ { ...this.state } }
/>
);
}
};

View File

@@ -2,53 +2,59 @@
/* eslint react/prop-types: 0 */
import React, { Component } from 'react';
import Store from './store';
import withSort from './sort/wrapper';
import withCellEdit from './cell-edit/wrapper';
import withSelection from './row-selection/wrapper';
import {
wrapWithCellEdit,
wrapWithSelection,
wrapWithFilter,
wrapWithSort,
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.wrapComponents();
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;
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
});
}
this.props.onTableChange('filter', this.getNewestState(newState));
}
handleUpdateCell(rowId, dataField, newValue) {
@@ -82,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 (
<this.BaseComponent { ...baseProps } />
);
}
};

View File

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

View File

@@ -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;
if (onSelect) {
const row = getRowByRowId(store)(rowKey);
onSelect(row, checked, rowIndex);
constructor(props) {
super(props);
this.handleRowSelect = this.handleRowSelect.bind(this);
this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this);
this.state = {
selectedRowKeys: props.store.selected
};
}
this.setState(() => ({
selectedRowKeys: currSelected
}));
}
/**
* 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;
/**
* 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);
let currSelected = [...store.selected];
// set next status of all row selected by store.selected or customizing by user.
const result = option || !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);
}
const currSelected = result ?
selectableKeys(store)(nonSelectable) :
unSelectableKeys(store)(nonSelectable);
store.selected = currSelected;
if (onSelect) {
const row = getRowByRowId(store)(rowKey);
onSelect(row, checked, rowIndex);
}
store.selected = currSelected;
if (onSelectAll) {
onSelectAll(result, getSelectedRows(store));
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);
render() {
return selectionElement({
...this.props,
onRowSelect: this.handleRowSelect,
onAllRowsSelect: this.handleAllRowsSelect
});
}
}
// set next status of all row selected by store.selected or customizing by user.
const result = option || !selected;
RowSelectionWrapper.propTypes = {
store: PropTypes.object.isRequired
};
const currSelected = result ?
selectableKeys(store)(nonSelectable) :
unSelectableKeys(store)(nonSelectable);
export default RowSelectionWrapper;
store.selected = currSelected;
if (onSelectAll) {
onSelectAll(result, getSelectedRows(store));
}
this.setState(() => ({
selectedRowKeys: currSelected
}));
}
render() {
return (
<Base
{ ...this.props }
onRowSelect={ this.handleRowSelect }
onAllRowsSelect={ this.handleAllRowsSelect }
/>
);
}
};

View File

@@ -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);
handleSort(column) {
const { store } = this.props;
store.sortBy(column);
this.forceUpdate();
}
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
};
export default SortWrapper;
render() {
return (
<Base
{ ...this.props }
onSort={ this.handleSort }
data={ this.props.store.data }
/>
);
}
};

View File

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

View File

@@ -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(
<CellEditWrapper
@@ -42,7 +47,6 @@ describe('CellEditWrapper', () => {
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(
<CellEditWrapper
keyField={ keyField }
@@ -229,17 +227,14 @@ describe('CellEditWrapper', () => {
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(
<CellEditWrapper
keyField={ keyField }
@@ -287,7 +281,6 @@ describe('CellEditWrapper', () => {
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);

View File

@@ -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(
<BootstrapTable keyField={ keyField } data={ data } columns={ columns } />
<Container keyField={ keyField } data={ data } columns={ columns } />
);
});
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(
<BootstrapTable
<Container
keyField={ keyField }
data={ data }
columns={ columns }
@@ -64,14 +68,16 @@ describe('withDataStore', () => {
);
});
it('should render CellEditWrapper component successfully', () => {
const component = wrapper.find(CellEditWrapper);
expect(component.length).toBe(1);
it('should initialize BaseComponent correctly', () => {
expect(wrapper.instance().BaseComponent.name).toBe('CellEditWrapper');
});
it('should injecting correct props to CellEditWrapper', () => {
const component = wrapper.find(CellEditWrapper);
expect(component.props().onUpdateCell).toBeDefined();
it('should render CellEditWrapper component successfully', () => {
expect(wrapper.find('CellEditWrapper')).toHaveLength(1);
});
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(
<BootstrapTable
<Container
keyField={ keyField }
data={ data }
columns={ columns }
@@ -139,7 +145,7 @@ describe('withDataStore', () => {
beforeEach(() => {
wrapper = shallow(
<BootstrapTable
<Container
keyField={ keyField }
data={ data }
columns={ columns }
@@ -148,20 +154,30 @@ describe('withDataStore', () => {
);
});
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 = () => <div>test</div>;
const wrapperFactory = Base => class PaginationWrapper extends React.Component {
render() { return <Base { ...this.props } />; }
};
const pagination = {
PaginationWrapper
wrapperFactory
};
beforeEach(() => {
wrapper = shallow(
<BootstrapTable
<Container
keyField={ keyField }
data={ data }
columns={ columns }
@@ -170,14 +186,47 @@ describe('withDataStore', () => {
);
});
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();
it('should render BootstrapTable component successfully', () => {
expect(wrapper.dive().find(BootstrapTable)).toHaveLength(1);
});
it('should render PaginationWrapper component successfully', () => {
expect(wrapper.find('PaginationWrapper').length).toBe(1);
});
});
describe('when filter prop is defined', () => {
const wrapperFactory = Base => class FilterWrapper extends React.Component {
render() { return <Base { ...this.props } />; }
};
const filter = { wrapperFactory };
beforeEach(() => {
wrapper = shallow(
<Container
keyField={ keyField }
data={ data }
columns={ columns }
filter={ filter }
/>
);
});
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);
});
it('should render FilterWrapper component successfully', () => {
expect(wrapper.find('FilterWrapper').length).toBe(1);
});
});
@@ -189,7 +238,7 @@ describe('withDataStore', () => {
sort: true
}];
wrapper = shallow(
<BootstrapTable
<Container
keyField={ keyField }
data={ data }
columns={ columnsWithSort }
@@ -197,103 +246,16 @@ describe('withDataStore', () => {
);
});
it('should initialize BaseComponent correctly', () => {
expect(wrapper.instance().BaseComponent.name).toBe('SortWrapper');
});
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);
});
});
describe('handleRemotePageChange', () => {
const onTableChangeCallBack = sinon.stub();
beforeEach(() => {
wrapper = shallow(
<BootstrapTable
keyField={ keyField }
data={ data }
columns={ columns }
onTableChange={ onTableChangeCallBack }
/>
);
wrapper.instance().handleRemotePageChange();
});
it('should calling onTableChange correctly', () => {
expect(onTableChangeCallBack.calledOnce).toBeTruthy();
expect(onTableChangeCallBack.calledWith('pagination', wrapper.instance().getNewestState())).toBeTruthy();
});
});
describe('handleRemoteFilterChange', () => {
const onTableChangeCallBack = sinon.stub();
beforeEach(() => {
onTableChangeCallBack.reset();
wrapper = shallow(
<BootstrapTable
keyField={ keyField }
data={ data }
columns={ columns }
onTableChange={ onTableChangeCallBack }
/>
);
});
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();
});
});
describe('when isRemotePagination argument is false', () => {
describe('and pagination.options.pageStartIndex is defined', () => {
const options = { pageStartIndex: 0 };
beforeEach(() => {
wrapper = shallow(
<BootstrapTable
keyField={ keyField }
data={ data }
columns={ columns }
pagination={ { options, PaginationWrapper: () => {} } }
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(
<BootstrapTable
keyField={ keyField }
data={ data }
columns={ columns }
pagination={ { PaginationWrapper: () => {} } }
onTableChange={ onTableChangeCallBack }
/>
);
wrapper.instance().handleRemoteFilterChange(true);
});
it('should calling onTableChange correctly', () => {
expect(onTableChangeCallBack.calledOnce).toBeTruthy();
const newState = wrapper.instance().getNewestState();
newState.page = 1;
expect(onTableChangeCallBack.calledWith('filter', newState)).toBeTruthy();
});
});
expect(wrapper.find('SortWrapper').length).toBe(1);
});
});
});

View File

@@ -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(
<Container
keyField={ keyField }
data={ data }
columns={ columns }
{ ...props }
/>
);
};
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 <Base { ...this.props } />; }
};
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();
});
});
});
});
});

View File

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

View File

@@ -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(
<SortWrapper
@@ -56,11 +58,10 @@ describe('SortWrapper', () => {
describe('call handleSort function', () => {
const sortColumn = columns[0];
beforeEach(() => {
store = new Store(keyField);
store.data = data;
wrapper = mount(
wrapper = shallow(
<SortWrapper
keyField={ keyField }
data={ data }
@@ -71,10 +72,6 @@ describe('SortWrapper', () => {
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);