diff --git a/docs/columns.md b/docs/columns.md
index 89c62cf..330b7ad 100644
--- a/docs/columns.md
+++ b/docs/columns.md
@@ -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.
+
## column.headerFormatter - [Function]
`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.
diff --git a/package.json b/package.json
index 5cb8003..ae5416b 100644
--- a/package.json
+++ b/package.json
@@ -82,7 +82,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": [
diff --git a/packages/react-bootstrap-table2-example/examples/basic/large-table.js b/packages/react-bootstrap-table2-example/examples/basic/large-table.js
new file mode 100644
index 0000000..671aaa5
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/basic/large-table.js
@@ -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 () => (
+
+
+
+);
diff --git a/packages/react-bootstrap-table2-example/examples/columns/column-attrs-table.js b/packages/react-bootstrap-table2-example/examples/columns/column-attrs-table.js
index 7e7bfcb..5fa6fcf 100644
--- a/packages/react-bootstrap-table2-example/examples/columns/column-attrs-table.js
+++ b/packages/react-bootstrap-table2-example/examples/columns/column-attrs-table.js
@@ -41,7 +41,7 @@ const columns = [{
export default () => (
-
Try to hover on Product Name header column
+ Try to hover on Product ID Cell
{ sourceCode }
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index eee2c87..24db59c 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -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', () => )
.add('Indication For Empty Table', () => )
.add('Customized id and class table', () => )
- .add('Table with caption', () => );
+ .add('Table with caption', () => )
+ .add('Large Table', () => );
storiesOf('Work on Columns', module)
.add('Display Nested Data', () => )
diff --git a/packages/react-bootstrap-table2/src/cell.js b/packages/react-bootstrap-table2/src/cell.js
index bfd61d5..db32d05 100644
--- a/packages/react-bootstrap-table2/src/cell.js
+++ b/packages/react-bootstrap-table2/src/cell.js
@@ -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 (
-
+ |
{ typeof content === 'boolean' ? `${content}` : content }
|
);
diff --git a/packages/react-bootstrap-table2/src/row.js b/packages/react-bootstrap-table2/src/row.js
index 1196716..84a3ea4 100644
--- a/packages/react-bootstrap-table2/src/row.js
+++ b/packages/react-bootstrap-table2/src/row.js
@@ -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 = 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 (
|
);
}
diff --git a/packages/react-bootstrap-table2/src/utils.js b/packages/react-bootstrap-table2/src/utils.js
index 7d6e218..ac8dd5a 100644
--- a/packages/react-bootstrap-table2/src/utils.js
+++ b/packages/react-bootstrap-table2/src/utils.js
@@ -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);
@@ -96,13 +83,4 @@ function debounce(func, wait, immediate) {
};
}
-export default {
- get,
- set,
- isFunction,
- isObject,
- isEmptyObject,
- isDefined,
- sleep,
- debounce
-};
+export default Object.assign(_, { get, set, isDefined, isEmptyObject, sleep, debounce });
diff --git a/packages/react-bootstrap-table2/test/cell.test.js b/packages/react-bootstrap-table2/test/cell.test.js
index 1695240..86bf393 100644
--- a/packages/react-bootstrap-table2/test/cell.test.js
+++ b/packages/react-bootstrap-table2/test/cell.test.js
@@ -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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
- });
-
- 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(
- | );
-
- 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(
- | );
-
- 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(
- | );
-
- 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(
- | );
-
- 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(
- | );
-
- 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(
- | );
- });
-
- 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ 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(
+ | );
+ });
+
+ it('should return true', () => {
+ nextProps = { ...props, column: { ...column, attrs: null } };
+ expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
+ });
+ });
+ });
});
diff --git a/packages/react-bootstrap-table2/test/row.test.js b/packages/react-bootstrap-table2/test/row.test.js
index 117a24f..621f140 100644
--- a/packages/react-bootstrap-table2/test/row.test.js
+++ b/packages/react-bootstrap-table2/test/row.test.js
@@ -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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+ });
+
+ 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(
+
+ );
+
+ 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(
+
+ );
+
+ 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(
+
+ );
+
+ 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(
+
+ );
+
+ 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(
+
+ );
+
+ 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(
+
+ );
+ });
+
+ 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);
+ });
+ });
+ });
});
diff --git a/packages/react-bootstrap-table2/test/utils.test.js b/packages/react-bootstrap-table2/test/utils.test.js
index 2aa43b1..89d9ff1 100644
--- a/packages/react-bootstrap-table2/test/utils.test.js
+++ b/packages/react-bootstrap-table2/test/utils.test.js
@@ -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);
});
});
diff --git a/yarn.lock b/yarn.lock
index ff2fc0d..4329d62 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -8286,6 +8286,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"