Compare commits

...

29 Commits

Author SHA1 Message Date
AllenFang
345cb83493 Publish
- react-bootstrap-table2-example@1.0.2
 - react-bootstrap-table2-paginator@1.0.2
 - react-bootstrap-table2-toolkit@1.0.2
 - react-bootstrap-table-next@1.1.2
2018-08-21 17:05:08 +08:00
Allen
6e526f455b Merge pull request #500 from react-bootstrap-table/develop
20180821 release
2018-08-21 17:03:00 +08:00
AllenFang
b05cf48f36 Merge branch 'COzero-issue-363' into develop 2018-08-20 23:36:42 +08:00
AllenFang
28249c9089 fixed version 2018-08-20 23:36:22 +08:00
AllenFang
2b6081ab31 Merge branch 'issue-363' of https://github.com/COzero/react-bootstrap-table2 into COzero-issue-363 2018-08-20 23:23:32 +08:00
AllenFang
c935447266 Merge branch 'develop' of https://github.com/react-bootstrap-table/react-bootstrap-table2 into develop 2018-08-20 22:59:36 +08:00
AllenFang
ba1d6fa3ed fix #498 2018-08-20 22:59:14 +08:00
Allen
1b3b68f8a7 fix controlled page state will effect the internal page state when internal table rerender (#492) 2018-08-19 12:43:52 +08:00
copas2
4a3486cc3c fix #488
* updated test for pagination fix

* suspected bug in page.js

Local pagination aligning returns start page when eg editing cell locally. This results in state change, too. Not really getting the purpose of the original idea. Please consider this modification. It only checks if page fits in the data size range. As I saw, this alignment only affects local pagination, not remote.

* modified fix w/ pageStartIndex

I forgot to mind pageStartIndex
2018-08-16 17:16:47 +08:00
Allen
cb49455a4e Merge pull request #489 from react-bootstrap-table/bugfix/infinite-remote-search
Bugfix/infinite remote search
2018-08-15 23:16:52 +08:00
AllenFang
5e63d6ae59 avoid infinite remote search 2018-08-15 23:08:46 +08:00
AllenFang
e3ca6f2c24 Publish
- react-bootstrap-table-next@1.1.1
2018-08-13 22:01:15 +08:00
Jeremy Nagel
96d33a60ba Add snapshot tests 2018-08-13 16:50:43 +10:00
Jeremy Nagel
03389aece0 Merge branch 'master' of github.com:react-bootstrap-table/react-bootstrap-table2 into issue-363 2018-08-13 16:45:34 +10:00
AllenFang
f86876ba51 Publish
- react-bootstrap-table2-example@1.0.1
 - react-bootstrap-table2-paginator@1.0.1
 - react-bootstrap-table2-toolkit@1.0.1
 - react-bootstrap-table-next@1.1.0
2018-08-12 14:41:44 +08:00
Allen
44569d6df9 Merge pull request #482 from react-bootstrap-table/develop
20180812 release
2018-08-12 14:39:43 +08:00
AllenFang
3663d1d4fe fix Warning: Failed prop type: Invalid prop of type supplied to , expected 2018-08-12 13:53:29 +08:00
Allen
6225f0e5cb fix #471 (#481) 2018-08-12 13:32:12 +08:00
Allen
f0fd06a5f5 fix #465 (#470) 2018-08-11 15:17:41 +08:00
Allen
b181c98a38 Merge pull request #449 from react-bootstrap-table/enhance/performance-cell-level
Enhance/performance on cell level
2018-08-11 15:17:15 +08:00
Allen
1f51f1a08d fix #464 (#469) 2018-08-11 15:03:17 +08:00
Allen
586acaed68 fix #455 (#468) 2018-08-11 13:33:52 +08:00
Allen
528be5c058 fix #453 (#467) 2018-08-11 13:32:44 +08:00
AllenFang
76575bd9f1 patch docs 2018-08-05 16:32:27 +08:00
AllenFang
54b98f41f4 implement shouldComponentUpdate on cell component 2018-08-05 16:05:22 +08:00
AllenFang
ffac3a42c5 add a large table example 2018-08-05 16:05:22 +08:00
AllenFang
97b9e1097b fix wrong title 2018-08-05 16:05:22 +08:00
AllenFang
a6de7fa84a integrate with underscroe.js 2018-08-05 16:05:22 +08:00
Jeremy Nagel
f35d644608 [BUGFIX] Fix issue with missing onChange prop for selection checkbox 2018-07-16 11:18:02 +10:00
36 changed files with 1070 additions and 655 deletions

View File

@@ -95,6 +95,10 @@ dataField: 'address.city'
* `rowIndex`
* [`formatExtraData`](#formatExtraData)
> Attention:
> Don't use any state data or any external data in formatter function, please pass them via [`formatExtraData`](#formatExtraData).
> In addition, please make formatter function as pure function as possible as you can.
## <a name='headerFormatter'>column.headerFormatter - [Function]</a>
`headerFormatter` allow you to customize the header column and only accept a callback function which take three arguments and a JSX/String are expected for return.

View File

@@ -52,6 +52,7 @@
"css-loader": "0.28.1",
"enzyme": "3.3.0",
"enzyme-adapter-react-16": "1.1.1",
"enzyme-to-json": "3.3.4",
"eslint": "4.5.0",
"eslint-config-airbnb": "15.1.0",
"eslint-loader": "1.9.0",
@@ -82,7 +83,8 @@
"classnames": "2.2.5",
"prop-types": "15.5.10",
"react": "16.3.2",
"react-dom": "16.3.2"
"react-dom": "16.3.2",
"underscore": "1.9.1"
},
"jest": {
"collectCoverageFrom": [

View File

@@ -0,0 +1,32 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(5000);
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
selectRow={ { mode: 'checkbox' } }
cellEdit={ cellEditFactory({
mode: 'click'
}) }
/>
</div>
);

View File

@@ -41,7 +41,7 @@ const columns = [{
export default () => (
<div>
<h3>Try to hover on Product Name header column</h3>
<h3>Try to hover on Product ID Cell</h3>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>

View File

@@ -22,8 +22,19 @@ const columns = [{
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// ...
const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
const NoDataIndication = () => (
<div className="spinner">
<div className="rect1" />
<div className="rect2" />
<div className="rect3" />
<div className="rect4" />
<div className="rect5" />
</div>
);
const Table = ({ data, page, sizePerPage, onTableChange, totalSize }) => (
<div>
<BootstrapTable
remote
@@ -32,12 +43,13 @@ const RemotePagination = ({ data, page, sizePerPage, onTableChange, totalSize })
columns={ columns }
pagination={ paginationFactory({ page, sizePerPage, totalSize }) }
onTableChange={ onTableChange }
noDataIndication={ () => <NoDataIndication /> }
/>
<Code>{ sourceCode }</Code>
</div>
);
class Container extends React.Component {
class EmptyTableOverlay extends React.Component {
constructor(props) {
super(props);
this.state = {
@@ -47,7 +59,7 @@ class Container extends React.Component {
};
}
handleTableChange = ({ page, sizePerPage }) => {
handleTableChange = (type, { page, sizePerPage }) => {
const currentIndex = (page - 1) * sizePerPage;
setTimeout(() => {
this.setState(() => ({
@@ -55,13 +67,14 @@ class Container extends React.Component {
data: products.slice(currentIndex, currentIndex + sizePerPage),
sizePerPage
}));
}, 2000);
}, 3000);
this.setState(() => ({ data: [] }));
}
render() {
const { data, sizePerPage, page } = this.state;
return (
<RemotePagination
<Table
data={ data }
page={ page }
sizePerPage={ sizePerPage }

View File

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

View File

@@ -11,6 +11,7 @@ import StripHoverCondensedTable from 'examples/basic/striped-hover-condensed-tab
import NoDataTable from 'examples/basic/no-data-table';
import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
import CaptionTable from 'examples/basic/caption-table';
import LargeTable from 'examples/basic/large-table';
// work on columns
import NestedDataTable from 'examples/columns/nested-data-table';
@@ -174,7 +175,8 @@ storiesOf('Basic Table', module)
.add('borderless table', () => <BorderlessTable />)
.add('Indication For Empty Table', () => <NoDataTable />)
.add('Customized id and class table', () => <CustomizedIdClassesTable />)
.add('Table with caption', () => <CaptionTable />);
.add('Table with caption', () => <CaptionTable />)
.add('Large Table', () => <LargeTable />);
storiesOf('Work on Columns', module)
.add('Display Nested Data', () => <NestedDataTable />)

View File

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

View File

@@ -54,31 +54,22 @@ export default (
componentWillReceiveProps(nextProps) {
let needNewState = false;
let { currPage, currSizePerPage } = this;
const { page, sizePerPage, onPageChange } = nextProps.pagination.options;
let { currPage } = this;
const { currSizePerPage } = this;
const { onPageChange } = nextProps.pagination.options;
const pageStartIndex = typeof nextProps.pagination.options.pageStartIndex !== 'undefined' ?
nextProps.pagination.options.pageStartIndex : Const.PAGE_START_INDEX;
if (typeof page !== 'undefined' && currPage !== page) { // user defined page
currPage = page;
needNewState = true;
} else {
// user should align the page when the page is not fit to the data size when remote enable
if (!isRemotePagination()) {
const newPage = alignPage(nextProps.data, currPage, currSizePerPage, pageStartIndex);
if (currPage !== newPage) {
currPage = newPage;
needNewState = true;
}
// user should align the page when the page is not fit to the data size when remote enable
if (!isRemotePagination()) {
const newPage = alignPage(nextProps.data, currPage, currSizePerPage, pageStartIndex);
if (currPage !== newPage) {
currPage = newPage;
needNewState = true;
}
}
if (typeof sizePerPage !== 'undefined' && currSizePerPage !== sizePerPage) {
currSizePerPage = sizePerPage;
needNewState = true;
}
if (needNewState) {
if (onPageChange) {
onPageChange(currPage, currSizePerPage);

View File

@@ -40,8 +40,8 @@ export default ExtendBase =>
let from = ((currPage - pageStartIndex) * currSizePerPage);
from = dataSize === 0 ? 0 : from + 1;
let to = Math.min((currSizePerPage * (currPage + offset) - 1), dataSize);
if (to >= dataSize) to -= 1;
let to = Math.min(currSizePerPage * (currPage + offset), dataSize);
if (to > dataSize) to = dataSize;
return [from, to];
}

View File

@@ -23,10 +23,9 @@ export const alignPage = (
sizePerPage,
pageStartIndex
) => {
const end = endIndex(page, sizePerPage, pageStartIndex);
const dataSize = data.length;
if (end - 1 > dataSize) {
if (page < pageStartIndex || page > (Math.floor(dataSize / sizePerPage) + pageStartIndex)) {
return pageStartIndex;
}
return page;

View File

@@ -126,69 +126,6 @@ describe('PaginationContext', () => {
let instance;
let nextProps;
describe('when nextProps.pagination.options.page is existing', () => {
const onPageChange = jest.fn();
afterEach(() => {
onPageChange.mockReset();
});
describe('and if it is different with currPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
page: 2,
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('should call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(1);
expect(onPageChange).toHaveBeenCalledWith(
instance.currPage,
instance.currSizePerPage
);
});
it('should set correct currPage', () => {
expect(instance.currPage).toEqual(nextProps.pagination.options.page);
});
});
describe('and if it is same as currPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
page: 1,
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('shouldn\'t call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(0);
});
it('should have correct currPage', () => {
expect(instance.currPage).toEqual(nextProps.pagination.options.page);
});
});
});
describe('when nextProps.pagination.options.page is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
@@ -206,69 +143,6 @@ describe('PaginationContext', () => {
});
});
describe('when nextProps.pagination.options.sizePerPage is existing', () => {
const onPageChange = jest.fn();
afterEach(() => {
onPageChange.mockReset();
});
describe('and if it is different with currSizePerPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
sizePerPage: Const.SIZE_PER_PAGE_LIST[2],
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('should call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(1);
expect(onPageChange).toHaveBeenCalledWith(
instance.currPage,
instance.currSizePerPage
);
});
it('should set correct currSizePerPage', () => {
expect(instance.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
});
});
describe('and if it is same as currSizePerPage', () => {
beforeEach(() => {
wrapper = shallow(shallowContext());
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: {
options: {
sizePerPage: Const.SIZE_PER_PAGE_LIST[0],
onPageChange
}
}
};
instance.componentWillReceiveProps(nextProps);
});
it('shouldn\'t call options.onPageChange', () => {
expect(onPageChange).toHaveBeenCalledTimes(0);
});
it('should have correct currSizePerPage', () => {
expect(instance.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
});
});
});
describe('when nextProps.pagination.options.sizePerPage is not existing', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
@@ -281,10 +155,53 @@ describe('PaginationContext', () => {
instance.componentWillReceiveProps(nextProps);
});
it('should not set currPage', () => {
it('should not set currSizePerPage', () => {
expect(instance.currSizePerPage).toEqual(Const.SIZE_PER_PAGE_LIST[2]);
});
});
describe('when page is not align', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultPagination,
page: 2
}));
instance = wrapper.instance();
wrapper.render();
nextProps = {
data: [],
pagination: { ...defaultPagination }
};
instance.componentWillReceiveProps(nextProps);
});
it('should reset currPage to first page', () => {
expect(instance.currPage).toEqual(1);
});
describe('if options.onPageChange is defined', () => {
const onPageChange = jest.fn();
beforeEach(() => {
onPageChange.mockClear();
wrapper = shallow(shallowContext({
...defaultPagination,
page: 2
}));
instance = wrapper.instance();
wrapper.render();
nextProps = {
data: [],
pagination: { ...defaultPagination, options: { onPageChange } }
};
instance.componentWillReceiveProps(nextProps);
});
it('should call options.onPageChange correctly', () => {
expect(onPageChange).toHaveBeenCalledTimes(1);
expect(onPageChange).toHaveBeenCalledWith(instance.currPage, instance.currSizePerPage);
});
});
});
});
describe('handleChangePage', () => {

View File

@@ -119,7 +119,34 @@ describe('PageResolver', () => {
it('should return correct array with from and to value', () => {
const instance = wrapper.instance();
expect(instance.calculateFromTo()).toEqual([1, props.currSizePerPage - 1]);
expect(instance.calculateFromTo()).toEqual([1, props.currSizePerPage]);
});
describe('if data is empty', () => {
beforeEach(() => {
props.dataSize = 87;
props.currPage = 9;
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
});
it('should return correct array with from and to value', () => {
const instance = wrapper.instance();
expect(instance.calculateFromTo()).toEqual([81, props.dataSize]);
});
});
describe('if current page is last page', () => {
beforeEach(() => {
props.dataSize = 0;
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
});
it('should return correct array with from and to value', () => {
const instance = wrapper.instance();
expect(instance.calculateFromTo()).toEqual([0, 0]);
});
});
});

View File

@@ -45,8 +45,8 @@ describe('Page Functions', () => {
describe('alignPage', () => {
const pageStartIndex = 1;
const sizePerPage = 10;
const page = 2;
describe('if the length of store.data is less than the end page index', () => {
const page = 3;
describe('if the page does not fit the pages interval calculated from the length of store.data', () => {
beforeEach(() => {
data = [];
for (let i = 0; i < 15; i += 1) {

View File

@@ -120,4 +120,7 @@ Custom the csv file separator.
Default is `false`. Give true to avoid to attach the csv header.
#### noAutoBOM - [bool]
Default is `true`.
Default is `true`.
#### exportAll - [bool]
Default is `true`. `false` will only export current data which display on table.

View File

@@ -48,7 +48,9 @@ class ToolkitProvider extends statelessDrcorator(React.Component) {
}
onSearch(searchText) {
this.setState({ searchText });
if (searchText !== this.state.searchText) {
this.setState({ searchText });
}
}
/**
@@ -69,7 +71,8 @@ class ToolkitProvider extends statelessDrcorator(React.Component) {
columns: this.props.columns,
data: this.props.data,
bootstrap4: this.props.bootstrap4,
setDependencyModules: this.setDependencyModules
setDependencyModules: this.setDependencyModules,
registerExposedAPI: this.registerExposedAPI
};
if (this.props.search) {
baseProps.search = {

View File

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

View File

@@ -4,13 +4,14 @@ const csvDefaultOptions = {
fileName: 'spreadsheet.csv',
separator: ',',
ignoreHeader: false,
noAutoBOM: true
noAutoBOM: true,
exportAll: true
};
export default Base =>
class CSVOperation extends Base {
handleExportCSV = () => {
const { columns, data, exportCSV } = this.props;
const { columns, exportCSV } = this.props;
const meta = getMetaInfo(columns);
const options = exportCSV === true ?
csvDefaultOptions :
@@ -18,6 +19,8 @@ export default Base =>
...csvDefaultOptions,
...exportCSV
};
const data = options.exportAll ? this.props.data : this.getData();
const content = transform(data, meta, this._.get, options);
save(content, options);
}

View File

@@ -22,14 +22,16 @@ export default (options = {
constructor(props) {
super(props);
this.needToSearch = true;
this.performRemoteSearch = props.searchText !== '';
}
componentWillReceiveProps(nextProps) {
if (nextProps.searchText !== this.props.searchText) {
this.needToSearch = true;
} else {
this.needToSearch = false;
if (isRemoteSearch()) {
if (nextProps.searchText !== this.props.searchText) {
this.performRemoteSearch = true;
} else {
this.performRemoteSearch = false;
}
}
}
@@ -37,9 +39,7 @@ export default (options = {
const { data, columns } = this.props;
let { searchText } = this.props;
if (!this.needToSearch) return data;
if (isRemoteSearch()) {
if (isRemoteSearch() && this.performRemoteSearch) {
handleRemoteSearchChange(searchText);
return data;
}

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table-next",
"version": "1.0.0",
"version": "1.1.2",
"description": "Next generation of react-bootstrap-table",
"main": "./lib/index.js",
"repository": {
@@ -36,7 +36,8 @@
}
],
"dependencies": {
"classnames": "2.2.5"
"classnames": "2.2.5",
"underscore": "1.9.1"
},
"peerDependencies": {
"classnames": "^2.2.5",

View File

@@ -15,6 +15,15 @@ class BootstrapTable extends PropsBaseResolver(Component) {
constructor(props) {
super(props);
this.validateProps();
if (props.registerExposedAPI) {
const getData = () => this.getData();
props.registerExposedAPI(getData);
}
}
// Exposed APIs
getData = () => {
return this.props.data;
}
render() {
@@ -161,7 +170,7 @@ BootstrapTable.propTypes = {
}),
onRowExpand: PropTypes.func,
onAllRowExpand: PropTypes.func,
isAnyExpands: PropTypes.func,
isAnyExpands: PropTypes.bool,
rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
rowEvents: PropTypes.object,
rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

View File

@@ -10,6 +10,25 @@ class Cell extends Component {
this.handleEditingCell = this.handleEditingCell.bind(this);
}
shouldComponentUpdate(nextProps) {
const shouldUpdate =
_.get(this.props.row, this.props.column.dataField)
!== _.get(nextProps.row, nextProps.column.dataField) ||
this.props.column.hidden !== nextProps.column.hidden ||
this.props.rowIndex !== nextProps.rowIndex ||
this.props.columnIndex !== nextProps.columnIndex ||
this.props.className !== nextProps.className ||
this.props.title !== nextProps.title ||
this.props.editable !== nextProps.editable ||
this.props.clickToEdit !== nextProps.clickToEdit ||
this.props.dbclickToEdit !== nextProps.dbclickToEdit ||
!_.isEqual(this.props.style, nextProps.style) ||
!_.isEqual(this.props.column.formatExtraData, nextProps.column.formatExtraData) ||
!_.isEqual(this.props.column.events, nextProps.column.events) ||
!_.isEqual(this.props.column.attrs, nextProps.column.attrs);
return shouldUpdate;
}
handleEditingCell(e) {
const { column, onStart, rowIndex, columnIndex, clickToEdit, dbclickToEdit } = this.props;
const { events } = column;
@@ -33,62 +52,32 @@ class Cell extends Component {
rowIndex,
column,
columnIndex,
onStart,
editable,
clickToEdit,
dbclickToEdit
dbclickToEdit,
...rest
} = this.props;
const {
dataField,
formatter,
formatExtraData,
style,
classes,
title,
events,
align,
attrs
formatExtraData
} = column;
let cellTitle;
let cellStyle = {};
const attrs = { ...rest };
let content = _.get(row, dataField);
const cellAttrs = {
..._.isFunction(attrs) ? attrs(content, row, rowIndex, columnIndex) : attrs,
...events
};
const cellClasses = _.isFunction(classes)
? classes(content, row, rowIndex, columnIndex)
: classes;
if (style) {
cellStyle = _.isFunction(style) ? style(content, row, rowIndex, columnIndex) : style;
}
if (title) {
cellTitle = _.isFunction(title) ? title(content, row, rowIndex, columnIndex) : content;
cellAttrs.title = cellTitle;
}
if (formatter) {
content = column.formatter(content, row, rowIndex, formatExtraData);
}
if (align) {
cellStyle.textAlign =
_.isFunction(align) ? align(content, row, rowIndex, columnIndex) : align;
}
if (cellClasses) cellAttrs.className = cellClasses;
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
if (clickToEdit && editable) {
cellAttrs.onClick = this.handleEditingCell;
attrs.onClick = this.handleEditingCell;
} else if (dbclickToEdit && editable) {
cellAttrs.onDoubleClick = this.handleEditingCell;
attrs.onDoubleClick = this.handleEditingCell;
}
return (
<td { ...cellAttrs }>
<td { ...attrs }>
{ typeof content === 'boolean' ? `${content}` : content }
</td>
);

View File

@@ -75,6 +75,7 @@ export default class SelectionCell extends Component {
checked={ selected }
disabled={ disabled }
className={ bootstrap4 ? 'selection-input-4' : '' }
onChange={ () => {} }
/>
)
}

View File

@@ -12,6 +12,7 @@ export const CheckBox = ({ className, checked, indeterminate }) => (
ref={ (input) => {
if (input) input.indeterminate = indeterminate; // eslint-disable-line no-param-reassign
} }
onChange={ () => {} }
/>
);

View File

@@ -102,6 +102,45 @@ class Row extends eventDelegater(Component) {
/>
);
}
// render cell
let cellTitle;
let cellStyle = {};
const cellAttrs = {
..._.isFunction(column.attrs)
? column.attrs(content, row, rowIndex, index)
: column.attrs,
...column.events
};
const cellClasses = _.isFunction(column.classes)
? column.classes(content, row, rowIndex, index)
: column.classes;
if (column.style) {
cellStyle = _.isFunction(column.style)
? column.style(content, row, rowIndex, index)
: column.style;
cellStyle = Object.assign({}, cellStyle) || {};
}
if (column.title) {
cellTitle = _.isFunction(column.title)
? column.title(content, row, rowIndex, index)
: content;
cellAttrs.title = cellTitle;
}
if (column.align) {
cellStyle.textAlign =
_.isFunction(column.align)
? column.align(content, row, rowIndex, index)
: column.align;
}
if (cellClasses) cellAttrs.className = cellClasses;
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
return (
<Cell
key={ `${content}-${index}` }
@@ -113,6 +152,7 @@ class Row extends eventDelegater(Component) {
editable={ editable }
clickToEdit={ mode === CLICK_TO_CELL_EDIT }
dbclickToEdit={ mode === DBCLICK_TO_CELL_EDIT }
{ ...cellAttrs }
/>
);
}

View File

@@ -1,6 +1,7 @@
/* eslint no-empty: 0 */
/* eslint no-param-reassign: 0 */
/* eslint prefer-rest-params: 0 */
import _ from 'underscore';
function splitNested(str) {
return [str]
@@ -38,22 +39,8 @@ function set(target, field, value, safe = false) {
}, target);
}
function isFunction(obj) {
return obj && (typeof obj === 'function');
}
/**
* Checks if `value` is the Object. the `Object` except `Function` and `Array.`
*
* @param {*} obj - The value gonna check
*/
function isObject(obj) {
const type = typeof obj;
return obj !== null && type === 'object' && obj.constructor === Object;
}
function isEmptyObject(obj) {
if (!isObject(obj)) return false;
if (!_.isObject(obj)) return false;
const hasOwnProperty = Object.prototype.hasOwnProperty;
const keys = Object.keys(obj);
@@ -91,18 +78,9 @@ function debounce(func, wait, immediate) {
timeout = setTimeout(later, wait || 0);
if (callNow) {
func.appy(this, arguments);
func.apply(this, arguments);
}
};
}
export default {
get,
set,
isFunction,
isObject,
isEmptyObject,
isDefined,
sleep,
debounce
};
export default Object.assign(_, { get, set, isDefined, isEmptyObject, sleep, debounce });

View File

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

View File

@@ -79,362 +79,6 @@ describe('Cell', () => {
});
});
describe('when column.style prop is defined', () => {
let column;
const columnIndex = 1;
const rowIndex = 1;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID'
};
});
describe('when style is an object', () => {
beforeEach(() => {
column.style = { backgroundColor: 'red' };
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('style')).toEqual(column.style);
});
});
describe('when style is a function', () => {
const returnStyle = { backgroundColor: 'red' };
let styleCallBack;
beforeEach(() => {
styleCallBack = sinon.stub()
.withArgs(row[column.dataField], row, rowIndex, columnIndex)
.returns(returnStyle);
column.style = styleCallBack;
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
afterEach(() => { styleCallBack.reset(); });
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('style')).toEqual(returnStyle);
});
it('should call custom style function correctly', () => {
expect(styleCallBack.callCount).toBe(1);
expect(
styleCallBack.calledWith(row[column.dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.classes prop is defined', () => {
let column;
const columnIndex = 1;
const rowIndex = 1;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID'
};
});
describe('when classes is an object', () => {
beforeEach(() => {
column.classes = 'td-test-class';
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.hasClass(column.classes)).toBe(true);
});
});
describe('when classes is a function', () => {
const returnClasses = 'td-test-class';
let classesCallBack;
beforeEach(() => {
classesCallBack = sinon.stub()
.withArgs(row[column.dataField], row, rowIndex, columnIndex)
.returns(returnClasses);
column.classes = classesCallBack;
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
afterEach(() => { classesCallBack.reset(); });
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.hasClass(returnClasses)).toBe(true);
});
it('should call custom classes function correctly', () => {
expect(classesCallBack.callCount).toBe(1);
expect(
classesCallBack.calledWith(row[column.dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.title prop is defined', () => {
let column;
const columnIndex = 1;
const rowIndex = 1;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID'
};
});
describe('when title is boolean', () => {
beforeEach(() => {
column.title = true;
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should render title as cell value as default', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('title')).toEqual(row[column.dataField]);
});
});
describe('when title is custom function', () => {
const customTitle = 'test_title';
let titleCallBack;
beforeEach(() => {
titleCallBack = sinon.stub()
.withArgs(row[column.dataField], row, rowIndex, columnIndex)
.returns(customTitle);
column.title = titleCallBack;
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should render title correctly by custom title function', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('title')).toBe(customTitle);
});
it('should call custom title function correctly', () => {
expect(titleCallBack.callCount).toBe(1);
expect(
titleCallBack.calledWith(row[column.dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.events prop is defined', () => {
let column;
const columnIndex = 1;
const rowIndex = 1;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
events: {
onClick: sinon.stub()
}
};
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should attachs DOM event successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('onClick')).toBeDefined();
});
it('event hook should be called when triggering', () => {
wrapper.find('td').simulate('click');
expect(column.events.onClick.callCount).toBe(1);
});
});
describe('when column.align prop is defined', () => {
let column;
const columnIndex = 1;
const rowIndex = 1;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID'
};
});
describe('when align is string', () => {
beforeEach(() => {
column.align = 'center';
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should render style.textAlign correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('style').textAlign).toEqual(column.align);
});
});
describe('when align is custom function', () => {
const customAlign = 'center';
let alignCallBack;
beforeEach(() => {
alignCallBack = sinon.stub()
.withArgs(row[column.dataField], row, rowIndex, columnIndex)
.returns(customAlign);
column.align = alignCallBack;
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should render style.textAlign correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('style').textAlign).toEqual(customAlign);
});
it('should call custom headerAlign function correctly', () => {
expect(alignCallBack.callCount).toBe(1);
expect(
alignCallBack.calledWith(row[column.dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.attrs prop is defined', () => {
let column;
const columnIndex = 1;
const rowIndex = 1;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID'
};
});
describe('when attrs is an object', () => {
it('should render column.attrs correctly', () => {
column.attrs = {
'data-test': 'test',
title: 'title',
className: 'attrs-class',
style: {
backgroundColor: 'attrs-style-test',
display: 'none',
textAlign: 'right'
}
};
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ 1 } column={ column } />);
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('data-test')).toEqual(column.attrs['data-test']);
expect(wrapper.find('td').prop('title')).toEqual(column.attrs.title);
expect(wrapper.hasClass(column.attrs.className)).toBe(true);
expect(wrapper.find('td').prop('style')).toEqual(column.attrs.style);
expect(wrapper.find('td').prop('style').textAlign).toEqual(column.attrs.style.textAlign);
});
describe('when column.title prop is defined', () => {
it('attrs.title should be overwrited', () => {
column.title = true;
column.attrs = { title: 'title' };
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ 1 } column={ column } />);
expect(wrapper.find('td').prop('title')).toEqual(row[column.dataField]);
});
});
describe('when column.classes prop is defined', () => {
it('attrs.class should be overwrited', () => {
column.classes = 'td-test-class';
column.attrs = { className: 'attrs-class' };
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ 1 } column={ column } />);
expect(wrapper.hasClass(column.classes)).toBe(true);
});
});
describe('when column.style prop is defined', () => {
it('attrs.style should be overwrited', () => {
column.style = { backgroundColor: 'red' };
column.attrs = { style: { backgroundColor: 'attrs-style-test' } };
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ 1 } column={ column } />);
expect(wrapper.find('td').prop('style')).toEqual(column.style);
});
});
describe('when column.align prop is defined', () => {
it('attrs.style.textAlign should be overwrited', () => {
column.align = 'center';
column.attrs = { style: { textAlign: 'right' } };
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ 1 } column={ column } />);
expect(wrapper.find('td').prop('style').textAlign).toEqual(column.align);
});
});
});
describe('when attrs is custom function', () => {
let attrsCallBack;
const customAttrs = {
title: 'title',
'data-test': 'test'
};
beforeEach(() => {
attrsCallBack = sinon.stub()
.withArgs(row[column.dataField], row, rowIndex, columnIndex)
.returns(customAttrs);
column.attrs = attrsCallBack;
wrapper = shallow(
<Cell row={ row } columnIndex={ columnIndex } rowIndex={ rowIndex } column={ column } />);
});
it('should render style.attrs correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find('td').prop('data-test')).toEqual(customAttrs['data-test']);
expect(wrapper.find('td').prop('title')).toEqual(customAttrs.title);
});
it('should call custom attrs function correctly', () => {
expect(attrsCallBack.callCount).toBe(1);
expect(
attrsCallBack.calledWith(row[column.dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when editable prop is true', () => {
let onStartCallBack;
const rowIndex = 1;
@@ -528,4 +172,259 @@ describe('Cell', () => {
});
});
});
describe('shouldComponentUpdate', () => {
let props;
let nextProps;
describe('when content is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, row: { id: 1, name: 'CDE' } };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when column.hidden is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, column: { ...column, hidden: true } };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.rowIndex is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, rowIndex: 2 };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.columnIndex is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, columnIndex: 2 };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.className is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column,
className: 'test'
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, className: null };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.title is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column,
title: 'test'
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, title: '123' };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.title is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, editable: true };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.clickToEdit is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, clickToEdit: true };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.dbclickToEdit is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, dbclickToEdit: true };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when props.style is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column,
style: {}
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, style: { color: 'red' } };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when column.formatExtraData is change', () => {
const column = { dataField: 'name', text: 'Product Name', formatExtraData: { a: 1 } };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, column: { ...column, formatExtraData: { b: 2 } } };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when column.events is change', () => {
const column = { dataField: 'name', text: 'Product Name', events: { a: jest.fn() } };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, column: { ...column, events: { b: jest.fn() } } };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when column.attrs is change', () => {
const column = { dataField: 'name', text: 'Product Name', attrs: { 'data-att': 1 } };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, column: { ...column, attrs: null } };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
});
});

View File

@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<SelectionCell /> render should render component correctly 1`] = `
<td
onClick={[Function]}
>
<input
checked={true}
className=""
onChange={[Function]}
type="checkbox"
/>
</td>
`;

View File

@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<CheckBox /> render should render component correctly 1`] = `
<input
checked={true}
onChange={[Function]}
type="checkbox"
/>
`;

View File

@@ -1,6 +1,7 @@
import 'jsdom-global/register';
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import sinon from 'sinon';
import { shallowWithContext } from '../test-helpers/new-context';
@@ -61,7 +62,8 @@ describe('<SelectionCell />', () => {
mode={ mode }
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
wrapper.find('td').simulate('click');
});
@@ -72,9 +74,7 @@ describe('<SelectionCell />', () => {
it('should calling onRowSelect callback correctly', () => {
expect(mockOnRowSelect.calledOnce).toBe(true);
expect(
mockOnRowSelect.calledWith(rowKey, !selected, rowIndex)
).toBe(true);
expect(mockOnRowSelect.calledWith(rowKey, !selected, rowIndex)).toBe(true);
});
});
@@ -88,7 +88,8 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
disabled
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
wrapper.find('td').simulate('click');
});
@@ -111,7 +112,8 @@ describe('<SelectionCell />', () => {
mode="radio"
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
@@ -132,11 +134,12 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
selected
onRowSelect={ mockOnRowSelect }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
it('should be called with correct paramters', () => {
it('should be called with correct parameters', () => {
// first click
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(1);
@@ -151,12 +154,8 @@ describe('<SelectionCell />', () => {
beforeEach(() => {
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
/>, { bootstrap4: false }
<SelectionCell rowKey={ 1 } mode={ mode } rowIndex={ rowIndex } selected={ selected } />,
{ bootstrap4: false }
);
});
@@ -165,6 +164,7 @@ describe('<SelectionCell />', () => {
expect(wrapper.find('input')).toHaveLength(1);
expect(wrapper.find('input').get(0).props.type).toBe(mode);
expect(wrapper.find('input').get(0).props.checked).toBe(selected);
expect(toJson(wrapper)).toMatchSnapshot();
});
describe('when disabled prop give as true', () => {
@@ -176,7 +176,8 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
selected={ selected }
disabled
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
@@ -198,7 +199,8 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
selected={ selected }
selectionRenderer={ selectionRenderer }
/>, { bootstrap4: false }
/>,
{ bootstrap4: false }
);
});
@@ -219,12 +221,8 @@ describe('<SelectionCell />', () => {
describe('when bootstrap4 context is true', () => {
beforeEach(() => {
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
/>, { bootstrap4: true }
<SelectionCell rowKey={ 1 } mode={ mode } rowIndex={ rowIndex } selected={ selected } />,
{ bootstrap4: true }
);
});

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
import sinon from 'sinon';
import { shallowWithContext } from '../test-helpers/new-context';
@@ -201,6 +202,7 @@ describe('<CheckBox />', () => {
expect(wrapper.find('input').length).toBe(1);
expect(wrapper.find('input').prop('checked')).toBe(checked);
expect(wrapper.find('input').prop('type')).toBe('checkbox');
expect(toJson(wrapper)).toMatchSnapshot();
});
});
});

View File

@@ -8,7 +8,7 @@ import Const from '../src/const';
import SelectionCell from '../src//row-selection/selection-cell';
import mockBodyResolvedProps from './test-helpers/mock/body-resolved-props';
const defaultColumns = [{
let defaultColumns = [{
dataField: 'id',
text: 'ID'
}, {
@@ -31,6 +31,19 @@ describe('Row', () => {
price: 1000
};
beforeEach(() => {
defaultColumns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'price',
text: 'Price'
}];
});
describe('simplest row', () => {
beforeEach(() => {
wrapper = shallow(
@@ -502,7 +515,7 @@ describe('Row', () => {
});
});
describe('when cloumn.hidden is true', () => {
describe('when column.hidden is true', () => {
beforeEach(() => {
const newColumns = [{
dataField: 'id',
@@ -870,4 +883,445 @@ describe('Row', () => {
});
});
});
describe('when column.style prop is defined', () => {
let columns;
const columnIndex = 1;
beforeEach(() => {
columns = [...defaultColumns];
});
describe('when style is an object', () => {
beforeEach(() => {
columns[columnIndex].style = { backgroundColor: 'red' };
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.style).toEqual(columns[columnIndex].style);
});
});
describe('when style is a function', () => {
const returnStyle = { backgroundColor: 'red' };
let styleCallBack;
beforeEach(() => {
styleCallBack = sinon.stub().returns(returnStyle);
columns[columnIndex].style = styleCallBack;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { styleCallBack.reset(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.style).toEqual(returnStyle);
});
it('should call custom style function correctly', () => {
expect(styleCallBack.callCount).toBe(1);
expect(
styleCallBack.calledWith(row[columns[columnIndex].dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.classes prop is defined', () => {
let columns;
const columnIndex = 1;
beforeEach(() => {
columns = [...defaultColumns];
});
describe('when classes is an object', () => {
beforeEach(() => {
columns[columnIndex].classes = 'td-test-class';
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.className)
.toEqual(columns[columnIndex].classes);
});
});
describe('when classes is a function', () => {
const returnClasses = 'td-test-class';
let classesCallBack;
beforeEach(() => {
classesCallBack = sinon.stub().returns(returnClasses);
columns[columnIndex].classes = classesCallBack;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { classesCallBack.reset(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.className).toEqual(returnClasses);
});
it('should call custom classes function correctly', () => {
expect(classesCallBack.callCount).toBe(1);
expect(
classesCallBack.calledWith(
row[columns[columnIndex].dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.title prop is defined', () => {
let columns;
const columnIndex = 1;
beforeEach(() => {
columns = [...defaultColumns];
});
describe('when title is an string', () => {
beforeEach(() => {
columns[columnIndex].title = true;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.title)
.toEqual(row[columns[columnIndex].dataField]);
});
});
describe('when title is a function', () => {
const returnTitle = 'test title';
let titleCallBack;
beforeEach(() => {
titleCallBack = sinon.stub().returns(returnTitle);
columns[columnIndex].title = titleCallBack;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { titleCallBack.reset(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.title).toEqual(returnTitle);
});
it('should call custom title function correctly', () => {
expect(titleCallBack.callCount).toBe(1);
expect(
titleCallBack.calledWith(
row[columns[columnIndex].dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.events prop is defined', () => {
let columns;
const columnIndex = 1;
beforeEach(() => {
columns = [...defaultColumns];
columns[columnIndex].events = {
onClick: sinon.stub()
};
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should attachs DOM event successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.onClick).toBeDefined();
});
});
describe('when column.align prop is defined', () => {
let columns;
const columnIndex = 1;
beforeEach(() => {
columns = [...defaultColumns];
});
describe('when align is a string', () => {
beforeEach(() => {
columns[columnIndex].align = 'right';
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.style.textAlign)
.toEqual(columns[columnIndex].align);
});
});
describe('when align is a function', () => {
const returnAlign = 'right';
let alignCallBack;
beforeEach(() => {
alignCallBack = sinon.stub().returns(returnAlign);
columns[columnIndex].align = alignCallBack;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { alignCallBack.reset(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props.style.textAlign).toEqual(returnAlign);
});
it('should call custom align function correctly', () => {
expect(alignCallBack.callCount).toBe(1);
expect(
alignCallBack.calledWith(row[columns[columnIndex].dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
describe('when column.attrs prop is defined', () => {
let columns;
const columnIndex = 1;
beforeEach(() => {
columns = [...defaultColumns];
});
describe('when attrs is an object', () => {
it('should render Cell correctly', () => {
columns[columnIndex].attrs = {
'data-test': 'test',
title: 'title',
className: 'attrs-class',
style: {
backgroundColor: 'attrs-style-test',
display: 'none',
textAlign: 'right'
}
};
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props['data-test'])
.toEqual(columns[columnIndex].attrs['data-test']);
expect(wrapper.find(Cell).get(columnIndex).props.title)
.toEqual(columns[columnIndex].attrs.title);
expect(wrapper.find(Cell).get(columnIndex).props.className)
.toEqual(columns[columnIndex].attrs.className);
expect(wrapper.find(Cell).get(columnIndex).props.style)
.toEqual(columns[columnIndex].attrs.style);
});
describe('when column.title prop is defined', () => {
it('attrs.title should be overwrited', () => {
columns[columnIndex].title = true;
columns[columnIndex].attrs = { title: 'title' };
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
expect(wrapper.find(Cell).get(columnIndex).props.title)
.toEqual(row[columns[columnIndex].dataField]);
});
});
describe('when column.classes prop is defined', () => {
it('attrs.className should be overwrited', () => {
columns[columnIndex].classes = 'td-test-class';
columns[columnIndex].attrs = { className: 'attrs-class' };
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
expect(wrapper.find(Cell).get(columnIndex).props.className)
.toEqual(columns[columnIndex].classes);
});
});
describe('when column.style prop is defined', () => {
it('attrs.style should be overwrited', () => {
columns[columnIndex].style = { backgroundColor: 'red' };
columns[columnIndex].attrs = { style: { backgroundColor: 'attrs-style-test' } };
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
expect(wrapper.find(Cell).get(columnIndex).props.style)
.toEqual(columns[columnIndex].style);
});
});
describe('when column.align prop is defined', () => {
it('attrs.style.textAlign should be overwrited', () => {
columns[columnIndex].align = 'center';
columns[columnIndex].attrs = { style: { textAlign: 'right' } };
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
expect(wrapper.find(Cell).get(columnIndex).props.style.textAlign)
.toEqual(columns[columnIndex].align);
});
});
});
describe('when attrs is custom function', () => {
let attrsCallBack;
const customAttrs = {
'data-test': 'test',
title: 'title'
};
beforeEach(() => {
attrsCallBack = sinon.stub().returns(customAttrs);
columns[columnIndex].attrs = attrsCallBack;
wrapper = shallow(
<Row
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render style.attrs correctly', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(Cell).get(columnIndex).props['data-test'])
.toEqual(customAttrs['data-test']);
expect(wrapper.find(Cell).get(columnIndex).props.title)
.toEqual(customAttrs.title);
});
it('should call custom attrs function correctly', () => {
expect(attrsCallBack.callCount).toBe(1);
expect(
attrsCallBack.calledWith(row[columns[columnIndex].dataField], row, rowIndex, columnIndex)
).toBe(true);
});
});
});
});

View File

@@ -58,32 +58,6 @@ describe('Utils', () => {
});
});
describe('isObject', () => {
describe('when given Object', () => {
it('should return true', () => {
expect(_.isObject({})).toBe(true);
});
});
describe('when given Function', () => {
it('should return false', () => {
expect(_.isObject(() => 'test')).toBe(false);
});
});
describe('when given Array', () => {
it('should return false', () => {
expect(_.isObject([])).toBe(false);
});
});
describe('when given null', () => {
it('should return false', () => {
expect(_.isObject(null)).toBe(false);
});
});
});
describe('isEmptyObject', () => {
describe('when given empty Object', () => {
it('should return true', () => {
@@ -98,14 +72,14 @@ describe('Utils', () => {
});
describe('when given Function', () => {
it('should return false', () => {
expect(_.isEmptyObject(() => 'test')).toBe(false);
it('should return true', () => {
expect(_.isEmptyObject(() => 'test')).toBe(true);
});
});
describe('when given Array', () => {
it('should return false', () => {
expect(_.isEmptyObject([])).toBe(false);
it('should return true', () => {
expect(_.isEmptyObject([])).toBe(true);
});
});

View File

@@ -2711,6 +2711,12 @@ enzyme-adapter-utils@^1.3.0:
object.assign "^4.1.0"
prop-types "^15.6.0"
enzyme-to-json@3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.3.4.tgz#67c6040e931182f183418af2eb9f4323258aa77f"
dependencies:
lodash "^4.17.4"
enzyme@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.3.0.tgz#0971abd167f2d4bf3f5bd508229e1c4b6dc50479"
@@ -8286,6 +8292,10 @@ unc-path-regex@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
underscore@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
underscore@~1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"