Compare commits

...

53 Commits

Author SHA1 Message Date
AllenFang
b1e5c0cb20 Publish
- react-bootstrap-table2-editor@1.2.0
 - react-bootstrap-table2-example@1.0.7
 - react-bootstrap-table2-filter@1.0.1
 - react-bootstrap-table-next@1.3.0
2018-10-14 23:49:57 +08:00
Allen
7ee38a647f Merge pull request #608 from react-bootstrap-table/develop
20181014 release
2018-10-14 23:45:50 +08:00
AllenFang
01ec19344d patch docs and proptype for selectRow.clickToExpand 2018-10-14 16:04:04 +08:00
AllenFang
19be67c914 fix #599 2018-10-14 15:07:30 +08:00
Allen
ae4d38cae6 Merge pull request #607 from react-bootstrap-table/enhance/598
Enhance/598
2018-10-14 14:49:43 +08:00
AllenFang
81a6428a03 patch docs for #598 2018-10-14 14:44:19 +08:00
AllenFang
166affc4c1 add story for #598 2018-10-14 14:44:19 +08:00
AllenFang
d0fb46e39f patch test for #598 2018-10-14 14:41:14 +08:00
AllenFang
828844a1e9 fix #598 2018-10-14 14:40:53 +08:00
Allen
774293b76d Merge pull request #497 from react-bootstrap-table/refactor/selection-consumer
Row Refactoring with Consumer and perf improvement
2018-10-14 13:38:28 +08:00
Benjamin Cavy
e77cbdb2df Add MULTISELECT filter type in filter doc (#606) 2018-10-14 13:32:36 +08:00
AllenFang
ef2f828572 patch docs and story for #546 2018-10-11 00:08:31 +08:00
AllenFang
15731932cf fix #546 2018-10-11 00:08:13 +08:00
AllenFang
dd54294382 refine large table example 2018-10-10 23:54:11 +08:00
AllenFang
185c184f01 upgrade react 2018-10-09 00:18:17 +08:00
AllenFang
d45345ed10 binder -> consumer 2018-10-09 00:18:17 +08:00
AllenFang
dda8460017 implement selectRow.clickToExpand 2018-10-09 00:18:17 +08:00
AllenFang
6735536fd8 refactoring cell edit consumer 2018-10-09 00:18:17 +08:00
AllenFang
95623bbb5f fix selection header checkbox will be checked when table data is empty 2018-10-09 00:18:17 +08:00
AllenFang
9567c7829d refactoring shouldComponentUpdate for aggreate and simple row for cell editing 2018-10-09 00:18:17 +08:00
AllenFang
8499991c41 fix cell edit broken 2018-10-09 00:18:17 +08:00
AllenFang
1e76ca9bdb fix row-pure-content will not update in some case 2018-10-09 00:18:17 +08:00
AllenFang
fa13550d8c patch test for row selection consumer refactoring 2018-10-09 00:18:17 +08:00
AllenFang
709d59ce62 patch tests for row refactoring 2018-10-09 00:18:16 +08:00
AllenFang
66329ecdbf implement selection consumers 2018-10-09 00:18:16 +08:00
AllenFang
ee6cec5a2d refactoring row level components 2018-10-09 00:18:16 +08:00
AllenFang
52fc84899b fixed wrong conflict fixs for #514 2018-10-09 00:18:16 +08:00
AllenFang
8c10867b8c fix cell level performace, remain select all 2018-10-09 00:18:16 +08:00
AllenFang
640ada7659 patch tests for refactoring cell edit with consumer 2018-10-09 00:18:16 +08:00
AllenFang
73a5c34535 add story for dbclick to edit with row selection 2018-10-09 00:18:16 +08:00
AllenFang
2879cf891e refactoring cell edit consumer 2018-10-09 00:18:16 +08:00
AllenFang
994ed2e395 no more expand row props resolver 2018-10-09 00:18:16 +08:00
AllenFang
4b790e4bec patch test for refining expand row consumer 2018-10-09 00:18:16 +08:00
AllenFang
21e7c3a53a refine expand row consumer 2018-10-09 00:18:16 +08:00
AllenFang
6fce0d7066 add story for combine selection and expansion 2018-10-09 00:18:16 +08:00
AllenFang
154f1c91c3 upgrade enzyme for new context API 2018-10-09 00:18:16 +08:00
AllenFang
02d78e5104 patch tests for refactoring row component with selection 2018-10-09 00:18:16 +08:00
AllenFang
41cc6b01af patch row event example 2018-10-09 00:18:16 +08:00
AllenFang
bd410e7303 refine selection consumer 2018-10-09 00:18:16 +08:00
Allen
eced3eef1f Merge pull request #597 from react-bootstrap-table/feature/introduce-logo-to-storybook
Feature/introduce logo to storybook
2018-10-09 00:16:48 +08:00
Chun-MingChen
ca5a41a8b3 Change primary of github avatar to main color of logo 2018-10-08 00:19:29 +08:00
Chun-MingChen
7a31729ebb Introduce logo to <Welcome /> 2018-10-08 00:19:29 +08:00
Chun-MingChen
3a8faf8170 Add favicon 2018-10-07 23:57:16 +08:00
AllenFang
532581bb6e Publish
- react-bootstrap-table2-example@1.0.6
 - react-bootstrap-table2-paginator@1.0.3
 - react-bootstrap-table2-toolkit@1.1.1
 - react-bootstrap-table-next@1.2.1
2018-10-07 23:33:25 +08:00
Allen
c228b229d2 Merge pull request #595 from react-bootstrap-table/develop
20181007 release
2018-10-07 23:31:56 +08:00
AllenFang
10adbf472c fix no-console 2018-10-07 16:24:15 +08:00
Allen
a6e2f0f8f8 fix #589 (#594) 2018-10-06 23:42:55 +08:00
Allen
f1d93853ec Merge pull request #593 from react-bootstrap-table/feat/587
Feat/587
2018-10-06 22:46:43 +08:00
AllenFang
bb7243c5db patch docs for column.sortCaret 2018-10-06 18:15:21 +08:00
AllenFang
3ea816b2e6 add story for #587 2018-10-06 18:15:21 +08:00
AllenFang
b268c4e0cd fix #587 2018-10-06 18:15:21 +08:00
Allen
8b8f336878 fix #585 (#592) 2018-10-06 17:29:24 +08:00
Allen
8517248aee fix #588 (#591) 2018-10-06 16:29:08 +08:00
80 changed files with 4178 additions and 2791 deletions

View File

@@ -17,6 +17,7 @@
* [hover](#hover) * [hover](#hover)
* [condensed](#condensed) * [condensed](#condensed)
* [id](#id) * [id](#id)
* [tabIndexCell](#tabIndexCell)
* [classes](#classes) * [classes](#classes)
* [wrapperClasses](#wrapperClasses) * [wrapperClasses](#wrapperClasses)
* [headerClasses](#headerClasses) * [headerClasses](#headerClasses)
@@ -112,6 +113,10 @@ Same as bootstrap `.table-condensed` class for making a table more compact by cu
### <a name='id'>id - [String]</a> ### <a name='id'>id - [String]</a>
Customize id on `table` element. Customize id on `table` element.
### <a name='tabIndexCell'>tabIndexCell - [Bool]</a>
Enable the `tabIndex` attribute on `<td>` element.
### <a name='classes'>classes - [String]</a> ### <a name='classes'>classes - [String]</a>
Customize class on `table` element. Customize class on `table` element.

View File

@@ -13,6 +13,7 @@ Available properties in a column object:
* [formatExtraData](#formatExtraData) * [formatExtraData](#formatExtraData)
* [sort](#sort) * [sort](#sort)
* [sortFunc](#sortFunc) * [sortFunc](#sortFunc)
* [sortCaret](#sortCaret)
* [onSort](#onSort) * [onSort](#onSort)
* [classes](#classes) * [classes](#classes)
* [style](#style) * [style](#style)
@@ -154,6 +155,20 @@ Enable the column sort via a `true` value given.
} }
``` ```
## <a name='sortCaret'>column.sortCaret - [Function]</a>
Use`column.sortCaret` to custom the sort caret. This callback function accept two arguments: `order` and `column`
```js
{
// omit...
sort: true,
sortCaret: (order, column) => {
return //...
}
}
```
> The possible value of `order` argument is **`asc`**, **`desc`** and **`undefined`**.
## <a name='classes'>column.classes - [String | Function]</a> ## <a name='classes'>column.classes - [String | Function]</a>
It's available to have custom class on table column: It's available to have custom class on table column:

View File

@@ -14,6 +14,7 @@
* [onExpandAll](#onExpandAll) * [onExpandAll](#onExpandAll)
* [showExpandColumn](#showExpandColumn) * [showExpandColumn](#showExpandColumn)
* [onlyOneExpanding](#onlyOneExpanding) * [onlyOneExpanding](#onlyOneExpanding)
* [expandByColumnOnly](#expandByColumnOnly)
* [expandColumnRenderer](#expandColumnRenderer) * [expandColumnRenderer](#expandColumnRenderer)
* [expandHeaderColumnRenderer](#expandHeaderColumnRenderer) * [expandHeaderColumnRenderer](#expandHeaderColumnRenderer)
@@ -138,3 +139,14 @@ const expandRow = {
onlyOneExpanding: true onlyOneExpanding: true
}; };
``` ```
### <a name='expandByColumnOnly'>expandRow.expandByColumnOnly - [Bool]</a>
Default is `false`. If you want to restrict user to expand/collapse row via clicking the expand column only, you can enable it.
```js
const expandRow = {
renderer: (row) => ...,
showExpandColumn: true,
expandByColumnOnly: true
};
```

View File

@@ -12,6 +12,7 @@
* [bgColor](#bgColor) * [bgColor](#bgColor)
* [nonSelectable)](#nonSelectable) * [nonSelectable)](#nonSelectable)
* [clickToSelect)](#clickToSelect) * [clickToSelect)](#clickToSelect)
* [clickToExpand)](#clickToExpand)
* [clickToEdit](#clickToEdit) * [clickToEdit](#clickToEdit)
* [onSelect](#onSelect) * [onSelect](#onSelect)
* [onSelectAll](#onSelectAll) * [onSelectAll](#onSelectAll)
@@ -148,6 +149,16 @@ const selectRow = {
> Note: When you also enable [cellEdit](./cell-edit.md), the `selectRow.clickToSelect` will deactivate the functionality of cell editing > Note: When you also enable [cellEdit](./cell-edit.md), the `selectRow.clickToSelect` will deactivate the functionality of cell editing
> If you want to click on row to select row and edit cell simultaneously, you are suppose to enable [`selectRow.clickToEdit`](#clickToEdit) > If you want to click on row to select row and edit cell simultaneously, you are suppose to enable [`selectRow.clickToEdit`](#clickToEdit)
### <a name='clickToExpand'>selectRow.clickToExpand - [Bool]</a>
Default is false, enable it will let user able to expand and select row when user clicking on the row.
```js
const selectRow = {
mode: 'checkbox',
clickToExpand: true
};
```
### <a name='clickToEdit'>selectRow.clickToEdit - [Bool]</a> ### <a name='clickToEdit'>selectRow.clickToEdit - [Bool]</a>
Able to click to edit cell and select row Able to click to edit cell and select row

View File

@@ -1,4 +1,4 @@
import Adapter from 'enzyme-adapter-react-16'; import Adapter from 'enzyme-adapter-react-16.3';
import { configure } from 'enzyme'; import { configure } from 'enzyme';
const configureEnzyme = () => { const configureEnzyme = () => {

View File

@@ -50,8 +50,8 @@
"babel-preset-stage-0": "6.24.1", "babel-preset-stage-0": "6.24.1",
"babel-register": "6.24.1", "babel-register": "6.24.1",
"css-loader": "0.28.1", "css-loader": "0.28.1",
"enzyme": "3.3.0", "enzyme": "3.4.0",
"enzyme-adapter-react-16": "1.1.1", "enzyme-adapter-react-16.3": "1.0.0",
"enzyme-to-json": "3.3.4", "enzyme-to-json": "3.3.4",
"eslint": "4.5.0", "eslint": "4.5.0",
"eslint-config-airbnb": "15.1.0", "eslint-config-airbnb": "15.1.0",
@@ -82,8 +82,8 @@
"dependencies": { "dependencies": {
"classnames": "2.2.5", "classnames": "2.2.5",
"prop-types": "15.5.10", "prop-types": "15.5.10",
"react": "16.3.2", "react": "16.4.0",
"react-dom": "16.3.2", "react-dom": "16.4.0",
"underscore": "1.9.1" "underscore": "1.9.1"
}, },
"jest": { "jest": {

View File

@@ -1,16 +1,16 @@
import createContext from './src/context'; import createContext from './src/context';
import editingCellFactory from './src/editing-cell'; import withRowLevelCellEdit from './src/row-consumer';
import createEditingCell from './src/editing-cell-consumer';
import { import {
EDITTYPE, EDITTYPE,
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK DELAY_FOR_DBCLICK
} from './src/const'; } from './src/const';
export default (options = {}) => ({ export default (options = {}) => ({
createContext, createContext,
editingCellFactory, createEditingCell,
CLICK_TO_CELL_EDIT, withRowLevelCellEdit,
DBCLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK, DELAY_FOR_DBCLICK,
options options

View File

@@ -1,6 +1,6 @@
{ {
"name": "react-bootstrap-table2-editor", "name": "react-bootstrap-table2-editor",
"version": "1.1.0", "version": "1.2.0",
"description": "it's the editor addon for react-bootstrap-table2", "description": "it's the editor addon for react-bootstrap-table2",
"main": "./lib/index.js", "main": "./lib/index.js",
"scripts": { "scripts": {

View File

@@ -4,15 +4,14 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const'; import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const';
const CellEditContext = React.createContext();
export default ( export default (
_, _,
dataOperator, dataOperator,
isRemoteCellEdit, isRemoteCellEdit,
handleCellChange handleCellChange
) => { ) => {
let EditingCell;
const CellEditContext = React.createContext();
class CellEditProvider extends React.Component { class CellEditProvider extends React.Component {
static propTypes = { static propTypes = {
data: PropTypes.array.isRequired, data: PropTypes.array.isRequired,
@@ -32,7 +31,6 @@ export default (
constructor(props) { constructor(props) {
super(props); super(props);
EditingCell = props.cellEdit.editingCellFactory(_, props.cellEdit.options.onStartEdit);
this.startEditing = this.startEditing.bind(this); this.startEditing = this.startEditing.bind(this);
this.escapeEditing = this.escapeEditing.bind(this); this.escapeEditing = this.escapeEditing.bind(this);
this.completeEditing = this.completeEditing.bind(this); this.completeEditing = this.completeEditing.bind(this);
@@ -102,8 +100,6 @@ export default (
const { const {
cellEdit: { cellEdit: {
options: { nonEditableRows, errorMessage, ...optionsRest }, options: { nonEditableRows, errorMessage, ...optionsRest },
editingCellFactory,
createContext,
...cellEditRest ...cellEditRest
} }
} = this.props; } = this.props;
@@ -112,7 +108,6 @@ export default (
...optionsRest, ...optionsRest,
...cellEditRest, ...cellEditRest,
...this.state, ...this.state,
EditingCell,
nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [], nonEditableRows: _.isDefined(nonEditableRows) ? nonEditableRows() : [],
onStart: this.startEditing, onStart: this.startEditing,
onEscape: this.escapeEditing, onEscape: this.escapeEditing,
@@ -121,7 +116,7 @@ export default (
return ( return (
<CellEditContext.Provider <CellEditContext.Provider
value={ { cellEdit: newCellEdit } } value={ { ...newCellEdit } }
> >
{ this.props.children } { this.props.children }
</CellEditContext.Provider> </CellEditContext.Provider>
@@ -129,7 +124,8 @@ export default (
} }
} }
return { return {
Provider: CellEditProvider, Provider: CellEditProvider
Consumer: CellEditContext.Consumer
}; };
}; };
export const Consumer = CellEditContext.Consumer;

View File

@@ -0,0 +1,44 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import { Consumer } from './context';
import createEditingCell from './editing-cell';
export default (_, onStartEdit) => {
const EditingCell = createEditingCell(_, onStartEdit);
const renderWithEditingCell = (props, cellEdit) => {
const content = _.get(props.row, props.column.dataField);
let editCellstyle = props.column.editCellStyle || {};
let editCellclasses = props.column.editCellClasses;
if (_.isFunction(props.column.editCellStyle)) {
editCellstyle = props.column.editCellStyle(
content,
props.row,
props.rowIndex,
props.columnIndex
);
}
if (_.isFunction(props.column.editCellClasses)) {
editCellclasses = props.column.editCellClasses(
content,
props.row,
props.rowIndex,
props.columnIndex)
;
}
return (
<EditingCell
{ ...props }
className={ editCellclasses }
style={ editCellstyle }
{ ...cellEdit }
/>
);
};
return props => (
<Consumer>
{ cellEdit => renderWithEditingCell(props, cellEdit) }
</Consumer>
);
};

View File

@@ -0,0 +1,43 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import { DELAY_FOR_DBCLICK, DBCLICK_TO_CELL_EDIT, CLICK_TO_CELL_EDIT } from './const';
import { Consumer } from './context';
export default (Component, selectRowEnabled) => {
const renderWithCellEdit = (props, cellEdit) => {
const key = props.value;
const editableRow = !(
cellEdit.nonEditableRows.length > 0 &&
cellEdit.nonEditableRows.indexOf(key) > -1
);
const attrs = {};
if (selectRowEnabled && cellEdit.mode === DBCLICK_TO_CELL_EDIT) {
attrs.DELAY_FOR_DBCLICK = DELAY_FOR_DBCLICK;
}
return (
<Component
{ ...props }
{ ...attrs }
editingRowIdx={ cellEdit.ridx }
editingColIdx={ cellEdit.cidx }
editable={ editableRow }
onStart={ cellEdit.onStart }
clickToEdit={ cellEdit.mode === CLICK_TO_CELL_EDIT }
dbclickToEdit={ cellEdit.mode === DBCLICK_TO_CELL_EDIT }
/>
);
};
function withConsumer(props) {
return (
<Consumer>
{ cellEdit => renderWithCellEdit(props, cellEdit) }
</Consumer>
);
}
withConsumer.displayName = 'WithCellEditingRowConsumer';
return withConsumer;
};

View File

@@ -3,14 +3,13 @@ import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils'; import _ from 'react-bootstrap-table-next/src/utils';
import dataOperator from 'react-bootstrap-table-next/src/store/operators'; import dataOperator from 'react-bootstrap-table-next/src/store/operators';
import BootstrapTable from 'react-bootstrap-table-next/src/bootstrap-table';
import { import {
CLICK_TO_CELL_EDIT, CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK DELAY_FOR_DBCLICK
} from '../src/const'; } from '../src/const';
import createCellEditContext from '../src/context'; import createCellEditContext, { Consumer } from '../src/context';
import cellEditFactory from '../index'; import cellEditFactory from '../index';
describe('CellEditContext', () => { describe('CellEditContext', () => {
@@ -42,14 +41,7 @@ describe('CellEditContext', () => {
const defaultSelectRow = undefined; const defaultSelectRow = undefined;
const mockBase = jest.fn((props => ( const mockBase = jest.fn((() => null));
<BootstrapTable
data={ data }
columns={ columns }
keyField={ keyField }
{ ...props }
/>
)));
const handleCellChange = jest.fn(); const handleCellChange = jest.fn();
@@ -75,11 +67,11 @@ describe('CellEditContext', () => {
selectRow={ selectRow } selectRow={ selectRow }
data={ data } data={ data }
> >
<CellEditContext.Consumer> <Consumer>
{ {
cellEditProps => mockBase(cellEditProps) cellEditProps => mockBase(cellEditProps)
} }
</CellEditContext.Consumer> </Consumer>
</CellEditContext.Provider> </CellEditContext.Provider>
); );
} }
@@ -94,10 +86,6 @@ describe('CellEditContext', () => {
expect(CellEditContext.Provider).toBeDefined(); expect(CellEditContext.Provider).toBeDefined();
}); });
it('should have correct Consumer property after calling createCellEditContext', () => {
expect(CellEditContext.Consumer).toBeDefined();
});
it('should have correct state.ridx', () => { it('should have correct state.ridx', () => {
expect(wrapper.state().ridx).toBeNull(); expect(wrapper.state().ridx).toBeNull();
}); });
@@ -113,14 +101,11 @@ describe('CellEditContext', () => {
it('should pass correct cell editing props to children element', () => { it('should pass correct cell editing props to children element', () => {
expect(wrapper.length).toBe(1); expect(wrapper.length).toBe(1);
expect(JSON.stringify(mockBase.mock.calls[0])).toEqual(JSON.stringify([{ expect(JSON.stringify(mockBase.mock.calls[0])).toEqual(JSON.stringify([{
cellEdit: {
...defaultCellEdit, ...defaultCellEdit,
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK, DELAY_FOR_DBCLICK,
...wrapper.state(), ...wrapper.state(),
nonEditableRows: [] nonEditableRows: []
}
}])); }]));
}); });
}); });

View File

@@ -0,0 +1,147 @@
import 'jsdom-global/register';
import React from 'react';
import { mount, shallow } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils';
import cellEditFactory from '..';
import { CLICK_TO_CELL_EDIT } from '../src/const';
import createCellEditContext from '../src/context';
import bindEditingCell from '../src/editing-cell-consumer';
describe('Editing Cell Consumer', () => {
let wrapper;
let cellEdit;
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}];
let columns;
const rowIndex = 1;
const row = { id: 1, name: 'A' };
const keyField = 'id';
const columnIndex = 1;
const { Provider } = createCellEditContext(_);
const WithCellEditComponent = bindEditingCell(_);
beforeEach(() => {
columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}];
});
describe('if column.editCellClasses is defined as string', () => {
beforeEach(() => {
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT });
columns[1].editCellClasses = 'test-class-1';
wrapper = shallow(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent
row={ row }
column={ columns[1] }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
/>
</Provider>
);
wrapper = wrapper.render();
});
it('should inject className target component correctly', () => {
expect(wrapper.hasClass(`${columns[1].editCellClasses}`)).toBeTruthy();
});
});
describe('if column.editCellStyle is defined as object', () => {
beforeEach(() => {
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT });
columns[1].editCellStyle = { color: 'pink' };
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent
row={ row }
column={ columns[1] }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
/>
</Provider>
);
});
it('should inject style target component correctly', () => {
expect(wrapper.find('.react-bootstrap-table-editing-cell').prop('style')).toEqual(columns[1].editCellStyle);
});
});
describe('if column.editCellClasses is defined as function', () => {
const className = 'test-class-1';
beforeEach(() => {
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT });
columns[1].editCellClasses = jest.fn().mockReturnValue(className);
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent
row={ row }
column={ columns[1] }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
/>
</Provider>
);
});
it('should inject empty className and style to target component', () => {
expect(wrapper.find(className)).toBeTruthy();
});
it('should call column.editCellClasses function correctly', () => {
expect(columns[1].editCellClasses).toHaveBeenCalledTimes(1);
expect(columns[1].editCellClasses).toHaveBeenCalledWith(
_.get(row, columns[1].dataField),
row,
rowIndex,
columnIndex
);
});
});
describe('if column.editCellStyle is defined as function', () => {
const style = { color: 'blue' };
beforeEach(() => {
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT });
columns[1].editCellStyle = jest.fn().mockReturnValue(style);
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent
row={ row }
column={ columns[1] }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
/>
</Provider>
);
});
it('should inject style target component correctly', () => {
expect(wrapper.find('.react-bootstrap-table-editing-cell').prop('style')).toEqual(style);
});
it('should call column.editCellStyle function correctly', () => {
expect(columns[1].editCellStyle).toHaveBeenCalledTimes(1);
expect(columns[1].editCellStyle).toHaveBeenCalledWith(
_.get(row, columns[1].dataField),
row,
rowIndex,
columnIndex
);
});
});
});

View File

@@ -0,0 +1,138 @@
import 'jsdom-global/register';
import React from 'react';
import { mount } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils';
import op from 'react-bootstrap-table-next/src/store/operators';
import cellEditFactory from '..';
import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT, DELAY_FOR_DBCLICK } from '../src/const';
import createCellEditContext from '../src/context';
import withRowLevelCellEdit from '../src/row-consumer';
describe('Row Consumer', () => {
let wrapper;
let cellEdit;
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}];
const row = { id: 1, name: 'A' };
const keyField = 'id';
const value = _.get(row, keyField);
const { Provider } = createCellEditContext(_, op, false, jest.fn());
const BaseComponent = () => null;
describe('if cellEdit.nonEditableRows is undefined', () => {
beforeEach(() => {
const WithCellEditComponent = withRowLevelCellEdit(
props => <BaseComponent { ...props } />,
false
);
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT });
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent value={ value } />
</Provider>
);
});
it('should inject correct props to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('editingRowIdx')).toBeNull();
expect(wrapper.find(BaseComponent).prop('editingColIdx')).toBeNull();
expect(wrapper.find(BaseComponent).prop('editable')).toBeTruthy();
});
});
describe('if cellEdit.nonEditableRows is defined', () => {
const nonEditableRows = jest.fn().mockReturnValue([value]);
describe('if value prop is match in one of cellEdit.nonEditableRows', () => {
beforeEach(() => {
const WithCellEditComponent = withRowLevelCellEdit(
props => <BaseComponent { ...props } />,
false
);
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT, nonEditableRows });
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent value={ value } />
</Provider>
);
});
it('should inject correct editable prop as false to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('editable')).toBeFalsy();
});
});
describe('if value prop is not match in one of cellEdit.nonEditableRows', () => {
beforeEach(() => {
const WithCellEditComponent = withRowLevelCellEdit(
props => <BaseComponent { ...props } />,
false
);
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT, nonEditableRows });
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent value={ 2 } />
</Provider>
);
});
it('should inject correct editable prop as false to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('editable')).toBeTruthy();
});
});
});
describe(`if selectRowEnabled argument is true and cellEdit.mode is ${DBCLICK_TO_CELL_EDIT}`, () => {
beforeEach(() => {
const WithCellEditComponent = withRowLevelCellEdit(
props => <BaseComponent { ...props } />,
true
);
cellEdit = cellEditFactory({ mode: DBCLICK_TO_CELL_EDIT });
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent value={ value } />
</Provider>
);
});
it('should inject correct DELAY_FOR_DBCLICK prop to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('DELAY_FOR_DBCLICK')).toEqual(DELAY_FOR_DBCLICK);
});
});
describe('if cellEdit.ridx and cellEdit.cidx are defined', () => {
const ridx = 0;
const cidx = 1;
beforeEach(() => {
const WithCellEditComponent = withRowLevelCellEdit(
props => <BaseComponent { ...props } />,
false
);
cellEdit = cellEditFactory({ mode: CLICK_TO_CELL_EDIT });
wrapper = mount(
<Provider data={ data } keyField={ keyField } cellEdit={ cellEdit }>
<WithCellEditComponent value={ value } />
</Provider>
);
wrapper.instance().startEditing(ridx, cidx);
wrapper.update();
});
it('should inject correct editable prop as false to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('editingRowIdx')).toEqual(ridx);
expect(wrapper.find(BaseComponent).prop('editingColIdx')).toEqual(cidx);
});
});
});

View File

@@ -0,0 +1,182 @@
/* eslint no-return-assign: 0 */
/* eslint no-console: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
import filterFactory, { textFilter } from 'react-bootstrap-table2-filter';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(63);
const columns = [{
dataField: 'id',
text: 'Product ID',
sort: true
}, {
dataField: 'name',
text: 'Product Name',
sort: true,
filter: textFilter()
}, {
dataField: 'price',
text: 'Product Price',
sort: true,
filter: textFilter()
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
class ExposedFunctionTable extends React.Component {
handleGetCurrentData = () => {
console.log(this.node.table.props.data);
}
handleGetSelectedData = () => {
console.log(this.node.selectionContext.state.selected);
}
handleGetExpandedData = () => {
console.log(this.node.rowExpandContext.state.expanded);
}
handleGetCurrentPage = () => {
console.log(this.node.paginationContext.currPage);
}
handleGetCurrentSizePerPage = () => {
console.log(this.node.paginationContext.currSizePerPage);
}
handleGetCurrentSortColumn = () => {
console.log(this.node.sortContext.state.sortColumn);
}
handleGetCurrentSortOrder = () => {
console.log(this.node.sortContext.state.sortOrder);
}
handleGetCurrentFilter = () => {
console.log(this.node.filterContext.currFilters);
}
render() {
const expandRow = {
renderer: row => (
<div>
<p>.....</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true
};
return (
<div>
<button className="btn btn-default" onClick={ this.handleGetCurrentData }>Get Current Display Rows</button>
<button className="btn btn-default" onClick={ this.handleGetSelectedData }>Get Current Selected Rows</button>
<button className="btn btn-default" onClick={ this.handleGetExpandedData }>Get Current Expanded Rows</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentPage }>Get Current Page</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentSizePerPage }>Get Current Size Per Page</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentSortColumn }>Get Current Sort Column</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentSortOrder }>Get Current Sort Order</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentFilter }>Get Current Filter Information</button>
<BootstrapTable
ref={ n => this.node = n }
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
pagination={ paginationFactory() }
selectRow={ { mode: 'checkbox', clickToSelect: true } }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);
}
}
`;
export default class ExposedFunctionTable extends React.Component {
handleGetCurrentData = () => {
console.log(this.node.table.props.data);
}
handleGetSelectedData = () => {
console.log(this.node.selectionContext.state.selected);
}
handleGetExpandedData = () => {
console.log(this.node.rowExpandContext.state.expanded);
}
handleGetCurrentPage = () => {
console.log(this.node.paginationContext.currPage);
}
handleGetCurrentSizePerPage = () => {
console.log(this.node.paginationContext.currSizePerPage);
}
handleGetCurrentSortColumn = () => {
console.log(this.node.sortContext.state.sortColumn);
}
handleGetCurrentSortOrder = () => {
console.log(this.node.sortContext.state.sortOrder);
}
handleGetCurrentFilter = () => {
console.log(this.node.filterContext.currFilters);
}
render() {
const expandRow = {
renderer: row => (
<div>
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true
};
return (
<div>
<button className="btn btn-default" onClick={ this.handleGetCurrentData }>Get Current Display Rows</button>
<button className="btn btn-default" onClick={ this.handleGetSelectedData }>Get Current Selected Rows</button>
<button className="btn btn-default" onClick={ this.handleGetExpandedData }>Get Current Expanded Rows</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentPage }>Get Current Page</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentSizePerPage }>Get Current Size Per Page</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentSortColumn }>Get Current Sort Column</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentSortOrder }>Get Current Sort Order</button>
<button className="btn btn-default" onClick={ this.handleGetCurrentFilter }>Get Current Filter Information</button>
<BootstrapTable
ref={ n => this.node = n }
keyField="id"
data={ products }
columns={ columns }
filter={ filterFactory() }
pagination={ paginationFactory() }
selectRow={ { mode: 'checkbox', clickToSelect: true } }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);
}
}

View File

@@ -1,10 +1,9 @@
import React from 'react'; import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import { productsGenerator } from 'utils/common'; import { productsGenerator } from 'utils/common';
const products = productsGenerator(5000); const products = productsGenerator(3000);
const columns = [{ const columns = [{
dataField: 'id', dataField: 'id',
@@ -17,16 +16,25 @@ const columns = [{
text: 'Product Price' text: 'Product Price'
}]; }];
const expandRow = {
showExpandColumn: true,
renderer: row => (
<div>
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
)
};
export default () => ( export default () => (
<div> <div>
<BootstrapTable <BootstrapTable
keyField="id" keyField="id"
data={ products } data={ products }
columns={ columns } columns={ columns }
selectRow={ { mode: 'checkbox' } } selectRow={ { mode: 'checkbox', clickToSelect: true } }
cellEdit={ cellEditFactory({ expandRow={ expandRow }
mode: 'click'
}) }
/> />
</div> </div>
); );

View File

@@ -0,0 +1,54 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ { mode: 'checkbox' } }
tabIndexCell
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
selectRow={ { mode: 'checkbox' } }
tabIndexCell
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,69 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
clickToEdit: true
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
clickToEdit: true
};
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
selectRow={ selectRow }
cellEdit={ cellEditFactory({ mode: 'dbclick' }) }
/>
`;
export default () => (
<div>
<h3>Double click to edit cell</h3>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
selectRow={ selectRow }
cellEdit={ cellEditFactory({ mode: 'dbclick' }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,76 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsExpandRowsGenerator } from 'utils/common';
const products = productsExpandRowsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const expandRow = {
renderer: row => (
<div>
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true,
expandByColumnOnly: true
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const expandRow = {
renderer: row => (
<div>
<p>{ \`This Expand row is belong to rowKey $\{row.id}\` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
),
showExpandColumn: true
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
`;
export default () => (
<div>
<h3>Only able to expand row via clicking expand column(indicator)</h3>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,88 @@
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
clickToExpand: true
};
const expandRow = {
showExpandColumn: true,
renderer: row => (
<div>
<p>{ `This Expand row is belong to rowKey ${row.id}` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
)
};
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
const selectRow = {
mode: 'checkbox',
clickToSelect: true,
clickToExpand: true
};
const expandRow = {
showExpandColumn: true,
renderer: row => (
<div>
<p>{ \`This Expand row is belong to rowKey $\{row.id}\` }</p>
<p>You can render anything here, also you can add additional data on every row object</p>
<p>expandRow.renderer callback will pass the origin row object to you</p>
</div>
)
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
expandRow={ expandRow }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
selectRow={ selectRow }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -1,5 +1,5 @@
/* eslint no-unused-vars: 0 */ /* eslint no-unused-vars: 0 */
/* eslint no-alert: 0 */ /* eslint no-console: 0 */
import React from 'react'; import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next'; import BootstrapTable from 'react-bootstrap-table-next';
@@ -21,7 +21,10 @@ const columns = [{
const rowEvents = { const rowEvents = {
onClick: (e, row, rowIndex) => { onClick: (e, row, rowIndex) => {
alert(`clicked on row with index: ${rowIndex}`); console.log(`clicked on row with index: ${rowIndex}`);
},
onMouseEnter: (e, row, rowIndex) => {
console.log(`enter on row with index: ${rowIndex}`);
} }
}; };
@@ -41,7 +44,10 @@ const columns = [{
const rowEvents = { const rowEvents = {
onClick: (e, row, rowIndex) => { onClick: (e, row, rowIndex) => {
alert(\`clicked on row with index: \${rowIndex}\`); console.log(\`clicked on row with index: \${rowIndex}\`);
},
onMouseEnter: (e, row, rowIndex) => {
console.log(\`enter on row with index: \${rowIndex}\`);
} }
}; };
@@ -50,7 +56,7 @@ const rowEvents = {
export default () => ( export default () => (
<div> <div>
<h3>Try to click on any rows</h3> <h3>Try to click or hover on any rows</h3>
<BootstrapTable keyField="id" data={ products } columns={ columns } rowEvents={ rowEvents } /> <BootstrapTable keyField="id" data={ products } columns={ columns } rowEvents={ rowEvents } />
<Code>{ sourceCode }</Code> <Code>{ sourceCode }</Code>
</div> </div>

View File

@@ -0,0 +1,59 @@
/* eslint no-unused-vars: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const products = productsGenerator();
const columns = [{
dataField: 'id',
text: 'Product ID',
sort: true
}, {
dataField: 'name',
text: 'Product Name',
sort: true,
sortCaret: (order, column) => {
if (!order) return (<span>&nbsp;&nbsp;Desc/Asc</span>);
else if (order === 'asc') return (<span>&nbsp;&nbsp;Desc/<font color="red">Asc</font></span>);
else if (order === 'desc') return (<span>&nbsp;&nbsp;<font color="red">Desc</font>/Asc</span>);
return null;
}
}, {
dataField: 'price',
text: 'Product Price'
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
const columns = [{
dataField: 'id',
text: 'Product ID',
sort: true
}, {
dataField: 'name',
text: 'Product Name',
sort: true,
sortCaret: (order, column) => {
if (!order) return (<span>&nbsp;&nbsp;Desc/Asc</span>);
else if (order === 'asc') return (<span>&nbsp;&nbsp;Desc/<font color="red">Asc</font></span>);
else if (order === 'desc') return (<span>&nbsp;&nbsp;<font color="red">Desc</font>/Asc</span>);
return null;
}
}, {
dataField: 'price',
text: 'Product Price'
}];
<BootstrapTable keyField='id' data={ products } columns={ columns } />
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -1,6 +1,8 @@
import React from 'react'; import React from 'react';
import Typed from 'typed.js'; import Typed from 'typed.js';
const PROJECT_NAME = 'react-bootstrap-table2';
export default class Welcome extends React.Component { export default class Welcome extends React.Component {
componentDidMount() { componentDidMount() {
// type.js config // type.js config
@@ -21,14 +23,21 @@ export default class Welcome extends React.Component {
return ( return (
<div> <div>
<div className="welcome"> <div className="welcome">
<h1 className="welcome-title">react-bootstrap-table2</h1> <div className="welcome-title">
<span className="welcome-title-logo">
<img src="images/logo-color-square.svg" alt={ `${PROJECT_NAME}-logo` } />
</span>
<h1>
{PROJECT_NAME}
</h1>
</div>
<span <span
className="welcome-sub-title" className="welcome-sub-title"
ref={ (el) => { this.el = el; } } ref={ (el) => { this.el = el; } }
/> />
</div> </div>
<a href="https://github.com/react-bootstrap-table/react-bootstrap-table2" className="github-corner" aria-label="View source on Github"> <a href={ `https://github.com/react-bootstrap-table/${PROJECT_NAME}` } className="github-corner" aria-label="View source on Github">
<svg width="80" height="80" viewBox="0 0 250 250" style={ { fill: '#009688', color: '#fff', position: 'absolute', top: '0', border: '0', right: '0' } } aria-hidden="true"> <svg width="80" height="80" viewBox="0 0 250 250" style={ { fill: '#0058B7', color: '#fff', position: 'absolute', top: '0', border: '0', right: '0' } } aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" /> <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style={ { transformOrigin: '130px 106px' } } className="octo-arm" /> <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style={ { transformOrigin: '130px 106px' } } className="octo-arm" />
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" className="octo-body" /> <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" className="octo-body" />

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1 @@
<svg id="layer_1" data-name="layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><defs><style>.cls-1{fill:#059ae6;}.cls-2{fill:#0058b7;}</style></defs><title>logo square</title><polygon class="cls-1" points="22.76 59 22.76 64.35 49.24 51.13 49.24 43.88 44.45 41.5 44.45 48.17 22.76 59"/><polygon class="cls-2" points="22.76 20.87 22.76 47.98 27.55 45.59 27.55 23.83 44.45 15.39 44.45 23.02 33.06 28.71 33.06 31.9 49.24 39.97 49.24 34.62 40.58 30.3 49.24 25.98 49.24 7.65 22.76 20.87"/></svg>

After

Width:  |  Height:  |  Size: 503 B

View File

@@ -13,6 +13,8 @@ import NoDataTable from 'examples/basic/no-data-table';
import CustomizedIdClassesTable from 'examples/basic/customized-id-classes'; import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
import CaptionTable from 'examples/basic/caption-table'; import CaptionTable from 'examples/basic/caption-table';
import LargeTable from 'examples/basic/large-table'; import LargeTable from 'examples/basic/large-table';
import ExposedAPITable from 'examples/basic/exposed-function';
import TabIndexCellTable from 'examples/basic/tabindex-column';
// bootstrap 4 // bootstrap 4
import Bootstrap4DefaultSortTable from 'examples/bootstrap4/sort'; import Bootstrap4DefaultSortTable from 'examples/bootstrap4/sort';
@@ -83,6 +85,7 @@ import DefaultSortTable from 'examples/sort/default-sort-table';
import DefaultSortDirectionTable from 'examples/sort/default-sort-direction'; import DefaultSortDirectionTable from 'examples/sort/default-sort-direction';
import SortEvents from 'examples/sort/sort-events'; import SortEvents from 'examples/sort/sort-events';
import CustomSortTable from 'examples/sort/custom-sort-table'; import CustomSortTable from 'examples/sort/custom-sort-table';
import CustomSortCaretTable from 'examples/sort/custom-sort-caret';
import HeaderSortingClassesTable from 'examples/sort/header-sorting-classes'; import HeaderSortingClassesTable from 'examples/sort/header-sorting-classes';
import HeaderSortingStyleTable from 'examples/sort/header-sorting-style'; import HeaderSortingStyleTable from 'examples/sort/header-sorting-style';
@@ -100,6 +103,7 @@ import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
import AutoSelectTextInput from 'examples/cell-edit/auto-select-text-input-table'; import AutoSelectTextInput from 'examples/cell-edit/auto-select-text-input-table';
import EditorStyleTable from 'examples/cell-edit/editor-style-table'; import EditorStyleTable from 'examples/cell-edit/editor-style-table';
import EditorClassTable from 'examples/cell-edit/editor-class-table'; import EditorClassTable from 'examples/cell-edit/editor-class-table';
import DBClickEditWithSelection from 'examples/cell-edit/dbclick-to-edit-with-selection-table';
import DropdownEditorTable from 'examples/cell-edit/dropdown-editor-table'; import DropdownEditorTable from 'examples/cell-edit/dropdown-editor-table';
import TextareaEditorTable from 'examples/cell-edit/textarea-editor-table'; import TextareaEditorTable from 'examples/cell-edit/textarea-editor-table';
import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table'; import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table';
@@ -113,6 +117,7 @@ import ClickToSelectTable from 'examples/row-selection/click-to-select';
import DefaultSelectTable from 'examples/row-selection/default-select'; import DefaultSelectTable from 'examples/row-selection/default-select';
import SelectionManagement from 'examples/row-selection/selection-management'; import SelectionManagement from 'examples/row-selection/selection-management';
import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit'; import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit';
import SelectionWithExpansionTable from 'examples/row-selection/selection-with-expansion';
import SelectionNoDataTable from 'examples/row-selection/selection-no-data'; import SelectionNoDataTable from 'examples/row-selection/selection-no-data';
import SelectionStyleTable from 'examples/row-selection/selection-style'; import SelectionStyleTable from 'examples/row-selection/selection-style';
import SelectionClassTable from 'examples/row-selection/selection-class'; import SelectionClassTable from 'examples/row-selection/selection-class';
@@ -128,6 +133,7 @@ import BasicRowExpand from 'examples/row-expand';
import RowExpandManagement from 'examples/row-expand/expand-management'; import RowExpandManagement from 'examples/row-expand/expand-management';
import NonExpandableRows from 'examples/row-expand/non-expandable-rows'; import NonExpandableRows from 'examples/row-expand/non-expandable-rows';
import ExpandColumn from 'examples/row-expand/expand-column'; import ExpandColumn from 'examples/row-expand/expand-column';
import OnlyExpandByColumn from 'examples/row-expand/expand-by-column-only.js';
import ExpandOnlyOne from 'examples/row-expand/expand-only-one'; import ExpandOnlyOne from 'examples/row-expand/expand-only-one';
import CustomExpandColumn from 'examples/row-expand/custom-expand-column'; import CustomExpandColumn from 'examples/row-expand/custom-expand-column';
import ExpandHooks from 'examples/row-expand/expand-hooks'; import ExpandHooks from 'examples/row-expand/expand-hooks';
@@ -189,7 +195,9 @@ storiesOf('Basic Table', module)
.add('Indication For Empty Table', () => <NoDataTable />) .add('Indication For Empty Table', () => <NoDataTable />)
.add('Customized id and class table', () => <CustomizedIdClassesTable />) .add('Customized id and class table', () => <CustomizedIdClassesTable />)
.add('Table with caption', () => <CaptionTable />) .add('Table with caption', () => <CaptionTable />)
.add('Large Table', () => <LargeTable />); .add('Large Table', () => <LargeTable />)
.add('Exposed API', () => <ExposedAPITable />)
.add('Enable tabIndex on Cell', () => <TabIndexCellTable />);
storiesOf('Bootstrap 4', module) storiesOf('Bootstrap 4', module)
.addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR)) .addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR))
@@ -267,6 +275,7 @@ storiesOf('Sort Table', module)
.add('Default Sort Direction Table', () => <DefaultSortDirectionTable />) .add('Default Sort Direction Table', () => <DefaultSortDirectionTable />)
.add('Sort Events', () => <SortEvents />) .add('Sort Events', () => <SortEvents />)
.add('Custom Sort Fuction', () => <CustomSortTable />) .add('Custom Sort Fuction', () => <CustomSortTable />)
.add('Custom Sort Caret', () => <CustomSortCaretTable />)
.add('Custom Classes on Sorting Header Column', () => <HeaderSortingClassesTable />) .add('Custom Classes on Sorting Header Column', () => <HeaderSortingClassesTable />)
.add('Custom Style on Sorting Header Column', () => <HeaderSortingStyleTable />); .add('Custom Style on Sorting Header Column', () => <HeaderSortingStyleTable />);
@@ -285,6 +294,7 @@ storiesOf('Cell Editing', module)
.add('Custom Cell Classes', () => <CellEditClassTable />) .add('Custom Cell Classes', () => <CellEditClassTable />)
.add('Custom Editor Classes', () => <EditorClassTable />) .add('Custom Editor Classes', () => <EditorClassTable />)
.add('Custom Editor Style', () => <EditorStyleTable />) .add('Custom Editor Style', () => <EditorStyleTable />)
.add('DoubleClick to Edit with Selection', () => <DBClickEditWithSelection />)
.add('Dropdown Editor', () => <DropdownEditorTable />) .add('Dropdown Editor', () => <DropdownEditorTable />)
.add('Textarea Editor', () => <TextareaEditorTable />) .add('Textarea Editor', () => <TextareaEditorTable />)
.add('Checkbox Editor', () => <CheckboxEditorTable />) .add('Checkbox Editor', () => <CheckboxEditorTable />)
@@ -299,6 +309,7 @@ storiesOf('Row Selection', module)
.add('Default Select', () => <DefaultSelectTable />) .add('Default Select', () => <DefaultSelectTable />)
.add('Selection Management', () => <SelectionManagement />) .add('Selection Management', () => <SelectionManagement />)
.add('Click to Select and Edit Cell', () => <ClickToSelectWithCellEditTable />) .add('Click to Select and Edit Cell', () => <ClickToSelectWithCellEditTable />)
.add('Row Select and Expand', () => <SelectionWithExpansionTable />)
.add('Selection without Data', () => <SelectionNoDataTable />) .add('Selection without Data', () => <SelectionNoDataTable />)
.add('Selection Style', () => <SelectionStyleTable />) .add('Selection Style', () => <SelectionStyleTable />)
.add('Selection Class', () => <SelectionClassTable />) .add('Selection Class', () => <SelectionClassTable />)
@@ -315,6 +326,7 @@ storiesOf('Row Expand', module)
.add('Expand Management', () => <RowExpandManagement />) .add('Expand Management', () => <RowExpandManagement />)
.add('Non Expandabled Rows', () => <NonExpandableRows />) .add('Non Expandabled Rows', () => <NonExpandableRows />)
.add('Expand Indicator', () => <ExpandColumn />) .add('Expand Indicator', () => <ExpandColumn />)
.add('Only Expand by Indicator', () => <OnlyExpandByColumn />)
.add('Expand Only One Row at The Same Time', () => <ExpandOnlyOne />) .add('Expand Only One Row at The Same Time', () => <ExpandOnlyOne />)
.add('Custom Expand Indicator', () => <CustomExpandColumn />) .add('Custom Expand Indicator', () => <CustomExpandColumn />)
.add('Expand Hooks', () => <ExpandHooks />); .add('Expand Hooks', () => <ExpandHooks />);

View File

@@ -1,3 +1,5 @@
$logo-size: 96px;
.welcome { .welcome {
margin-top: 70px; margin-top: 70px;
text-align: center; text-align: center;
@@ -5,7 +7,22 @@
&-title { &-title {
color: $grey-900; color: $grey-900;
width: 100%;
display: inline-flex;
justify-content: center;
align-content: center;
&-logo {
position: relative;
top: -8px;
right: -12px;
width: $logo-size;
height: $logo-size;
} }
}
&-sub-title { &-sub-title {
font-size: 30px; font-size: 30px;
color: $grey-500; color: $grey-500;

View File

@@ -288,3 +288,4 @@ Following properties is valid in `FILTER_TYPES`:
* SELECT * SELECT
* NUMBER * NUMBER
* DATE * DATE
* MULTISELECT

View File

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

View File

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

View File

@@ -68,6 +68,9 @@ export default (
currPage = newPage; currPage = newPage;
needNewState = true; needNewState = true;
} }
} else {
this.currPage = nextProps.pagination.options.page;
this.currSizePerPage = nextProps.pagination.options.sizePerPage;
} }
if (needNewState) { if (needNewState) {

View File

@@ -160,6 +160,27 @@ describe('PaginationContext', () => {
}); });
}); });
describe('when remote pagination is enable', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({ ...defaultPagination }, true));
instance = wrapper.instance();
wrapper.render();
nextProps = {
data,
pagination: { ...defaultPagination, options: { page: 3, sizePerPage: 5 } }
};
instance.componentWillReceiveProps(nextProps);
});
it('should always set currPage from nextProps.pagination.options.page', () => {
expect(instance.currPage).toEqual(nextProps.pagination.options.page);
});
it('should always set currSizePerPage from nextProps.pagination.options.sizePerPage', () => {
expect(instance.currSizePerPage).toEqual(nextProps.pagination.options.sizePerPage);
});
});
describe('when page is not align', () => { describe('when page is not align', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallow(shallowContext({ wrapper = shallow(shallowContext({

View File

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

View File

@@ -39,8 +39,10 @@ export default (options = {
const { data, columns } = this.props; const { data, columns } = this.props;
let { searchText } = this.props; let { searchText } = this.props;
if (isRemoteSearch() && this.performRemoteSearch) { if (isRemoteSearch()) {
if (this.performRemoteSearch) {
handleRemoteSearchChange(searchText); handleRemoteSearchChange(searchText);
}
return data; return data;
} }

View File

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

View File

@@ -3,35 +3,39 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cs from 'classnames';
import _ from './utils'; import _ from './utils';
import Row from './row'; import SimpleRow from './row/simple-row';
import ExpandRow from './row-expand/expand-row'; import RowAggregator from './row/aggregate-row';
import RowSection from './row-section'; import RowSection from './row/row-section';
import Const from './const'; import Const from './const';
import withRowSelection from './row-selection/row-consumer';
import withRowExpansion from './row-expand/row-consumer';
const Body = (props) => { class Body extends React.Component {
constructor(props) {
super(props);
if (props.cellEdit.createContext) {
this.EditingCell = props.cellEdit.createEditingCell(_, props.cellEdit.options.onStartEdit);
}
}
render() {
const { const {
columns, columns,
data, data,
tabIndexCell,
keyField, keyField,
isEmpty, isEmpty,
noDataIndication, noDataIndication,
visibleColumnSize, visibleColumnSize,
cellEdit, cellEdit,
selectRow, selectRow,
selectedRowKeys,
rowStyle, rowStyle,
rowClasses, rowClasses,
rowEvents, rowEvents,
expandRow expandRow
} = props; } = this.props;
const {
bgColor,
nonSelectable
} = selectRow;
let content; let content;
@@ -42,90 +46,63 @@ const Body = (props) => {
} }
content = <RowSection content={ indication } colSpan={ visibleColumnSize } />; content = <RowSection content={ indication } colSpan={ visibleColumnSize } />;
} else { } else {
const nonEditableRows = cellEdit.nonEditableRows || []; let RowComponent = SimpleRow;
const selectRowEnabled = selectRow.mode !== Const.ROW_SELECT_DISABLED;
const expandRowEnabled = !!expandRow.renderer;
const additionalRowProps = {};
if (expandRowEnabled) {
RowComponent = withRowExpansion(RowAggregator, visibleColumnSize);
}
if (selectRowEnabled) {
RowComponent = withRowSelection(expandRowEnabled ? RowComponent : RowAggregator);
}
if (cellEdit.createContext) {
RowComponent = cellEdit.withRowLevelCellEdit(RowComponent, selectRowEnabled, keyField, _);
additionalRowProps.EditingCellComponent = this.EditingCell;
}
if (selectRowEnabled || expandRowEnabled) {
additionalRowProps.expandRow = expandRow;
additionalRowProps.selectRow = selectRow;
}
content = data.map((row, index) => { content = data.map((row, index) => {
const key = _.get(row, keyField); const key = _.get(row, keyField);
const editable = !(nonEditableRows.length > 0 && nonEditableRows.indexOf(key) > -1); const baseRowProps = {
key,
const selected = selectRow.mode !== Const.ROW_SELECT_DISABLED row,
? selectedRowKeys.includes(key) tabIndexCell,
: null; columns,
keyField,
const attrs = rowEvents || {}; cellEdit,
let style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle; value: key,
let classes = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses); rowIndex: index,
if (selected) { visibleColumnSize,
const selectedStyle = _.isFunction(selectRow.style) attrs: rowEvents || {},
? selectRow.style(row, index) ...additionalRowProps
: selectRow.style;
const selectedClasses = _.isFunction(selectRow.classes)
? selectRow.classes(row, index)
: selectRow.classes;
style = {
...style,
...selectedStyle
}; };
classes = cs(classes, selectedClasses);
if (bgColor) { baseRowProps.style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle;
style = style || {}; baseRowProps.className = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses);
style.backgroundColor = _.isFunction(bgColor) ? bgColor(row, index) : bgColor;
}
}
const selectable = !nonSelectable || !nonSelectable.includes(key); return <RowComponent { ...baseRowProps } />;
const expandable = expandRow && !expandRow.nonExpandable.includes(key);
const expanded = expandRow && expandRow.expanded.includes(key);
const result = [
<Row
key={ key }
row={ row }
keyField={ keyField }
rowIndex={ index }
columns={ columns }
cellEdit={ cellEdit }
editable={ editable }
selectable={ selectable }
expandable={ expandable }
selected={ selected }
expanded={ expanded }
selectRow={ selectRow }
expandRow={ expandRow }
style={ style }
className={ classes }
attrs={ attrs }
/>
];
if (expanded) {
result.push((
<ExpandRow
key={ `${key}-expanding` }
colSpan={ visibleColumnSize }
>
{ expandRow.renderer(row) }
</ExpandRow>
));
}
return result;
}); });
} }
return ( return (
<tbody>{ content }</tbody> <tbody>{ content }</tbody>
); );
}; }
}
Body.propTypes = { Body.propTypes = {
keyField: PropTypes.string.isRequired, keyField: PropTypes.string.isRequired,
data: PropTypes.array.isRequired, data: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired, columns: PropTypes.array.isRequired,
selectRow: PropTypes.object, selectRow: PropTypes.object
selectedRowKeys: PropTypes.array
}; };
export default Body; export default Body;

View File

@@ -9,7 +9,6 @@ import Caption from './caption';
import Body from './body'; import Body from './body';
import PropsBaseResolver from './props-resolver'; import PropsBaseResolver from './props-resolver';
import Const from './const'; import Const from './const';
import { getSelectionSummary } from './store/selection';
class BootstrapTable extends PropsBaseResolver(Component) { class BootstrapTable extends PropsBaseResolver(Component) {
constructor(props) { constructor(props) {
@@ -44,6 +43,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
data, data,
columns, columns,
keyField, keyField,
tabIndexCell,
id, id,
classes, classes,
striped, striped,
@@ -56,7 +56,9 @@ class BootstrapTable extends PropsBaseResolver(Component) {
rowClasses, rowClasses,
wrapperClasses, wrapperClasses,
rowEvents, rowEvents,
selected selectRow,
expandRow,
cellEdit
} = this.props; } = this.props;
const tableWrapperClass = cs('react-bootstrap-table', wrapperClasses); const tableWrapperClass = cs('react-bootstrap-table', wrapperClasses);
@@ -68,20 +70,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
'table-condensed': condensed 'table-condensed': condensed
}, classes); }, classes);
const cellSelectionInfo = this.resolveSelectRowProps({
onRowSelect: this.props.onRowSelect
});
const { allRowsSelected, allRowsNotSelected } = getSelectionSummary(data, keyField, selected);
const headerCellSelectionInfo = this.resolveSelectRowPropsForHeader({
onAllRowsSelect: this.props.onAllRowsSelect,
selected,
allRowsSelected,
allRowsNotSelected
});
const tableCaption = (caption && <Caption>{ caption }</Caption>); const tableCaption = (caption && <Caption>{ caption }</Caption>);
const expandRow = this.resolveExpandRowProps();
return ( return (
<div className={ tableWrapperClass }> <div className={ tableWrapperClass }>
@@ -95,19 +84,19 @@ class BootstrapTable extends PropsBaseResolver(Component) {
onSort={ this.props.onSort } onSort={ this.props.onSort }
onFilter={ this.props.onFilter } onFilter={ this.props.onFilter }
onExternalFilter={ this.props.onExternalFilter } onExternalFilter={ this.props.onExternalFilter }
selectRow={ headerCellSelectionInfo } selectRow={ selectRow }
expandRow={ expandRow } expandRow={ expandRow }
/> />
<Body <Body
data={ data } data={ data }
keyField={ keyField } keyField={ keyField }
tabIndexCell={ tabIndexCell }
columns={ columns } columns={ columns }
isEmpty={ this.isEmpty() } isEmpty={ this.isEmpty() }
visibleColumnSize={ this.visibleColumnSize() } visibleColumnSize={ this.visibleColumnSize() }
noDataIndication={ noDataIndication } noDataIndication={ noDataIndication }
cellEdit={ this.props.cellEdit || {} } cellEdit={ cellEdit }
selectRow={ cellSelectionInfo } selectRow={ selectRow }
selectedRowKeys={ selected }
expandRow={ expandRow } expandRow={ expandRow }
rowStyle={ rowStyle } rowStyle={ rowStyle }
rowClasses={ rowClasses } rowClasses={ rowClasses }
@@ -131,6 +120,7 @@ BootstrapTable.propTypes = {
striped: PropTypes.bool, striped: PropTypes.bool,
bordered: PropTypes.bool, bordered: PropTypes.bool,
hover: PropTypes.bool, hover: PropTypes.bool,
tabIndexCell: PropTypes.bool,
id: PropTypes.string, id: PropTypes.string,
classes: PropTypes.string, classes: PropTypes.string,
wrapperClasses: PropTypes.string, wrapperClasses: PropTypes.string,
@@ -143,8 +133,13 @@ BootstrapTable.propTypes = {
filter: PropTypes.object, filter: PropTypes.object,
cellEdit: PropTypes.object, cellEdit: PropTypes.object,
selectRow: PropTypes.shape({ selectRow: PropTypes.shape({
mode: PropTypes.oneOf([Const.ROW_SELECT_SINGLE, Const.ROW_SELECT_MULTIPLE]).isRequired, mode: PropTypes.oneOf([
Const.ROW_SELECT_SINGLE,
Const.ROW_SELECT_MULTIPLE,
Const.ROW_SELECT_DISABLED
]).isRequired,
clickToSelect: PropTypes.bool, clickToSelect: PropTypes.bool,
clickToExpand: PropTypes.bool,
clickToEdit: PropTypes.bool, clickToEdit: PropTypes.bool,
hideSelectAll: PropTypes.bool, hideSelectAll: PropTypes.bool,
onSelect: PropTypes.func, onSelect: PropTypes.func,
@@ -157,22 +152,18 @@ BootstrapTable.propTypes = {
selectionRenderer: PropTypes.func, selectionRenderer: PropTypes.func,
selectionHeaderRenderer: PropTypes.func selectionHeaderRenderer: PropTypes.func
}), }),
onRowSelect: PropTypes.func,
onAllRowsSelect: PropTypes.func,
expandRow: PropTypes.shape({ expandRow: PropTypes.shape({
renderer: PropTypes.func.isRequired, renderer: PropTypes.func,
expanded: PropTypes.array, expanded: PropTypes.array,
onExpand: PropTypes.func, onExpand: PropTypes.func,
onExpandAll: PropTypes.func, onExpandAll: PropTypes.func,
nonExpandable: PropTypes.array, nonExpandable: PropTypes.array,
showExpandColumn: PropTypes.bool, showExpandColumn: PropTypes.bool,
onlyOneExpanding: PropTypes.bool, onlyOneExpanding: PropTypes.bool,
expandByColumnOnly: PropTypes.bool,
expandColumnRenderer: PropTypes.func, expandColumnRenderer: PropTypes.func,
expandHeaderColumnRenderer: PropTypes.func expandHeaderColumnRenderer: PropTypes.func
}), }),
onRowExpand: PropTypes.func,
onAllRowExpand: PropTypes.func,
isAnyExpands: PropTypes.bool,
rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), rowStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
rowEvents: PropTypes.object, rowEvents: PropTypes.object,
rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), rowClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
@@ -202,7 +193,21 @@ BootstrapTable.defaultProps = {
bordered: true, bordered: true,
hover: false, hover: false,
condensed: false, condensed: false,
noDataIndication: null noDataIndication: null,
selectRow: {
mode: Const.ROW_SELECT_DISABLED,
selected: [],
hideSelectColumn: true
},
expandRow: {
renderer: undefined,
expanded: [],
nonExpandable: []
},
cellEdit: {
mode: null,
nonEditableRows: []
}
}; };
export default BootstrapTable; export default BootstrapTable;

View File

@@ -34,7 +34,8 @@ class Cell extends Component {
!_.isEqual(this.props.style, nextProps.style) || !_.isEqual(this.props.style, nextProps.style) ||
!_.isEqual(this.props.column.formatExtraData, nextProps.column.formatExtraData) || !_.isEqual(this.props.column.formatExtraData, nextProps.column.formatExtraData) ||
!_.isEqual(this.props.column.events, nextProps.column.events) || !_.isEqual(this.props.column.events, nextProps.column.events) ||
!_.isEqual(this.props.column.attrs, nextProps.column.attrs); !_.isEqual(this.props.column.attrs, nextProps.column.attrs) ||
this.props.tabIndex !== nextProps.tabIndex;
return shouldUpdate; return shouldUpdate;
} }

View File

@@ -4,8 +4,8 @@ import React, { Component } from 'react';
import _ from '../utils'; import _ from '../utils';
import createDataContext from './data-context'; import createDataContext from './data-context';
import createSortContext from './sort-context'; import createSortContext from './sort-context';
import createSelectionContext from './selection-context'; import SelectionContext from './selection-context';
import createRowExpandContext from './row-expand-context'; import RowExpandContext from './row-expand-context';
import remoteResolver from '../props-resolver/remote-resolver'; import remoteResolver from '../props-resolver/remote-resolver';
import { BootstrapContext } from './bootstrap'; import { BootstrapContext } from './bootstrap';
import dataOperator from '../store/operators'; import dataOperator from '../store/operators';
@@ -22,11 +22,11 @@ const withContext = Base =>
} }
if (props.selectRow) { if (props.selectRow) {
this.SelectionContext = createSelectionContext(dataOperator); this.SelectionContext = SelectionContext;
} }
if (props.expandRow) { if (props.expandRow) {
this.RowExpandContext = createRowExpandContext(dataOperator); this.RowExpandContext = RowExpandContext;
} }
if (props.cellEdit && props.cellEdit.createContext) { if (props.cellEdit && props.cellEdit.createContext) {
@@ -54,26 +54,31 @@ const withContext = Base =>
} }
} }
componentWillReceiveProps(nextProps) {
if (!nextProps.pagination && this.props.pagination) {
this.PaginationContext = null;
}
if (nextProps.pagination && !this.props.pagination) {
this.PaginationContext = nextProps.pagination.createContext(
this.isRemotePagination, this.handleRemotePageChange);
}
}
renderBase() { renderBase() {
return ( return (
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps, sortProps,
paginationProps, paginationProps,
expandProps,
selectionProps
) => ( ) => (
<Base <Base
ref={ n => this.table = n }
{ ...this.props } { ...this.props }
{ ...selectionProps }
{ ...sortProps } { ...sortProps }
{ ...cellEditProps }
{ ...filterProps } { ...filterProps }
{ ...searchProps } { ...searchProps }
{ ...paginationProps } { ...paginationProps }
{ ...expandProps }
data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) } data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) }
/> />
); );
@@ -82,32 +87,26 @@ const withContext = Base =>
renderWithSelectionCtx(base, baseProps) { renderWithSelectionCtx(base, baseProps) {
return ( return (
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps, sortProps,
paginationProps, paginationProps
expandProps
) => ( ) => (
<this.SelectionContext.Provider <this.SelectionContext.Provider
{ ...baseProps } { ...baseProps }
ref={ n => this.selectionContext = n }
selectRow={ this.props.selectRow } selectRow={ this.props.selectRow }
data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) } data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) }
> >
<this.SelectionContext.Consumer>
{ {
selectionProps => base( base(
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps, sortProps,
paginationProps, paginationProps
expandProps,
selectionProps
) )
} }
</this.SelectionContext.Consumer>
</this.SelectionContext.Provider> </this.SelectionContext.Provider>
); );
} }
@@ -115,7 +114,6 @@ const withContext = Base =>
renderWithRowExpandCtx(base, baseProps) { renderWithRowExpandCtx(base, baseProps) {
return ( return (
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps, sortProps,
@@ -123,22 +121,19 @@ const withContext = Base =>
) => ( ) => (
<this.RowExpandContext.Provider <this.RowExpandContext.Provider
{ ...baseProps } { ...baseProps }
ref={ n => this.rowExpandContext = n }
expandRow={ this.props.expandRow } expandRow={ this.props.expandRow }
data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) } data={ rootProps.getData(filterProps, searchProps, sortProps, paginationProps) }
> >
<this.RowExpandContext.Consumer>
{ {
expandProps => base( base(
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps, sortProps,
paginationProps, paginationProps
expandProps
) )
} }
</this.RowExpandContext.Consumer>
</this.RowExpandContext.Provider> </this.RowExpandContext.Provider>
); );
} }
@@ -146,7 +141,6 @@ const withContext = Base =>
renderWithPaginationCtx(base) { renderWithPaginationCtx(base) {
return ( return (
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps sortProps
@@ -161,7 +155,6 @@ const withContext = Base =>
{ {
paginationProps => base( paginationProps => base(
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps, sortProps,
@@ -176,7 +169,6 @@ const withContext = Base =>
renderWithSortCtx(base, baseProps) { renderWithSortCtx(base, baseProps) {
return ( return (
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps searchProps
) => ( ) => (
@@ -191,7 +183,6 @@ const withContext = Base =>
{ {
sortProps => base( sortProps => base(
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps, searchProps,
sortProps, sortProps,
@@ -205,7 +196,6 @@ const withContext = Base =>
renderWithSearchCtx(base, baseProps) { renderWithSearchCtx(base, baseProps) {
return ( return (
rootProps, rootProps,
cellEditProps,
filterProps filterProps
) => ( ) => (
<this.SearchContext.Provider <this.SearchContext.Provider
@@ -218,7 +208,6 @@ const withContext = Base =>
{ {
searchProps => base( searchProps => base(
rootProps, rootProps,
cellEditProps,
filterProps, filterProps,
searchProps searchProps
) )
@@ -229,10 +218,7 @@ const withContext = Base =>
} }
renderWithFilterCtx(base, baseProps) { renderWithFilterCtx(base, baseProps) {
return ( return rootProps => (
rootProps,
cellEditProps
) => (
<this.FilterContext.Provider <this.FilterContext.Provider
{ ...baseProps } { ...baseProps }
ref={ n => this.filterContext = n } ref={ n => this.filterContext = n }
@@ -242,7 +228,6 @@ const withContext = Base =>
{ {
filterProps => base( filterProps => base(
rootProps, rootProps,
cellEditProps,
filterProps filterProps
) )
} }
@@ -259,11 +244,7 @@ const withContext = Base =>
cellEdit={ this.props.cellEdit } cellEdit={ this.props.cellEdit }
data={ rootProps.getData() } data={ rootProps.getData() }
> >
<this.CellEditContext.Consumer> { base(rootProps) }
{
cellEditProps => base(rootProps, cellEditProps)
}
</this.CellEditContext.Consumer>
</this.CellEditContext.Provider> </this.CellEditContext.Provider>
); );
} }

View File

@@ -1,13 +1,11 @@
/* eslint react/prop-types: 0 */ /* eslint react/prop-types: 0 */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import dataOperator from '../store/operators';
export default ( const RowExpandContext = React.createContext();
dataOperator
) => {
const RowExpandContext = React.createContext();
class RowExpandProvider extends React.Component { class RowExpandProvider extends React.Component {
static propTypes = { static propTypes = {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
data: PropTypes.array.isRequired, data: PropTypes.array.isRequired,
@@ -74,8 +72,9 @@ export default (
return ( return (
<RowExpandContext.Provider <RowExpandContext.Provider
value={ { value={ {
isAnyExpands: dataOperator.isAnyExpands(data, keyField, this.state.expanded), ...this.props.expandRow,
expanded: this.state.expanded, expanded: this.state.expanded,
isAnyExpands: dataOperator.isAnyExpands(data, keyField, this.state.expanded),
onRowExpand: this.handleRowExpand, onRowExpand: this.handleRowExpand,
onAllRowExpand: this.handleAllRowExpand onAllRowExpand: this.handleAllRowExpand
} } } }
@@ -84,9 +83,9 @@ export default (
</RowExpandContext.Provider> </RowExpandContext.Provider>
); );
} }
} }
return {
export default {
Provider: RowExpandProvider, Provider: RowExpandProvider,
Consumer: RowExpandContext.Consumer Consumer: RowExpandContext.Consumer
};
}; };

View File

@@ -3,12 +3,11 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Const from '../const'; import Const from '../const';
export default ( import dataOperator from '../store/operators';
dataOperator import { getSelectionSummary } from '../store/selection';
) => {
const SelectionContext = React.createContext();
class SelectionProvider extends React.Component { const SelectionContext = React.createContext();
class SelectionProvider extends React.Component {
static propTypes = { static propTypes = {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
data: PropTypes.array.isRequired, data: PropTypes.array.isRequired,
@@ -23,7 +22,7 @@ export default (
} }
} }
state = { selected: (this.props.selectRow && this.props.selectRow.selected) || [] }; state = { selected: this.props.selectRow.selected || [] };
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
if (nextProps.selectRow) { if (nextProps.selectRow) {
@@ -95,21 +94,41 @@ export default (
} }
render() { render() {
const {
allRowsSelected,
allRowsNotSelected
} = getSelectionSummary(
this.props.data,
this.props.keyField,
this.state.selected
);
let checkedStatus;
// checkbox status depending on selected rows counts
if (allRowsSelected) checkedStatus = Const.CHECKBOX_STATUS_CHECKED;
else if (allRowsNotSelected) checkedStatus = Const.CHECKBOX_STATUS_UNCHECKED;
else checkedStatus = Const.CHECKBOX_STATUS_INDETERMINATE;
return ( return (
<SelectionContext.Provider <SelectionContext.Provider
value={ { value={ {
...this.props.selectRow,
selected: this.state.selected, selected: this.state.selected,
onRowSelect: this.handleRowSelect, onRowSelect: this.handleRowSelect,
onAllRowsSelect: this.handleAllRowsSelect onAllRowsSelect: this.handleAllRowsSelect,
allRowsSelected,
allRowsNotSelected,
checkedStatus
} } } }
> >
{ this.props.children } { this.props.children }
</SelectionContext.Provider> </SelectionContext.Provider>
); );
} }
} }
return {
export default {
Provider: SelectionProvider, Provider: SelectionProvider,
Consumer: SelectionContext.Consumer Consumer: SelectionContext.Consumer
};
}; };

View File

@@ -24,6 +24,7 @@ const HeaderCell = (props) => {
const { const {
text, text,
sort, sort,
sortCaret,
filter, filter,
filterRenderer, filterRenderer,
headerTitle, headerTitle,
@@ -69,7 +70,7 @@ const HeaderCell = (props) => {
cellAttrs.className = cs(cellAttrs.className, 'sortable'); cellAttrs.className = cs(cellAttrs.className, 'sortable');
if (sorting) { if (sorting) {
sortSymbol = <SortCaret order={ sortOrder } />; sortSymbol = sortCaret ? sortCaret(sortOrder, column) : <SortCaret order={ sortOrder } />;
// append customized classes or style if table was sorting based on the current column. // append customized classes or style if table was sorting based on the current column.
cellClasses = cs( cellClasses = cs(
@@ -86,7 +87,7 @@ const HeaderCell = (props) => {
: headerSortingStyle : headerSortingStyle
}; };
} else { } else {
sortSymbol = <SortSymbol />; sortSymbol = sortCaret ? sortCaret(undefined, column) : <SortSymbol />;
} }
} }
@@ -151,6 +152,7 @@ HeaderCell.propTypes = {
onSort: PropTypes.func, onSort: PropTypes.func,
sorting: PropTypes.bool, sorting: PropTypes.bool,
sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]), sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]),
sortCaret: PropTypes.func,
isLastSorting: PropTypes.bool, isLastSorting: PropTypes.bool,
onFilter: PropTypes.func, onFilter: PropTypes.func,
onExternalFilter: PropTypes.func onExternalFilter: PropTypes.func

View File

@@ -1,15 +1,14 @@
/* eslint react/require-default-props: 0 */ /* eslint react/require-default-props: 0 */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Const from './const';
import HeaderCell from './header-cell'; import HeaderCell from './header-cell';
import SelectionHeaderCell from './row-selection/selection-header-cell'; import SelectionHeaderCell from './row-selection/selection-header-cell';
import ExpandHeaderCell from './row-expand/expand-header-cell'; import ExpandHeaderCell from './row-expand/expand-header-cell';
import withHeaderSelection from './row-selection/selection-header-cell-consumer';
import withHeaderExpansion from './row-expand/expand-header-cell-consumer';
const Header = (props) => { const Header = (props) => {
const { ROW_SELECT_DISABLED } = Const;
const { const {
className, className,
columns, columns,
@@ -23,20 +22,24 @@ const Header = (props) => {
bootstrap4 bootstrap4
} = props; } = props;
let SelectionHeaderCellComp = () => null;
let ExpansionHeaderCellComp = () => null;
if (expandRow.showExpandColumn) {
ExpansionHeaderCellComp = withHeaderExpansion(ExpandHeaderCell);
}
if (selectRow) {
SelectionHeaderCellComp = withHeaderSelection(SelectionHeaderCell);
}
return ( return (
<thead> <thead>
<tr className={ className }> <tr className={ className }>
<ExpansionHeaderCellComp />
{ {
(expandRow && expandRow.showExpandColumn) !selectRow.hideSelectColumn ?
? <ExpandHeaderCell <SelectionHeaderCellComp /> : null
onAllRowExpand={ expandRow.onAllRowExpand }
anyExpands={ expandRow.isAnyExpands }
renderer={ expandRow.expandHeaderColumnRenderer }
/> : null
}
{
(selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn)
? <SelectionHeaderCell { ...selectRow } /> : null
} }
{ {
columns.map((column, i) => { columns.map((column, i) => {

View File

@@ -1,17 +0,0 @@
export default ExtendBase =>
class ExpandRowResolver extends ExtendBase {
resolveExpandRowProps() {
const { expandRow, expanded, onRowExpand, onAllRowExpand, isAnyExpands } = this.props;
if (expandRow) {
return {
...expandRow,
expanded,
onRowExpand,
onAllRowExpand,
isAnyExpands,
nonExpandable: expandRow.nonExpandable || []
};
}
return null;
}
};

View File

@@ -1,11 +1,7 @@
import ColumnResolver from './column-resolver'; import ColumnResolver from './column-resolver';
import ExpandRowResolver from './expand-row-resolver';
import Const from '../const';
import _ from '../utils';
export default ExtendBase => export default ExtendBase =>
class TableResolver extends class TableResolver extends ColumnResolver(ExtendBase) {
ExpandRowResolver(ColumnResolver(ExtendBase)) {
validateProps() { validateProps() {
const { keyField } = this.props; const { keyField } = this.props;
if (!keyField) { if (!keyField) {
@@ -19,63 +15,4 @@ export default ExtendBase =>
isEmpty() { isEmpty() {
return this.props.data.length === 0; return this.props.data.length === 0;
} }
/**
* props resolver for cell selection
* @param {Object} options - addtional options like callback which are about to merge into props
*
* @returns {Object} result - props for cell selections
* @returns {String} result.mode - input type of row selection or disabled.
*/
resolveSelectRowProps(options) {
const { selectRow } = this.props;
const { ROW_SELECT_DISABLED } = Const;
if (_.isDefined(selectRow)) {
return {
...selectRow,
...options
};
}
return {
mode: ROW_SELECT_DISABLED
};
}
/**
* props resolver for header cell selection
* @param {Object} options - addtional options like callback which are about to merge into props
*
* @returns {Object} result - props for cell selections
* @returns {String} result.mode - input type of row selection or disabled.
* @returns {String} result.checkedStatus - checkbox status depending on selected rows counts
*/
resolveSelectRowPropsForHeader(options = {}) {
const { selectRow } = this.props;
const { allRowsSelected, allRowsNotSelected, ...rest } = options;
const {
ROW_SELECT_DISABLED, CHECKBOX_STATUS_CHECKED,
CHECKBOX_STATUS_INDETERMINATE, CHECKBOX_STATUS_UNCHECKED
} = Const;
if (_.isDefined(selectRow)) {
let checkedStatus;
// checkbox status depending on selected rows counts
if (allRowsSelected) checkedStatus = CHECKBOX_STATUS_CHECKED;
else if (allRowsNotSelected) checkedStatus = CHECKBOX_STATUS_UNCHECKED;
else checkedStatus = CHECKBOX_STATUS_INDETERMINATE;
return {
...selectRow,
...rest,
checkedStatus
};
}
return {
mode: ROW_SELECT_DISABLED
};
}
}; };

View File

@@ -1,6 +1,3 @@
import _ from './utils';
import Const from './const';
const events = [ const events = [
'onClick', 'onClick',
'onDoubleClick', 'onDoubleClick',
@@ -15,7 +12,6 @@ export default ExtendBase =>
super(props); super(props);
this.clickNum = 0; this.clickNum = 0;
this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this); this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this);
this.createClickEventHandler = this.createClickEventHandler.bind(this);
} }
createDefaultEventHandler(cb) { createDefaultEventHandler(cb) {
@@ -25,65 +21,11 @@ export default ExtendBase =>
}; };
} }
createClickEventHandler(cb) {
return (e) => {
const {
row,
selected,
keyField,
selectable,
expandable,
rowIndex,
expanded,
expandRow,
selectRow,
cellEdit: {
mode,
DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK
}
} = this.props;
const clickFn = () => {
if (cb) {
cb(e, row, rowIndex);
}
const key = _.get(row, keyField);
if (expandRow && expandable) {
expandRow.onRowExpand(key, !expanded, rowIndex, e);
}
if (selectRow.mode !== Const.ROW_SELECT_DISABLED && selectable) {
selectRow.onRowSelect(key, !selected, rowIndex, e);
}
};
if (mode === DBCLICK_TO_CELL_EDIT && selectRow.clickToEdit) {
this.clickNum += 1;
_.debounce(() => {
if (this.clickNum === 1) {
clickFn();
}
this.clickNum = 0;
}, DELAY_FOR_DBCLICK)();
} else {
clickFn();
}
};
}
delegate(attrs = {}) { delegate(attrs = {}) {
const newAttrs = {}; const newAttrs = { ...attrs };
const { expandRow, selectRow } = this.props;
if (expandRow || (selectRow && selectRow.clickToSelect)) {
newAttrs.onClick = this.createClickEventHandler(attrs.onClick);
}
Object.keys(attrs).forEach((attr) => { Object.keys(attrs).forEach((attr) => {
if (!newAttrs[attr]) {
if (events.includes(attr)) { if (events.includes(attr)) {
newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]); newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]);
} else {
newAttrs[attr] = attrs[attr];
}
} }
}); });
return newAttrs; return newAttrs;

View File

@@ -12,7 +12,8 @@ export default class ExpandCell extends Component {
expanded: PropTypes.bool.isRequired, expanded: PropTypes.bool.isRequired,
onRowExpand: PropTypes.func.isRequired, onRowExpand: PropTypes.func.isRequired,
expandColumnRenderer: PropTypes.func, expandColumnRenderer: PropTypes.func,
rowIndex: PropTypes.number rowIndex: PropTypes.number,
tabIndex: PropTypes.number
} }
constructor() { constructor() {
@@ -20,17 +21,29 @@ export default class ExpandCell extends Component {
this.handleClick = this.handleClick.bind(this); this.handleClick = this.handleClick.bind(this);
} }
shouldComponentUpdate(nextProps) {
const shouldUpdate =
this.props.rowIndex !== nextProps.rowIndex ||
this.props.expanded !== nextProps.expanded ||
this.props.rowKey !== nextProps.rowKey ||
this.props.tabIndex !== nextProps.tabIndex;
return shouldUpdate;
}
handleClick(e) { handleClick(e) {
const { rowKey, expanded, onRowExpand, rowIndex } = this.props; const { rowKey, expanded, onRowExpand, rowIndex } = this.props;
onRowExpand(rowKey, expanded, rowIndex, e); onRowExpand(rowKey, !expanded, rowIndex, e);
} }
render() { render() {
const { expanded, expandColumnRenderer } = this.props; const { expanded, expandColumnRenderer, tabIndex } = this.props;
const attrs = {};
if (tabIndex !== -1) attrs.tabIndex = tabIndex;
return ( return (
<td onClick={ this.handleClick }> <td onClick={ this.handleClick } { ...attrs }>
{ {
expandColumnRenderer ? expandColumnRenderer({ expandColumnRenderer ? expandColumnRenderer({
expanded expanded

View File

@@ -0,0 +1,8 @@
import React from 'react';
import ExpansionContext from '../contexts/row-expand-context';
export default Component => () => (
<ExpansionContext.Consumer>
{ expandRow => <Component { ...expandRow } /> }
</ExpansionContext.Consumer>
);

View File

@@ -3,11 +3,11 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default class SelectionHeaderCell extends Component { export default class ExpansionHeaderCell extends Component {
static propTypes = { static propTypes = {
anyExpands: PropTypes.bool.isRequired, isAnyExpands: PropTypes.bool.isRequired,
onAllRowExpand: PropTypes.func.isRequired, onAllRowExpand: PropTypes.func.isRequired,
renderer: PropTypes.func expandHeaderColumnRenderer: PropTypes.func
} }
constructor() { constructor() {
@@ -16,13 +16,13 @@ export default class SelectionHeaderCell extends Component {
} }
handleCheckBoxClick(e) { handleCheckBoxClick(e) {
const { anyExpands, onAllRowExpand } = this.props; const { isAnyExpands, onAllRowExpand } = this.props;
onAllRowExpand(e, !anyExpands); onAllRowExpand(e, !isAnyExpands);
} }
render() { render() {
const { anyExpands, renderer } = this.props; const { isAnyExpands, expandHeaderColumnRenderer } = this.props;
const attrs = { const attrs = {
onClick: this.handleCheckBoxClick onClick: this.handleCheckBoxClick
}; };
@@ -30,9 +30,9 @@ export default class SelectionHeaderCell extends Component {
return ( return (
<th data-row-selection { ...attrs }> <th data-row-selection { ...attrs }>
{ {
renderer ? expandHeaderColumnRenderer ?
renderer({ isAnyExpands: anyExpands }) : expandHeaderColumnRenderer({ isAnyExpands }) :
(anyExpands ? '(-)' : '(+)') (isAnyExpands ? '(-)' : '(+)')
} }
</th> </th>
); );

View File

@@ -0,0 +1,34 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import ExpandRow from './expand-row';
import ExpansionContext from '../contexts/row-expand-context';
export default (Component, visibleColumnSize) => {
const renderWithExpansion = (props, expandRow) => {
const key = props.value;
const expanded = expandRow.expanded.includes(key);
const expandable = !expandRow.nonExpandable || !expandRow.nonExpandable.includes(key);
return [
<Component
{ ...props }
key={ key }
expanded={ expanded }
expandable={ expandable }
expandRow={ { ...expandRow } }
/>,
expanded ? <ExpandRow
key={ `${key}-expanding` }
colSpan={ visibleColumnSize }
>
{ expandRow.renderer(props.row) }
</ExpandRow> : null
];
};
return props => (
<ExpansionContext.Consumer>
{ expandRow => renderWithExpansion(props, expandRow) }
</ExpansionContext.Consumer>
);
};

View File

@@ -0,0 +1,63 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import cs from 'classnames';
import _ from '../utils';
import SelectionContext from '../contexts/selection-context';
export default (Component) => {
const renderWithSelection = (props, selectRow) => {
const key = props.value;
const selected = selectRow.selected.includes(key);
const selectable = !selectRow.nonSelectable || !selectRow.nonSelectable.includes(key);
let {
style,
className
} = props;
if (selected) {
const selectedStyle = _.isFunction(selectRow.style)
? selectRow.style(props.row, props.rowIndex)
: selectRow.style;
const selectedClasses = _.isFunction(selectRow.classes)
? selectRow.classes(props.row, props.rowIndex)
: selectRow.classes;
style = {
...style,
...selectedStyle
};
className = cs(className, selectedClasses) || undefined;
if (selectRow.bgColor) {
style = style || {};
style.backgroundColor = _.isFunction(selectRow.bgColor)
? selectRow.bgColor(props.row, props.rowIndex)
: selectRow.bgColor;
}
}
return (
<Component
{ ...props }
style={ style }
className={ className }
selectRow={ selectRow }
selected={ selected }
selectable={ selectable }
/>
);
};
function withConsumer(props) {
return (
<SelectionContext.Consumer>
{ selectRow => renderWithSelection(props, selectRow) }
</SelectionContext.Consumer>
);
}
withConsumer.displayName = 'WithSelectionRowConsumer';
return withConsumer;
};

View File

@@ -15,6 +15,7 @@ export default class SelectionCell extends Component {
onRowSelect: PropTypes.func, onRowSelect: PropTypes.func,
disabled: PropTypes.bool, disabled: PropTypes.bool,
rowIndex: PropTypes.number, rowIndex: PropTypes.number,
tabIndex: PropTypes.number,
clickToSelect: PropTypes.bool, clickToSelect: PropTypes.bool,
selectionRenderer: PropTypes.func selectionRenderer: PropTypes.func
} }
@@ -25,9 +26,14 @@ export default class SelectionCell extends Component {
} }
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
const { selected } = this.props; const shouldUpdate =
this.props.rowIndex !== nextProps.rowIndex ||
this.props.selected !== nextProps.selected ||
this.props.disabled !== nextProps.disabled ||
this.props.rowKey !== nextProps.rowKey ||
this.props.tabIndex !== nextProps.tabIndex;
return nextProps.selected !== selected; return shouldUpdate;
} }
handleClick(e) { handleClick(e) {
@@ -56,14 +62,18 @@ export default class SelectionCell extends Component {
mode: inputType, mode: inputType,
selected, selected,
disabled, disabled,
tabIndex,
selectionRenderer selectionRenderer
} = this.props; } = this.props;
const attrs = {};
if (tabIndex !== -1) attrs.tabIndex = tabIndex;
return ( return (
<BootstrapContext.Consumer> <BootstrapContext.Consumer>
{ {
({ bootstrap4 }) => ( ({ bootstrap4 }) => (
<td onClick={ this.handleClick }> <td onClick={ this.handleClick } { ...attrs }>
{ {
selectionRenderer ? selectionRenderer({ selectionRenderer ? selectionRenderer({
mode: inputType, mode: inputType,

View File

@@ -0,0 +1,8 @@
import React from 'react';
import SelectionContext from '../contexts/selection-context';
export default Component => () => (
<SelectionContext.Consumer>
{ selectRow => <Component { ...selectRow } /> }
</SelectionContext.Consumer>
);

View File

@@ -1,183 +0,0 @@
/* eslint react/prop-types: 0 */
/* eslint react/no-array-index-key: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import _ from './utils';
import Cell from './cell';
import SelectionCell from './row-selection/selection-cell';
import ExpandCell from './row-expand/expand-cell';
import eventDelegater from './row-event-delegater';
import Const from './const';
class Row extends eventDelegater(Component) {
render() {
const {
row,
columns,
keyField,
rowIndex,
className,
style,
attrs,
cellEdit,
selected,
selectRow,
expanded,
expandRow,
selectable,
editable: editableRow
} = this.props;
const {
mode,
onStart,
EditingCell,
ridx: editingRowIdx,
cidx: editingColIdx,
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT,
...rest
} = cellEdit;
const key = _.get(row, keyField);
const { hideSelectColumn } = selectRow;
const { showExpandColumn } = expandRow || {};
const trAttrs = this.delegate(attrs);
return (
<tr style={ style } className={ className } { ...trAttrs }>
{
showExpandColumn ? (
<ExpandCell
{ ...expandRow }
rowKey={ key }
rowIndex={ rowIndex }
expanded={ expanded }
/>
) : null
}
{
(selectRow.mode !== Const.ROW_SELECT_DISABLED && !hideSelectColumn)
? (
<SelectionCell
{ ...selectRow }
rowKey={ key }
rowIndex={ rowIndex }
selected={ selected }
disabled={ !selectable }
/>
)
: null
}
{
columns.map((column, index) => {
if (!column.hidden) {
const { dataField } = column;
const content = _.get(row, dataField);
let editable = _.isDefined(column.editable) ? column.editable : true;
if (dataField === keyField || !editableRow) editable = false;
if (_.isFunction(column.editable)) {
editable = column.editable(content, row, rowIndex, index);
}
if (rowIndex === editingRowIdx && index === editingColIdx) {
let editCellstyle = column.editCellStyle || {};
let editCellclasses = column.editCellClasses;
if (_.isFunction(column.editCellStyle)) {
editCellstyle = column.editCellStyle(content, row, rowIndex, index);
}
if (_.isFunction(column.editCellClasses)) {
editCellclasses = column.editCellClasses(content, row, rowIndex, index);
}
return (
<EditingCell
key={ `${content}-${index}` }
row={ row }
rowIndex={ rowIndex }
column={ column }
columnIndex={ index }
className={ editCellclasses }
style={ editCellstyle }
{ ...rest }
/>
);
}
// 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}` }
row={ row }
rowIndex={ rowIndex }
columnIndex={ index }
column={ column }
onStart={ onStart }
editable={ editable }
clickToEdit={ mode === CLICK_TO_CELL_EDIT }
dbclickToEdit={ mode === DBCLICK_TO_CELL_EDIT }
{ ...cellAttrs }
/>
);
}
return false;
})
}
</tr>
);
}
}
Row.propTypes = {
row: PropTypes.object.isRequired,
rowIndex: PropTypes.number.isRequired,
columns: PropTypes.array.isRequired,
style: PropTypes.object,
className: PropTypes.string,
attrs: PropTypes.object
};
Row.defaultProps = {
editable: true,
style: {},
className: null,
attrs: {}
};
export default Row;

View File

@@ -0,0 +1,117 @@
/* eslint react/prop-types: 0 */
/* eslint no-plusplus: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import _ from '../utils';
import ExpandCell from '../row-expand/expand-cell';
import SelectionCell from '../row-selection/selection-cell';
import shouldUpdater from './should-updater';
import eventDelegater from './event-delegater';
import RowPureContent from './row-pure-content';
export default class RowAggregator extends shouldUpdater(eventDelegater(React.Component)) {
static propTypes = {
attrs: PropTypes.object,
style: PropTypes.object
}
static defaultProps = {
attrs: {},
style: {}
}
constructor(props) {
super(props);
this.clickNum = 0;
this.shouldUpdateRowContent = false;
this.createClickEventHandler = this.createClickEventHandler.bind(this);
}
shouldComponentUpdate(nextProps) {
if (
this.props.selected !== nextProps.selected ||
this.props.expanded !== nextProps.expanded ||
this.props.selectable !== nextProps.selectable ||
this.shouldUpdatedBySelfProps(nextProps)
) {
this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps);
return true;
}
this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps);
return this.shouldUpdateRowContent;
}
render() {
const {
row,
columns,
keyField,
rowIndex,
style,
className,
attrs,
selectRow,
expandRow,
expanded,
selected,
selectable,
visibleColumnSize,
tabIndexCell,
...rest
} = this.props;
const key = _.get(row, keyField);
const { hideSelectColumn, clickToSelect } = selectRow;
const { showExpandColumn } = expandRow;
const newAttrs = this.delegate({ ...attrs });
if (clickToSelect || !!expandRow.renderer) {
newAttrs.onClick = this.createClickEventHandler(newAttrs.onClick);
}
let tabIndexStart = (rowIndex * visibleColumnSize) + 1;
return (
<tr
style={ style }
className={ className }
{ ...newAttrs }
>
{
showExpandColumn ? (
<ExpandCell
{ ...expandRow }
rowKey={ key }
rowIndex={ rowIndex }
expanded={ expanded }
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
/>
) : null
}
{
!hideSelectColumn
? (
<SelectionCell
{ ...selectRow }
rowKey={ key }
rowIndex={ rowIndex }
selected={ selected }
disabled={ !selectable }
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
/>
)
: null
}
<RowPureContent
row={ row }
columns={ columns }
keyField={ keyField }
rowIndex={ rowIndex }
shouldUpdate={ this.shouldUpdateRowContent }
tabIndexStart={ tabIndexCell ? tabIndexStart : -1 }
{ ...rest }
/>
</tr>
);
}
}

View File

@@ -0,0 +1,83 @@
import _ from '../utils';
import Const from '../const';
const events = [
'onClick',
'onDoubleClick',
'onMouseEnter',
'onMouseLeave',
'onContextMenu'
];
export default ExtendBase =>
class RowEventDelegater extends ExtendBase {
constructor(props) {
super(props);
this.clickNum = 0;
this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this);
this.createClickEventHandler = this.createClickEventHandler.bind(this);
}
createClickEventHandler(cb) {
return (e) => {
const {
row,
selected,
keyField,
selectable,
expandable,
rowIndex,
expanded,
expandRow,
selectRow,
DELAY_FOR_DBCLICK
} = this.props;
const clickFn = () => {
if (cb) {
cb(e, row, rowIndex);
}
const key = _.get(row, keyField);
if (expandRow && expandable && !expandRow.expandByColumnOnly) {
if (
(selectRow.mode !== Const.ROW_SELECT_DISABLED && selectRow.clickToExpand) ||
selectRow.mode === Const.ROW_SELECT_DISABLED
) {
expandRow.onRowExpand(key, !expanded, rowIndex, e);
}
}
if (selectRow.clickToSelect && selectable) {
selectRow.onRowSelect(key, !selected, rowIndex, e);
}
};
if (DELAY_FOR_DBCLICK) {
this.clickNum += 1;
_.debounce(() => {
if (this.clickNum === 1) {
clickFn();
}
this.clickNum = 0;
}, DELAY_FOR_DBCLICK)();
} else {
clickFn();
}
};
}
createDefaultEventHandler(cb) {
return (e) => {
const { row, rowIndex } = this.props;
cb(e, row, rowIndex);
};
}
delegate(attrs = {}) {
const newAttrs = { ...attrs };
Object.keys(attrs).forEach((attr) => {
if (events.includes(attr)) {
newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]);
}
});
return newAttrs;
}
};

View File

@@ -0,0 +1,116 @@
/* eslint react/prop-types: 0 */
/* eslint react/no-array-index-key: 0 */
/* eslint no-plusplus: 0 */
import React from 'react';
import _ from '../utils';
import Cell from '../cell';
export default class RowPureContent extends React.Component {
shouldComponentUpdate(nextProps) {
if (typeof nextProps.shouldUpdate !== 'undefined') {
return nextProps.shouldUpdate;
}
return true;
}
render() {
const {
row,
keyField,
columns,
rowIndex,
editable,
editingRowIdx,
editingColIdx,
onStart,
clickToEdit,
dbclickToEdit,
EditingCellComponent,
tabIndexStart
} = this.props;
let tabIndex = tabIndexStart;
return columns.map((column, index) => {
if (!column.hidden) {
const { dataField } = column;
const content = _.get(row, dataField);
if (rowIndex === editingRowIdx && index === editingColIdx) {
return (
<EditingCellComponent
key={ `${content}-${index}-editing` }
row={ row }
rowIndex={ rowIndex }
column={ column }
columnIndex={ index }
/>
);
}
// 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;
let editableCell = _.isDefined(column.editable) ? column.editable : true;
if (column.dataField === keyField || !editable) editableCell = false;
if (_.isFunction(column.editable)) {
editableCell = column.editable(content, row, rowIndex, index);
}
if (tabIndexStart !== -1) {
cellAttrs.tabIndex = tabIndex++;
}
return (
<Cell
key={ `${content}-${index}` }
row={ row }
editable={ editableCell }
rowIndex={ rowIndex }
columnIndex={ index }
column={ column }
onStart={ onStart }
clickToEdit={ clickToEdit }
dbclickToEdit={ dbclickToEdit }
{ ...cellAttrs }
/>
);
}
return false;
});
}
}

View File

@@ -0,0 +1,37 @@
/* eslint react/prop-types: 0 */
import _ from '../utils';
export default ExtendBase =>
class RowShouldUpdater extends ExtendBase {
shouldUpdateByCellEditing(nextProps) {
if (!(this.props.clickToEdit || this.props.dbclickToEdit)) return false;
return (
nextProps.editingRowIdx === nextProps.rowIndex ||
(this.props.editingRowIdx === nextProps.rowIndex &&
nextProps.editingRowIdx === null)
);
}
shouldUpdatedBySelfProps(nextProps) {
return (
this.props.className !== nextProps.className ||
!_.isEqual(this.props.style, nextProps.style) ||
!_.isEqual(this.props.attrs, nextProps.attrs)
);
}
shouldUpdatedByNormalProps(nextProps) {
const shouldUpdate =
this.props.rowIndex !== nextProps.rowIndex ||
this.props.editable !== nextProps.editable ||
!_.isEqual(this.props.row, nextProps.row) ||
this.props.columns.length !== nextProps.columns.length;
return shouldUpdate;
}
shouldUpdateChild(nextProps) {
return this.shouldUpdateByCellEditing(nextProps) ||
this.shouldUpdatedByNormalProps(nextProps);
}
};

View File

@@ -0,0 +1,64 @@
/* eslint react/prop-types: 0 */
/* eslint react/no-array-index-key: 0 */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import RowPureContent from './row-pure-content';
import eventDelegater from './event-delegater';
import shouldUpdater from './should-updater';
class SimpleRow extends shouldUpdater(eventDelegater(Component)) {
constructor(props) {
super(props);
this.shouldUpdateRowContent = false;
}
shouldComponentUpdate(nextProps) {
this.shouldUpdateRowContent = false;
this.shouldUpdateRowContent = this.shouldUpdateChild(nextProps);
if (this.shouldUpdateRowContent) return true;
return this.shouldUpdatedBySelfProps(nextProps);
}
render() {
const {
className,
style,
attrs,
visibleColumnSize,
tabIndexCell,
...rest
} = this.props;
const trAttrs = this.delegate(attrs);
const tabIndexStart = (this.props.rowIndex * visibleColumnSize) + 1;
return (
<tr style={ style } className={ className } { ...trAttrs }>
<RowPureContent
shouldUpdate={ this.shouldUpdateRowContent }
tabIndexStart={ tabIndexCell ? tabIndexStart : -1 }
{ ...rest }
/>
</tr>
);
}
}
SimpleRow.propTypes = {
row: PropTypes.object.isRequired,
rowIndex: PropTypes.number.isRequired,
columns: PropTypes.array.isRequired,
style: PropTypes.object,
className: PropTypes.string,
attrs: PropTypes.object
};
SimpleRow.defaultProps = {
editable: true,
style: {},
className: null,
attrs: {}
};
export default SimpleRow;

View File

@@ -6,7 +6,7 @@ export const getSelectionSummary = (
keyField, keyField,
selected = [] selected = []
) => { ) => {
let allRowsSelected = true; let allRowsSelected = data.length > 0;
let allRowsNotSelected = true; let allRowsNotSelected = true;
const rowKeys = data.map(d => d[keyField]); const rowKeys = data.map(d => d[keyField]);

View File

@@ -1,11 +1,15 @@
import 'jsdom-global/register';
import React from 'react'; import React from 'react';
import sinon from 'sinon'; import sinon from 'sinon';
import { shallow } from 'enzyme'; import { shallow, mount } from 'enzyme';
import Body from '../src/body'; import Body from '../src/body';
import Row from '../src/row'; import Row from '../src/row/simple-row';
import RowAggregator from '../src/row/aggregate-row';
import Const from '../src/const'; import Const from '../src/const';
import RowSection from '../src/row-section'; import RowSection from '../src/row/row-section';
import SelectionContext from '../src/contexts/selection-context';
import ExpansionContext from '../src/contexts/row-expand-context';
import mockBodyResolvedProps from './test-helpers/mock/body-resolved-props'; import mockBodyResolvedProps from './test-helpers/mock/body-resolved-props';
describe('Body', () => { describe('Body', () => {
@@ -169,92 +173,6 @@ describe('Body', () => {
}); });
}); });
}); });
describe('when selectRow.style is defined', () => {
const selectedRowKey = data[0][keyField];
const selectedRowKeys = [selectedRowKey];
const selectedStyle = { backgroundColor: 'green', fontWeight: 'bold' };
const selectRow = { mode: 'radio', style: selectedStyle };
beforeEach(() => {
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
keyField="id"
columns={ columns }
data={ data }
rowStyle={ rowStyle }
selectRow={ selectRow }
selectedRowKeys={ selectedRowKeys }
/>);
});
it('should rendering selected Row component with mixing selectRow.style correctly', () => {
const selectedRow = wrapper.find(Row).get(0);
expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({
...rowStyle,
...selectedStyle
}));
});
describe('and selectRow.bgColor is also defined', () => {
beforeEach(() => {
selectRow.bgColor = 'gray';
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
keyField="id"
columns={ columns }
data={ data }
rowStyle={ rowStyle }
selectRow={ selectRow }
selectedRowKeys={ selectedRowKeys }
/>);
});
it('should rendering selected Row component with mixing selectRow.style correctly', () => {
const selectedRow = wrapper.find(Row).get(0);
expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({
...rowStyle,
...selectedStyle,
backgroundColor: selectRow.bgColor
}));
});
it('should render selected Row component with correct style.backgroundColor', () => {
const selectedRow = wrapper.find(Row).get(0);
expect(selectedRow.props.style.backgroundColor).toEqual(selectRow.bgColor);
});
});
});
describe('when selectRow.bgColor is defined', () => {
const selectedRowKey = data[0][keyField];
const selectedRowKeys = [selectedRowKey];
const selectRow = { mode: 'radio', bgColor: 'gray' };
beforeEach(() => {
selectRow.bgColor = 'gray';
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
keyField="id"
columns={ columns }
data={ data }
rowStyle={ rowStyle }
selectRow={ selectRow }
selectedRowKeys={ selectedRowKeys }
/>);
});
it('should rendering selected Row component with correct style', () => {
const selectedRow = wrapper.find(Row).get(0);
expect(JSON.stringify(selectedRow.props.style)).toBe(JSON.stringify({
...rowStyle,
backgroundColor: selectRow.bgColor
}));
});
});
}); });
describe('when rowClasses prop is defined', () => { describe('when rowClasses prop is defined', () => {
@@ -310,31 +228,6 @@ describe('Body', () => {
}); });
}); });
}); });
describe('when selectRow.classes is defined', () => {
const selectedRowKey = data[0][keyField];
const selectedRowKeys = [selectedRowKey];
const selectedClasses = 'selected-classes';
const selectRow = { mode: 'radio', classes: selectedClasses };
beforeEach(() => {
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
keyField="id"
columns={ columns }
data={ data }
rowClasses={ rowClasses }
selectRow={ selectRow }
selectedRowKeys={ selectedRowKeys }
/>);
});
it('should rendering selected Row component with mixing selectRow.classes correctly', () => {
const selectedRow = wrapper.find(Row).get(0);
expect(selectedRow.props.className).toBe(`${rowClasses} ${selectedClasses}`);
});
});
}); });
describe('when rowEvents prop is defined', () => { describe('when rowEvents prop is defined', () => {
@@ -361,11 +254,14 @@ describe('Body', () => {
}); });
}); });
describe('when cellEdit.nonEditableRows props is defined', () => { describe('when cellEdit.createContext props is defined', () => {
const nonEditableRows = [data[1].id]; const EditingCellComponent = () => null;
const RowComponent = props => <Row { ...props } />;
const cellEdit = { const cellEdit = {
mode: Const.CLICK_TO_CELL_EDIT, options: { onStartEdit: jest.fn() },
nonEditableRows createContext: jest.fn(),
createEditingCell: jest.fn().mockReturnValue(EditingCellComponent),
withRowLevelCellEdit: jest.fn().mockReturnValue(RowComponent)
}; };
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = shallow(
@@ -379,259 +275,82 @@ describe('Body', () => {
); );
}); });
it('should render Row component with correct editable prop', () => { it('should render Row Component correctly', () => {
expect(wrapper.length).toBe(1); expect(wrapper.length).toBe(1);
const rows = wrapper.find(Row); expect(cellEdit.createEditingCell).toHaveBeenCalledTimes(1);
for (let i = 0; i < rows.length; i += 1) { expect(cellEdit.withRowLevelCellEdit).toHaveBeenCalledTimes(1);
if (nonEditableRows.indexOf(rows.get(i).props.row[keyField]) > -1) { expect(wrapper.find(RowComponent)).toHaveLength(2);
expect(rows.get(i).props.editable).toBeFalsy(); const aRowElement = wrapper.find(RowComponent).get(0);
} else { expect(aRowElement.props.EditingCellComponent).toBeDefined();
expect(rows.get(i).props.editable).toBeTruthy();
}
}
}); });
}); });
describe('when selectRow.mode is checkbox or radio (row was selectable)', () => { describe('when selectRow.mode is ROW_SELECT_DISABLED or expandRow.renderer is undefined', () => {
beforeEach(() => {
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
/>
);
});
it('shouldn\'t render RowAggregator component', () => {
expect(wrapper.find(RowAggregator)).toHaveLength(0);
});
});
describe('when selectRow.mode is defined correctly', () => {
const selectRow = { mode: 'checkbox' }; const selectRow = { mode: 'checkbox' };
const selectedRowKey = data[0][keyField];
const selectedRowKeys = [selectedRowKey];
beforeEach(() => { beforeEach(() => {
wrapper = shallow( wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<Body <Body
{ ...mockBodyResolvedProps } { ...mockBodyResolvedProps }
data={ data } data={ data }
columns={ columns } columns={ columns }
keyField={ keyField } keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow } selectRow={ selectRow }
/> />
</SelectionContext.Provider>
); );
}); });
it('should render Row component with correct selected prop', () => { it('should render RowAggregator component correctly', () => {
const rows = wrapper.find(Row); const rowAggregator = wrapper.find(RowAggregator);
for (let i = 0; i < rows.length; i += 1) {
const row = rows.get(i); expect(rowAggregator.get(0).props.selectRow.mode)
expect(row.props.selected).toBe(selectedRowKeys.indexOf(row.props.row[keyField]) > -1); .not.toEqual(Const.ROW_SELECT_DISABLED);
} expect(rowAggregator.get(0).props.selected).toBeDefined();
expect(rowAggregator.get(0).props.selectable).toBeDefined();
});
}); });
describe('if selectRow.style is defined as an object', () => { describe('when expandRow.renderer is defined correctly', () => {
const style = { backgroundColor: 'red' }; const expandRow = { renderer: jest.fn() };
beforeEach(() => { beforeEach(() => {
selectRow.style = style; wrapper = mount(
wrapper = shallow( <ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<Body <Body
{ ...mockBodyResolvedProps } { ...mockBodyResolvedProps }
data={ data } data={ data }
columns={ columns } columns={ columns }
keyField={ keyField } keyField={ keyField }
selectedRowKeys={ selectedRowKeys } expandRow={ expandRow }
selectRow={ selectRow }
/> />
</ExpansionContext.Provider>
); );
}); });
it('should render Row component with correct style prop', () => { it('should render RowAggregator component correctly', () => {
expect(JSON.stringify(wrapper.find(Row).get(0).props.style)).toBe(JSON.stringify(style)); const rowAggregator = wrapper.find(RowAggregator);
}); expect(rowAggregator.get(0).props.expandRow.renderer).toEqual(expandRow.renderer);
}); expect(rowAggregator.get(0).props.expanded).toBeDefined();
expect(rowAggregator.get(0).props.expandable).toBeDefined();
describe('if selectRow.style is defined as a function', () => {
const style = { backgroundColor: 'red' };
const styleCallBack = sinon.stub().returns(style);
beforeEach(() => {
selectRow.style = styleCallBack;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should calling style callback correctly', () => {
expect(styleCallBack.callCount).toBe(1);
expect(styleCallBack.calledWith(data[0]), 1);
});
it('should render Row component with correct style prop', () => {
expect(JSON.stringify(wrapper.find(Row).get(0).props.style)).toBe(JSON.stringify(style));
});
});
describe('if selectRow.classes is defined as a string', () => {
const className = 'custom-class';
beforeEach(() => {
selectRow.classes = className;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should render Row component with correct className prop', () => {
expect(wrapper.find(Row).get(0).props.className).toEqual(className);
});
});
describe('if selectRow.classes is defined as a function', () => {
const className = 'custom-class';
const classesCallBack = sinon.stub().returns(className);
beforeEach(() => {
selectRow.classes = classesCallBack;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should calling style callback correctly', () => {
expect(classesCallBack.callCount).toBe(1);
expect(classesCallBack.calledWith(data[0]), 1);
});
it('should render Row component with correct style prop', () => {
expect(wrapper.find(Row).get(0).props.className).toEqual(className);
});
});
describe('if selectRow.bgColor is defined as a string', () => {
const bgColor = 'red';
beforeEach(() => {
selectRow.bgColor = bgColor;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should render Row component with correct style.backgroundColor prop', () => {
expect(wrapper.find(Row).get(0).props.style).toEqual({ backgroundColor: bgColor });
});
});
describe('if selectRow.bgColor is defined as a string', () => {
const bgColor = 'red';
const bgColorCallBack = sinon.stub().returns(bgColor);
beforeEach(() => {
selectRow.bgColor = bgColorCallBack;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should calling selectRow.bgColor callback correctly', () => {
expect(bgColorCallBack.calledOnce).toBeTruthy();
expect(bgColorCallBack.calledWith(data[0]), 1).toBeTruthy();
});
it('should render Row component with correct style.backgroundColor prop', () => {
expect(wrapper.find(Row).get(0).props.style).toEqual({ backgroundColor: bgColor });
});
});
describe('if selectRow.bgColor defined and selectRow.style.backgroundColor defined', () => {
const bgColor = 'yellow';
const style = { backgroundColor: 'red' };
beforeEach(() => {
selectRow.style = style;
selectRow.bgColor = bgColor;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should take selectRow.bgColor as higher priority', () => {
expect(wrapper.find(Row).get(0).props.style.backgroundColor).toBe(bgColor);
});
});
describe('if selectRow.nonSelectable is defined', () => {
const nonSelectableRowIndex = 1;
const nonSelectable = [data[nonSelectableRowIndex][keyField]];
beforeEach(() => {
selectRow.nonSelectable = nonSelectable;
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
);
});
it('should render Row component with correct selectable prop', () => {
expect(wrapper.find(Row).get(0).props.selectable).toBeTruthy();
expect(wrapper.find(Row).get(nonSelectableRowIndex).props.selectable).toBeFalsy();
});
});
});
describe('when selectRow.mode is ROW_SELECT_DISABLED (row was un-selectable)', () => {
beforeEach(() => {
wrapper = shallow(
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ [] }
/>
);
});
it('prop selected should be null', () => {
expect(wrapper.find(Row).get(0).props.selected).toBeNull();
}); });
}); });
}); });

View File

@@ -198,6 +198,26 @@ describe('Cell', () => {
}); });
}); });
describe('when props.tabIndex is change', () => {
const column = { dataField: 'name', text: 'Product Name' };
beforeEach(() => {
props = {
row,
columnIndex: 1,
rowIndex: 1,
tabIndex: 5,
column
};
wrapper = shallow(
<Cell { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, tabIndex: 2 };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('if column.isDummyField is true', () => { describe('if column.isDummyField is true', () => {
describe('when content is change', () => { describe('when content is change', () => {
const column = { dataField: '', text: 'Product Name', isDummyField: true }; const column = { dataField: '', text: 'Product Name', isDummyField: true };

View File

@@ -111,7 +111,10 @@ describe('Context', () => {
createContext: jest.fn().mockReturnValue({ createContext: jest.fn().mockReturnValue({
Provider: CellEditContext.Provider, Provider: CellEditContext.Provider,
Consumer: CellEditContext.Consumer Consumer: CellEditContext.Consumer
}) }),
options: {},
createEditingCell: jest.fn().mockReturnValue(() => null),
withRowLevelCellEdit: jest.fn().mockReturnValue(() => null)
}; };
wrapper = shallow( wrapper = shallow(
<BootstrapTable <BootstrapTable

View File

@@ -4,7 +4,7 @@ import { shallow } from 'enzyme';
import dataOperator from '../../src/store/operators'; import dataOperator from '../../src/store/operators';
import BootstrapTable from '../../src/bootstrap-table'; import BootstrapTable from '../../src/bootstrap-table';
import createSelectionContext from '../../src/contexts/selection-context'; import SelectionContext from '../../src/contexts/selection-context';
describe('DataContext', () => { describe('DataContext', () => {
let wrapper; let wrapper;
@@ -42,7 +42,6 @@ describe('DataContext', () => {
const defaultSelectRow = { const defaultSelectRow = {
mode: 'checkbox' mode: 'checkbox'
}; };
const SelectionContext = createSelectionContext(dataOperator);
function shallowContext(selectRow = defaultSelectRow) { function shallowContext(selectRow = defaultSelectRow) {
return ( return (
@@ -81,9 +80,13 @@ describe('DataContext', () => {
it('should pass correct sort props to children element', () => { it('should pass correct sort props to children element', () => {
expect(wrapper.length).toBe(1); expect(wrapper.length).toBe(1);
expect(mockBase).toHaveBeenCalledWith({ expect(mockBase).toHaveBeenCalledWith({
...defaultSelectRow,
selected: wrapper.state().selected, selected: wrapper.state().selected,
onRowSelect: wrapper.instance().handleRowSelect, onRowSelect: wrapper.instance().handleRowSelect,
onAllRowsSelect: wrapper.instance().handleAllRowsSelect onAllRowsSelect: wrapper.instance().handleAllRowsSelect,
allRowsNotSelected: true,
allRowsSelected: false,
checkedStatus: 'unchecked'
}); });
}); });
}); });

View File

@@ -403,6 +403,24 @@ describe('HeaderCell', () => {
it('header should render SortSymbol as default', () => { it('header should render SortSymbol as default', () => {
expect(wrapper.find(SortSymbol).length).toBe(1); expect(wrapper.find(SortSymbol).length).toBe(1);
}); });
describe('when sortCaret is defined ', () => {
beforeEach(() => {
column = { ...column, sortCaret: jest.fn() };
wrapper = shallow(
<HeaderCell column={ column } index={ index } onSort={ onSortCallBack } />
);
});
it('header should not render SortSymbol', () => {
expect(wrapper.find(SortSymbol).length).toBe(0);
});
it('should call column.sortCaret correctly', () => {
expect(column.sortCaret).toHaveBeenCalledTimes(1);
expect(column.sortCaret).toHaveBeenCalledWith(undefined, column);
});
});
}); });
describe('and sorting prop is true', () => { describe('and sorting prop is true', () => {
@@ -420,6 +438,30 @@ describe('HeaderCell', () => {
}); });
}); });
describe('when sortCaret is defined ', () => {
beforeEach(() => {
column = { ...column, sortCaret: jest.fn() };
wrapper = shallow(
<HeaderCell
column={ column }
index={ index }
onSort={ onSortCallBack }
sortOrder={ Const.SORT_ASC }
sorting
/>
);
});
it('header should not render SortSymbol', () => {
expect(wrapper.find(SortSymbol).length).toBe(0);
});
it('should call column.sortCaret correctly', () => {
expect(column.sortCaret).toHaveBeenCalledTimes(1);
expect(column.sortCaret).toHaveBeenCalledWith(Const.SORT_ASC, column);
});
});
describe('when headerSortingClasses is defined ', () => { describe('when headerSortingClasses is defined ', () => {
const classes = 'foo'; const classes = 'foo';
const order = Const.SORT_DESC; const order = Const.SORT_DESC;

View File

@@ -1,8 +1,12 @@
import 'jsdom-global/register';
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow, mount } from 'enzyme';
import HeaderCell from '../src/header-cell'; import HeaderCell from '../src/header-cell';
import SelectionHeaderCell from '../src//row-selection/selection-header-cell'; import SelectionHeaderCell from '../src/row-selection/selection-header-cell';
import ExpandHeaderCell from '../src/row-expand/expand-header-cell';
import SelectionContext from '../src/contexts/selection-context';
import ExpansionContext from '../src/contexts/row-expand-context';
import Header from '../src/header'; import Header from '../src/header';
import Const from '../src/const'; import Const from '../src/const';
import mockHeaderResolvedProps from './test-helpers/mock/header-resolved-props'; import mockHeaderResolvedProps from './test-helpers/mock/header-resolved-props';
@@ -17,6 +21,16 @@ describe('Header', () => {
text: 'Name' text: 'Name'
}]; }];
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}];
const keyField = 'id';
describe('simplest header', () => { describe('simplest header', () => {
beforeEach(() => { beforeEach(() => {
wrapper = shallow(<Header { ...mockHeaderResolvedProps } columns={ columns } />); wrapper = shallow(<Header { ...mockHeaderResolvedProps } columns={ columns } />);
@@ -89,12 +103,18 @@ describe('Header', () => {
describe('when selectRow.mode is radio (single selection)', () => { describe('when selectRow.mode is radio (single selection)', () => {
beforeEach(() => { beforeEach(() => {
const selectRow = { mode: 'radio' }; const selectRow = { mode: 'radio' };
wrapper = shallow( wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header <Header
{ ...mockHeaderResolvedProps } { ...mockHeaderResolvedProps }
columns={ columns } columns={ columns }
selectRow={ selectRow } selectRow={ selectRow }
/> />
</SelectionContext.Provider>
); );
}); });
@@ -105,12 +125,18 @@ describe('Header', () => {
describe('when selectRow.hideSelectColumn is true', () => { describe('when selectRow.hideSelectColumn is true', () => {
beforeEach(() => { beforeEach(() => {
const selectRow = { mode: 'radio', hideSelectColumn: true }; const selectRow = { mode: 'radio', hideSelectColumn: true };
wrapper = shallow( wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header <Header
{ ...mockHeaderResolvedProps } { ...mockHeaderResolvedProps }
columns={ columns } columns={ columns }
selectRow={ selectRow } selectRow={ selectRow }
/> />
</SelectionContext.Provider>
); );
}); });
@@ -146,12 +172,18 @@ describe('Header', () => {
describe('when selectRow.mode is checkbox (multiple selection)', () => { describe('when selectRow.mode is checkbox (multiple selection)', () => {
beforeEach(() => { beforeEach(() => {
const selectRow = { mode: 'checkbox' }; const selectRow = { mode: 'checkbox' };
wrapper = shallow( wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header <Header
{ ...mockHeaderResolvedProps } { ...mockHeaderResolvedProps }
columns={ columns } columns={ columns }
selectRow={ selectRow } selectRow={ selectRow }
/> />
</SelectionContext.Provider>
); );
}); });
@@ -162,12 +194,18 @@ describe('Header', () => {
describe('when selectRow.hideSelectColumn is true', () => { describe('when selectRow.hideSelectColumn is true', () => {
beforeEach(() => { beforeEach(() => {
const selectRow = { mode: 'checkbox', hideSelectColumn: true }; const selectRow = { mode: 'checkbox', hideSelectColumn: true };
wrapper = shallow( wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header <Header
{ ...mockHeaderResolvedProps } { ...mockHeaderResolvedProps }
columns={ columns } columns={ columns }
selectRow={ selectRow } selectRow={ selectRow }
/> />
</SelectionContext.Provider>
); );
}); });
@@ -177,4 +215,44 @@ describe('Header', () => {
}); });
}); });
}); });
describe('expandRow', () => {
describe('when expandRow.showExpandColumn is false', () => {
beforeEach(() => {
wrapper = shallow(
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
/>
);
});
it('should not render <ExpandHeaderCell />', () => {
expect(wrapper.find(ExpandHeaderCell).length).toBe(0);
});
});
describe('when expandRow.showExpandColumn is true', () => {
beforeEach(() => {
const expandRow = { renderer: jest.fn(), expanded: [], showExpandColumn: true };
wrapper = mount(
<ExpansionContext.Provider
data={ data }
keyField={ keyField }
expandRow={ expandRow }
>
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
expandRow={ expandRow }
/>
</ExpansionContext.Provider>
);
});
it('should render <ExpandHeaderCell /> correctly', () => {
expect(wrapper.find(ExpandHeaderCell).length).toBe(1);
});
});
});
}); });

View File

@@ -1,10 +1,8 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { extendTo } from '../test-helpers/mock-component'; import { extendTo } from '../test-helpers/mock-component';
import baseResolver from '../../src/props-resolver/index'; import baseResolver from '../../src/props-resolver/index';
import Const from '../../src/const';
describe('TableResolver', () => { describe('TableResolver', () => {
const keyField = 'id'; const keyField = 'id';
@@ -71,231 +69,4 @@ describe('TableResolver', () => {
}); });
}); });
}); });
describe('resolveSelectRowProps', () => {
let cellSelectionInfo;
let selectRow;
describe('if selectRow was not defined', () => {
beforeEach(() => {
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveSelectRowProps();
});
it('should return object', () => {
expect(cellSelectionInfo).toBeDefined();
expect(cellSelectionInfo.constructor).toEqual(Object);
});
it('should contain mode in ROW_SELECT_DISABLED', () => {
expect(cellSelectionInfo.mode).toEqual(Const.ROW_SELECT_DISABLED);
});
});
describe('if selectRow was defined', () => {
describe('when mode was defined', () => {
it('should return object which contains ROW_SELECT_SINGLE if mode is radio', () => {
selectRow = { mode: 'radio' };
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveSelectRowProps();
expect(cellSelectionInfo).toBeDefined();
expect(cellSelectionInfo.constructor).toEqual(Object);
expect(cellSelectionInfo.mode).toEqual(Const.ROW_SELECT_SINGLE);
});
it('should return object which contains ROW_SELECT_MULTIPLE if mode is checkbox', () => {
selectRow = { mode: 'checkbox' };
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveSelectRowProps();
expect(cellSelectionInfo).toBeDefined();
expect(cellSelectionInfo.constructor).toEqual(Object);
expect(cellSelectionInfo.mode).toEqual(Const.ROW_SELECT_MULTIPLE);
});
});
describe('when options were given', () => {
beforeEach(() => {
selectRow = {};
const mockOptions = {
foo: 'test',
bar: sinon.stub()
};
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
cellSelectionInfo = wrapper.instance().resolveSelectRowProps(mockOptions);
});
it('should return object which contain options', () => {
expect(cellSelectionInfo).toEqual(expect.objectContaining({
foo: 'test',
bar: expect.any(Function)
}));
});
});
});
});
describe('resolveSelectRowPropsForHeader', () => {
let headerCellSelectionInfo;
let selectRow;
beforeEach(() => {
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveSelectRowPropsForHeader();
});
describe('if selectRow was not defined', () => {
it('should return object', () => {
expect(headerCellSelectionInfo).toBeDefined();
expect(headerCellSelectionInfo.constructor).toEqual(Object);
});
it('should contain mode in ROW_SELECT_DISABLED', () => {
expect(headerCellSelectionInfo.mode).toEqual(Const.ROW_SELECT_DISABLED);
});
});
describe('if selectRow was defined', () => {
describe('when mode was defined', () => {
it('should return object which contains ROW_SELECT_SINGLE if mode is radio', () => {
selectRow = { mode: 'radio' };
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectedRowKeys, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveSelectRowPropsForHeader();
expect(headerCellSelectionInfo).toBeDefined();
expect(headerCellSelectionInfo.constructor).toEqual(Object);
expect(headerCellSelectionInfo.mode).toEqual(Const.ROW_SELECT_SINGLE);
});
it('should return object which contains ROW_SELECT_MULTIPLE if mode is checkbox', () => {
selectRow = { mode: 'checkbox' };
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectedRowKeys, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveSelectRowPropsForHeader();
expect(headerCellSelectionInfo).toBeDefined();
expect(headerCellSelectionInfo.constructor).toEqual(Object);
expect(headerCellSelectionInfo.mode).toEqual(Const.ROW_SELECT_MULTIPLE);
});
});
describe('when options were given', () => {
beforeEach(() => {
selectRow = {};
const mockOptions = {
foo: 'test',
bar: sinon.stub(),
allRowsSelected: false,
selected: []
};
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectedRowKeys, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveSelectRowPropsForHeader(mockOptions);
});
it('should return object which contain specified options', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
foo: 'test',
bar: expect.any(Function)
}));
});
});
describe('if options.allRowsSelected is true', () => {
beforeEach(() => {
selectRow = {};
const selectedRowKeys = [1, 2];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveSelectRowPropsForHeader({
allRowsSelected: true,
selected: selectedRowKeys
});
});
it('should return checkedStatus which eqauls to checked', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
checkedStatus: Const.CHECKBOX_STATUS_CHECKED
}));
});
});
describe('if options.allRowsSelected and options.allRowsNotSelected both are false', () => {
beforeEach(() => {
selectRow = {};
const selectedRowKeys = [1];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveSelectRowPropsForHeader({
allRowsSelected: false,
allRowsNotSelected: false,
selected: selectedRowKeys
});
});
it('should return checkedStatus which eqauls to indeterminate', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
checkedStatus: Const.CHECKBOX_STATUS_INDETERMINATE
}));
});
});
describe('if options.allRowsNotSelected is true', () => {
beforeEach(() => {
selectRow = {};
const selectedRowKeys = [];
const mockElement = React.createElement(BootstrapTableMock, {
data, keyField, columns, selectRow
}, null);
wrapper = shallow(mockElement);
headerCellSelectionInfo = wrapper.instance().resolveSelectRowPropsForHeader({
allRowsSelected: false,
allRowsNotSelected: true,
selected: selectedRowKeys
});
});
it('should return checkedStatus which eqauls to unchecked', () => {
expect(headerCellSelectionInfo).toEqual(expect.objectContaining({
checkedStatus: Const.CHECKBOX_STATUS_UNCHECKED
}));
});
});
});
});
}); });

View File

@@ -0,0 +1,414 @@
import 'jsdom-global/register';
import React from 'react';
import { mount } from 'enzyme';
import SelectionContext from '../../src/contexts/selection-context';
import withSelectionConsumer from '../../src/row-selection/row-consumer';
describe('withSelectionConsumer', () => {
let wrapper;
let selectRow;
const BaseComponent = () => null;
const WithSelectionComponent = withSelectionConsumer(props => <BaseComponent { ...props } />);
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}, {
id: 3,
name: 'C'
}];
const rowIndex = 1;
const row = data[rowIndex];
const keyField = 'id';
const value = row[keyField];
describe('if current row is selected', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', selected: [data[rowIndex][keyField]] };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject selected prop as true to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('selected')).toBeTruthy();
});
});
describe('if current row is not selected', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', selected: [] };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject selected prop as false to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('selected')).toBeFalsy();
});
});
describe('if current row is selectable', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', nonSelectable: [] };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject selectable prop as true to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('selectable')).toBeTruthy();
});
});
describe('if current row is non selectable', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', nonSelectable: [data[rowIndex][keyField]] };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject selectable prop as false to target component', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('selectable')).toBeFalsy();
});
});
describe('if current row is selected', () => {
const selectedStyle = { backgroundColor: 'green', fontWeight: 'bold' };
describe('when selectRow.style is defined as an object', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', selected: [data[rowIndex][keyField]], style: selectedStyle };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual(selectedStyle);
});
describe('and props.style is also defined', () => {
const componentStype = { fontSize: '16px' };
beforeEach(() => {
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
style={ componentStype }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual({
...selectedStyle,
...componentStype
});
});
});
describe('and selectRow.bgColor is also defined as an object', () => {
beforeEach(() => {
selectRow.bgColor = 'gray';
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop with correct backgroundColor', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual({
...selectedStyle,
backgroundColor: selectRow.bgColor
});
});
});
describe('and selectRow.bgColor is also defined as a function', () => {
const color = 'gray';
beforeEach(() => {
selectRow.bgColor = jest.fn().mockReturnValue(color);
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop with correct backgroundColor', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual({
...selectedStyle,
backgroundColor: color
});
});
it('should call selectRow.bgColor function correctly', () => {
expect(selectRow.bgColor).toHaveBeenCalledTimes(1);
expect(selectRow.bgColor).toHaveBeenCalledWith(row, rowIndex);
});
});
});
describe('when selectRow.style is defined as a function', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', selected: [data[rowIndex][keyField]], style: jest.fn().mockReturnValue(selectedStyle) };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual(selectedStyle);
});
it('should call selectRow.style function correctly', () => {
expect(selectRow.style).toHaveBeenCalledTimes(1);
expect(selectRow.style).toHaveBeenCalledWith(row, rowIndex);
});
describe('and props.style is also defined', () => {
const componentStype = { fontSize: '16px' };
beforeEach(() => {
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
style={ componentStype }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual({
...selectedStyle,
...componentStype
});
});
});
describe('and selectRow.bgColor is also defined as an object', () => {
beforeEach(() => {
selectRow.bgColor = 'gray';
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop with correct backgroundColor', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual({
...selectedStyle,
backgroundColor: selectRow.bgColor
});
});
});
describe('and selectRow.bgColor is also defined as a function', () => {
const color = 'gray';
beforeEach(() => {
selectRow.bgColor = jest.fn().mockReturnValue(color);
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop with correct backgroundColor', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('style')).toEqual({
...selectedStyle,
backgroundColor: color
});
});
it('should call selectRow.bgColor function correctly', () => {
expect(selectRow.bgColor).toHaveBeenCalledTimes(1);
expect(selectRow.bgColor).toHaveBeenCalledWith(row, rowIndex);
});
});
});
});
describe('if current row is selected', () => {
const selectedClassName = 'select-classname';
describe('when selectRow.style is defined as an object', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', selected: [data[rowIndex][keyField]], classes: selectedClassName };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject className prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('className')).toEqual(selectedClassName);
});
describe('and props.className is also defined', () => {
const componentClassName = 'component-classname';
beforeEach(() => {
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
className={ componentClassName }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('className')).toEqual(`${componentClassName} ${selectedClassName}`);
});
});
});
describe('when selectRow.style is defined as a function', () => {
beforeEach(() => {
selectRow = { mode: 'checkbox', selected: [data[rowIndex][keyField]], classes: jest.fn().mockReturnValue(selectedClassName) };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
/>
</SelectionContext.Provider>
);
});
it('should inject className prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('className')).toEqual(selectedClassName);
});
it('should call selectRow.classes function correctly', () => {
expect(selectRow.classes).toHaveBeenCalledTimes(1);
expect(selectRow.classes).toHaveBeenCalledWith(row, rowIndex);
});
describe('and props.className is also defined', () => {
const componentClassName = 'component-classname';
beforeEach(() => {
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<WithSelectionComponent
row={ row }
value={ value }
keyField={ keyField }
rowIndex={ rowIndex }
className={ componentClassName }
/>
</SelectionContext.Provider>
);
});
it('should inject style prop correctly', () => {
expect(wrapper.find(BaseComponent)).toHaveLength(1);
expect(wrapper.find(BaseComponent).prop('className')).toEqual(`${componentClassName} ${selectedClassName}`);
});
});
});
});
});

View File

@@ -14,24 +14,106 @@ describe('<SelectionCell />', () => {
let wrapper; let wrapper;
describe('shouldComponentUpdate', () => { describe('shouldComponentUpdate', () => {
const selected = true; let props;
let nextProps;
describe('when selected prop has not been changed', () => {
it('should not update component', () => {
const nextProps = { selected };
wrapper = shallow(<SelectionCell rowKey={ 1 } mode={ mode } selected={ selected } />);
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(false);
});
});
describe('when selected prop has been changed', () => { describe('when selected prop has been changed', () => {
it('should update component', () => { beforeEach(() => {
const nextProps = { selected: !selected }; props = {
selected: false,
mode,
rowIndex,
disabled: false,
rowKey: 1
};
wrapper = shallow(
<SelectionCell { ...props } />
);
});
wrapper = shallow(<SelectionCell rowKey={ 1 } mode={ mode } selected={ selected } />); it('should return true', () => {
nextProps = { ...props, selected: true };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when rowIndex prop has been changed', () => {
beforeEach(() => {
props = {
selected: false,
mode,
rowIndex,
disabled: false,
rowKey: 1
};
wrapper = shallow(
<SelectionCell { ...props } />
);
});
it('should return true', () => {
nextProps = { ...props, rowIndex: 2 };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when tabIndex prop has been changed', () => {
beforeEach(() => {
props = {
selected: false,
mode,
rowIndex,
disabled: false,
tabIndex: 0,
rowKey: 1
};
wrapper = shallow(
<SelectionCell { ...props } />
);
});
it('should return true', () => {
nextProps = { ...props, tabIndex: 2 };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when disabled prop has been changed', () => {
beforeEach(() => {
props = {
selected: false,
mode,
rowIndex,
disabled: false,
rowKey: 1
};
wrapper = shallow(
<SelectionCell { ...props } />
);
});
it('should return true', () => {
nextProps = { ...props, disabled: true };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('when rowKey prop has been changed', () => {
beforeEach(() => {
props = {
selected: false,
mode,
rowIndex,
disabled: false,
rowKey: 1
};
wrapper = shallow(
<SelectionCell { ...props } />
);
});
it('should return true', () => {
nextProps = { ...props, rowKey: '1' };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true); expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
}); });
}); });

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,295 @@
import 'jsdom-global/register';
import React from 'react';
import { mount } from 'enzyme';
import mockBodyResolvedProps from '../test-helpers/mock/body-resolved-props';
import SelectionContext from '../../src/contexts/selection-context';
import ExpansionContext from '../../src/contexts/row-expand-context';
import bindSelection from '../../src/row-selection/row-consumer';
import bindExpansion from '../../src/row-expand/row-consumer';
import ExpandCell from '../../src/row-expand/expand-cell';
import SelectionCell from '../../src/row-selection/selection-cell';
import RowAggregator from '../../src/row/aggregate-row';
describe('Row Aggregator', () => {
let wrapper;
let rowAggregator;
const RowAggregatorWithSelection = bindSelection(RowAggregator);
const RowAggregatorWithExpansion = bindExpansion(RowAggregator);
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}, {
id: 3,
name: 'C'
}];
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}];
const rowIndex = 1;
const row = data[rowIndex];
const keyField = 'id';
const getBaseProps = () => ({
row,
value: row[keyField],
columns,
keyField,
rowIndex,
...mockBodyResolvedProps
});
describe('when selectRow is enable', () => {
describe('if props.selectRow.hideSelectColumn is false', () => {
beforeEach(() => {
const selectRow = { mode: 'radio' };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<RowAggregatorWithSelection { ...getBaseProps() } />
</SelectionContext.Provider>
);
});
it('should render RowAggregator correctly', () => {
rowAggregator = wrapper.find(RowAggregator);
expect(rowAggregator).toHaveLength(1);
});
it('should render selection column correctly', () => {
const selectionCell = wrapper.find(SelectionCell);
expect(selectionCell).toHaveLength(1);
expect(selectionCell.props().selected).toEqual(rowAggregator.props().selected);
expect(selectionCell.props().disabled).toEqual(!rowAggregator.props().selectable);
});
});
describe('if props.selectRow.hideSelectColumn is true', () => {
beforeEach(() => {
const selectRow = { mode: 'radio', hideSelectColumn: true };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<RowAggregatorWithSelection { ...getBaseProps() } />
</SelectionContext.Provider>
);
});
it('should render RowAggregator correctly', () => {
rowAggregator = wrapper.find(RowAggregator);
expect(rowAggregator).toHaveLength(1);
});
it('should not render selection column', () => {
const selectionCell = wrapper.find(SelectionCell);
expect(selectionCell).toHaveLength(0);
});
});
describe('if props.selectRow.clickToSelect is defined', () => {
beforeEach(() => {
const selectRow = { mode: 'radio', clickToSelect: true };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<RowAggregatorWithSelection { ...getBaseProps() } />
</SelectionContext.Provider>
);
});
it('should render RowAggregator correctly', () => {
rowAggregator = wrapper.find(RowAggregator);
expect(rowAggregator).toHaveLength(1);
});
it('should add onClick prop to Row Component', () => {
const tr = wrapper.find('tr');
expect(tr).toHaveLength(1);
expect(tr.props().onClick).toBeDefined();
});
});
});
describe('when expandRow is enable', () => {
describe('if props.expandRow.showExpandColumn is false', () => {
beforeEach(() => {
const expandRow = { renderer: jest.fn() };
wrapper = mount(
<ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<RowAggregatorWithExpansion { ...getBaseProps() } />
</ExpansionContext.Provider>
);
});
it('should render RowAggregator correctly', () => {
rowAggregator = wrapper.find(RowAggregator);
expect(rowAggregator).toHaveLength(1);
});
it('should not render expansion column', () => {
const expandCell = wrapper.find(ExpandCell);
expect(expandCell).toHaveLength(0);
});
});
describe('if props.expandRow.showExpandColumn is true', () => {
beforeEach(() => {
const expandRow = { renderer: jest.fn(), showExpandColumn: true };
wrapper = mount(
<ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<RowAggregatorWithExpansion { ...getBaseProps() } />
</ExpansionContext.Provider>
);
});
it('should render RowAggregator correctly', () => {
rowAggregator = wrapper.find(RowAggregator);
expect(rowAggregator).toHaveLength(1);
});
it('should render expansion column correctly', () => {
const expandCell = wrapper.find(ExpandCell);
expect(expandCell).toHaveLength(1);
expect(expandCell.props().expanded).toEqual(rowAggregator.props().expanded);
});
});
});
describe('createClickEventHandler', () => {
describe('if props.attrs.onClick is defined', () => {
const attrs = { onClick: jest.fn() };
beforeEach(() => {
const selectRow = { mode: 'radio' };
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<RowAggregatorWithSelection { ...getBaseProps() } attrs={ attrs } />
</SelectionContext.Provider>
);
wrapper.find('tr').simulate('click');
});
it('should call attrs.onClick correctly', () => {
expect(attrs.onClick).toHaveBeenCalledTimes(1);
});
});
describe('if props.selectRow.clickToSelect is true', () => {
const selectRow = { mode: 'radio', clickToSelect: true };
beforeEach(() => {
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<RowAggregatorWithSelection { ...getBaseProps() } />
</SelectionContext.Provider>
);
wrapper.find(RowAggregator).props().selectRow.onRowSelect = jest.fn();
wrapper.find('tr').simulate('click');
});
it('should call selectRow.onRowSelect correctly', () => {
expect(wrapper.find(RowAggregator).props().selectRow.onRowSelect).toHaveBeenCalledTimes(1);
});
});
describe('if props.selectRow.clickToSelect is true', () => {
describe('but selectable props is false', () => {
const selectRow = { mode: 'radio', clickToSelect: true, nonSelectable: [row[keyField]] };
beforeEach(() => {
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<RowAggregatorWithSelection { ...getBaseProps() } />
</SelectionContext.Provider>
);
wrapper.find(RowAggregator).props().selectRow.onRowSelect = jest.fn();
wrapper.find('tr').simulate('click');
});
it('should call selectRow.onRowSelect correctly', () => {
expect(wrapper.find(RowAggregator).props().selectRow.onRowSelect)
.toHaveBeenCalledTimes(0);
});
});
});
describe('if props.expandRow is not defined', () => {
describe('but expandable props is false', () => {
const expandRow = { renderer: jest.fn(), nonExpandable: [row[keyField]] };
beforeEach(() => {
wrapper = mount(
<ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<RowAggregatorWithExpansion { ...getBaseProps() } />
</ExpansionContext.Provider>
);
wrapper.find(RowAggregator).props().expandRow.onRowExpand = jest.fn();
wrapper.find('tr').simulate('click');
});
it('should call expandRow.onRowExpand correctly', () => {
expect(wrapper.find(RowAggregator).props().expandRow.onRowExpand)
.toHaveBeenCalledTimes(0);
});
});
});
describe('if props.expandRow is defined', () => {
const expandRow = { renderer: jest.fn() };
beforeEach(() => {
wrapper = mount(
<ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<RowAggregatorWithExpansion { ...getBaseProps() } />
</ExpansionContext.Provider>
);
wrapper.find(RowAggregator).props().expandRow.onRowExpand = jest.fn();
wrapper.find('tr').simulate('click');
});
it('should call expandRow.onRowExpand correctly', () => {
expect(wrapper.find(RowAggregator).props().expandRow.onRowExpand).toHaveBeenCalledTimes(1);
});
});
describe('if props.attrs.onClick and props.expandRow both are defined', () => {
const attrs = { onClick: jest.fn() };
const expandRow = { renderer: jest.fn() };
beforeEach(() => {
wrapper = mount(
<ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<RowAggregatorWithExpansion { ...getBaseProps() } attrs={ attrs } />
</ExpansionContext.Provider>
);
wrapper.find(RowAggregator).props().expandRow.onRowExpand = jest.fn();
wrapper.find('tr').simulate('click');
});
it('should call attrs.onClick and expandRow.onRowExpand correctly', () => {
expect(attrs.onClick).toHaveBeenCalledTimes(1);
expect(wrapper.find(RowAggregator).props().expandRow.onRowExpand).toHaveBeenCalledTimes(1);
});
});
describe('if props.attrs.onClick and props.selectRow.clickToSelect both are defined', () => {
const attrs = { onClick: jest.fn() };
const selectRow = { mode: 'radio', clickToSelect: true };
beforeEach(() => {
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<RowAggregatorWithSelection { ...getBaseProps() } attrs={ attrs } />
</SelectionContext.Provider>
);
wrapper.find(RowAggregator).props().selectRow.onRowSelect = jest.fn();
wrapper.find('tr').simulate('click');
});
it('should call attrs.onClick and selectRow.onRowSelect correctly', () => {
expect(attrs.onClick).toHaveBeenCalledTimes(1);
expect(wrapper.find(RowAggregator).props().selectRow.onRowSelect).toHaveBeenCalledTimes(1);
});
});
});
});

View File

@@ -0,0 +1,624 @@
import React from 'react';
import { shallow } from 'enzyme';
import Cell from '../../src/cell';
import RowPureContent from '../../src/row/row-pure-content';
import mockBodyResolvedProps from '../test-helpers/mock/body-resolved-props';
let defaultColumns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'price',
text: 'Price'
}];
const keyField = 'id';
const rowIndex = 1;
describe('RowPureContent', () => {
let wrapper;
const row = {
id: 1,
name: 'A',
price: 1000
};
beforeEach(() => {
defaultColumns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'price',
text: 'Price'
}];
});
describe('shouldComponentUpdate', () => {
let props;
let nextProps;
describe('if nextProps.shouldUpdate is different with this.props.shouldUpdate', () => {
beforeEach(() => {
props = {
keyField,
columns: defaultColumns,
rowIndex: 1,
row,
shouldUpdate: false
};
wrapper = shallow(
<RowPureContent { ...props } />
);
});
it('should return true', () => {
nextProps = { ...props, shouldUpdate: true };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
});
describe('if nextProps.shouldUpdate is same with this.props.shouldUpdate', () => {
beforeEach(() => {
props = {
keyField,
columns: defaultColumns,
rowIndex: 1,
row,
shouldUpdate: false
};
wrapper = shallow(
<RowPureContent { ...props } />
);
});
it('should return false', () => {
nextProps = { ...props };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(false);
});
});
});
describe('simplest row', () => {
beforeEach(() => {
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
/>
);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(defaultColumns.length);
expect(wrapper.find(Cell).length).toBe(Object.keys(row).length);
});
});
describe('when tabIndexStart prop is -1', () => {
beforeEach(() => {
wrapper = shallow(
<RowPureContent
tabIndexStart={ -1 }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
/>
);
});
it('should not render tabIndex prop on Cell', () => {
wrapper.find(Cell).forEach((cell) => {
expect(cell.prop('tabIndex')).toBeUndefined();
});
});
});
describe('when tabIndexStart prop is not -1', () => {
const tabIndexStart = 4;
beforeEach(() => {
wrapper = shallow(
<RowPureContent
tabIndexStart={ tabIndexStart }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
/>
);
});
it('should render correct tabIndex prop on Cell', () => {
wrapper.find(Cell).forEach((cell, i) => {
expect(cell.prop('tabIndex')).toEqual(tabIndexStart + i);
});
});
});
describe('when editingRowIdx and editingColIdx prop is defined', () => {
const editingRowIdx = rowIndex;
const editingColIdx = 1;
const EditingCellComponent = () => null;
beforeEach(() => {
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
EditingCellComponent={ EditingCellComponent }
editingRowIdx={ editingRowIdx }
editingColIdx={ editingColIdx }
/>);
});
it('should render EditingCell component correctly', () => {
const EditingCell = wrapper.find(EditingCellComponent);
expect(wrapper.length).toBe(defaultColumns.length);
expect(EditingCell).toHaveLength(1);
expect(EditingCell.prop('row')).toEqual(row);
expect(EditingCell.prop('rowIndex')).toEqual(editingRowIdx);
expect(EditingCell.prop('column')).toEqual(defaultColumns[editingColIdx]);
expect(EditingCell.prop('columnIndex')).toEqual(editingColIdx);
});
});
describe('when column.hidden is true', () => {
beforeEach(() => {
const newColumns = [{
dataField: 'id',
text: 'ID',
hidden: true
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'price',
text: 'Price'
}];
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ newColumns }
row={ row }
/>);
});
it('should not render column with hidden value true', () => {
expect(wrapper.find(Cell).length).toBe(2);
});
});
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(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
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 = jest.fn().mockReturnValue(returnStyle);
columns[columnIndex].style = styleCallBack;
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { styleCallBack.mockClear(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
expect(wrapper.find(Cell).get(columnIndex).props.style).toEqual(returnStyle);
});
it('should call custom style function correctly', () => {
expect(styleCallBack).toHaveBeenCalledTimes(1);
expect(styleCallBack).toHaveBeenCalledWith(
row[columns[columnIndex].dataField], row, rowIndex, columnIndex);
});
});
});
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(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
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 = jest.fn().mockReturnValue(returnClasses);
columns[columnIndex].classes = classesCallBack;
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { classesCallBack.mockClear(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
expect(wrapper.find(Cell).get(columnIndex).props.className).toEqual(returnClasses);
});
it('should call custom classes function correctly', () => {
expect(classesCallBack).toHaveBeenCalledTimes(1);
expect(classesCallBack).toHaveBeenCalledWith(
row[columns[columnIndex].dataField], row, rowIndex, columnIndex);
});
});
});
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(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
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 = jest.fn().mockReturnValue(returnTitle);
columns[columnIndex].title = titleCallBack;
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { titleCallBack.mockClear(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
expect(wrapper.find(Cell).get(columnIndex).props.title).toEqual(returnTitle);
});
it('should call custom title function correctly', () => {
expect(titleCallBack).toHaveBeenCalledTimes(1);
expect(titleCallBack).toHaveBeenCalledWith(
row[columns[columnIndex].dataField], row, rowIndex, columnIndex);
});
});
});
describe('when column.events prop is defined', () => {
let columns;
const columnIndex = 1;
beforeEach(() => {
columns = [...defaultColumns];
columns[columnIndex].events = {
onClick: jest.fn()
};
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should attachs DOM event successfully', () => {
expect(wrapper.length).toBe(defaultColumns.length);
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(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
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 = jest.fn().mockReturnValue(returnAlign);
columns[columnIndex].align = alignCallBack;
wrapper = shallow(
<RowPureContent
{ ...mockBodyResolvedProps }
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { alignCallBack.mockClear(); });
it('should render Cell correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
expect(wrapper.find(Cell).get(columnIndex).props.style.textAlign).toEqual(returnAlign);
});
it('should call custom align function correctly', () => {
expect(alignCallBack).toHaveBeenCalledTimes(1);
expect(alignCallBack).toHaveBeenCalledWith(
row[columns[columnIndex].dataField], row, rowIndex, columnIndex);
});
});
});
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(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
expect(wrapper.length).toBe(defaultColumns.length);
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(
<RowPureContent
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(
<RowPureContent
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(
<RowPureContent
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(
<RowPureContent
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 = jest.fn().mockReturnValue(customAttrs);
columns[columnIndex].attrs = attrsCallBack;
wrapper = shallow(
<RowPureContent
keyField={ keyField }
rowIndex={ rowIndex }
columns={ columns }
row={ row }
/>
);
});
afterEach(() => { attrsCallBack.mockClear(); });
it('should render style.attrs correctly', () => {
expect(wrapper.length).toBe(defaultColumns.length);
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).toHaveBeenCalledTimes(1);
expect(attrsCallBack).toHaveBeenCalledWith(
row[columns[columnIndex].dataField], row, rowIndex, columnIndex);
});
});
});
});

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import RowSection from '../src/row-section'; import RowSection from '../../src/row/row-section';
describe('Row', () => { describe('Row', () => {
const colSpan = 3; const colSpan = 3;

View File

@@ -0,0 +1,164 @@
import React from 'react';
import { shallow } from 'enzyme';
import shouldUpdater from '../../src/row/should-updater';
describe('Row shouldUpdater', () => {
let wrapper;
let props;
let nextProps;
class DummyComponent extends shouldUpdater(React.Component) {
render() { return null; }
}
describe('shouldUpdateByCellEditing', () => {
describe('when nextProps.clickToEdit and nexrProps.dbclickToEdit both are negative', () => {
beforeEach(() => {
props = {
editingRowIdx: null,
rowIndex: 0
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should always return false', () => {
nextProps = { ...props, editingRowIdx: 0 };
expect(wrapper.instance().shouldUpdateByCellEditing(nextProps)).toBeFalsy();
});
});
describe('when nextProps.editingRowIdx eq props.rowIndex and it\' not null', () => {
beforeEach(() => {
props = {
clickToEdit: true,
editingRowIdx: null,
rowIndex: 0
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, editingRowIdx: 0 };
expect(wrapper.instance().shouldUpdateByCellEditing(nextProps)).toBeTruthy();
});
});
describe('when props.editingRowIdx eq props.rowIndex but nextProps.editingRowIdx is null', () => {
beforeEach(() => {
props = {
clickToEdit: true,
editingRowIdx: 0,
rowIndex: 0
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, editingRowIdx: null };
expect(wrapper.instance().shouldUpdateByCellEditing(nextProps)).toBeTruthy();
});
});
});
describe('shouldUpdatedBySelfProps', () => {
describe('when nextProps.className is not eq props.className', () => {
beforeEach(() => {
props = {
className: ''
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, className: 'test' };
expect(wrapper.instance().shouldUpdatedBySelfProps(nextProps)).toBeTruthy();
});
});
describe('when nextProps.style is not eq props.style', () => {
beforeEach(() => {
props = {
style: null
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, style: { color: 'red' } };
expect(wrapper.instance().shouldUpdatedBySelfProps(nextProps)).toBeTruthy();
});
});
describe('when nextProps.attrs is not eq props.attrs', () => {
beforeEach(() => {
props = {
attrs: null
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, attrs: { onClick: jest.fn() } };
expect(wrapper.instance().shouldUpdatedBySelfProps(nextProps)).toBeTruthy();
});
});
});
describe('shouldUpdatedByNormalProps', () => {
describe('when nextProps.rowIndex is not eq props.rowIndex', () => {
beforeEach(() => {
props = {
rowIndex: 0
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, rowIndex: 1 };
expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy();
});
});
describe('when nextProps.editable is not eq props.editable', () => {
beforeEach(() => {
props = {
editable: false
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, editable: true };
expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy();
});
});
describe('when nextProps.columns.length is not eq props.columns.length', () => {
beforeEach(() => {
props = {
columns: [{ dataField: 'price', text: 'Price' }]
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, columns: [...props.columns, { dataField: 'name', text: 'Name' }] };
expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy();
});
});
describe('when nextProps.row is not eq props.row', () => {
beforeEach(() => {
props = {
row: { id: 1, name: 'test' }
};
wrapper = shallow(<DummyComponent { ...props } />);
});
it('should return true', () => {
nextProps = { ...props, row: { id: 1, name: 'test', price: 123 } };
expect(wrapper.instance().shouldUpdatedByNormalProps(nextProps)).toBeTruthy();
});
});
});
});

View File

@@ -0,0 +1,216 @@
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import RowPureContent from '../../src/row/row-pure-content';
import SimpleRow from '../../src/row/simple-row';
let defaultColumns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'price',
text: 'Price'
}];
const keyField = 'id';
const rowIndex = 1;
describe('SimpleRow', () => {
let wrapper;
const row = {
id: 1,
name: 'A',
price: 1000
};
beforeEach(() => {
defaultColumns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'price',
text: 'Price'
}];
});
describe('simplest row', () => {
beforeEach(() => {
wrapper = shallow(
<SimpleRow
keyField={ keyField }
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
/>
);
});
it('should render successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(RowPureContent)).toHaveLength(1);
});
describe('when tabIndexCell prop is enable', () => {
const visibleColumnSize = 3;
beforeEach(() => {
wrapper = shallow(
<SimpleRow
keyField={ keyField }
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
tabIndexCell
visibleColumnSize={ visibleColumnSize }
/>
);
});
it('should render correct tabIndexStart', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(RowPureContent)).toHaveLength(1);
expect(wrapper.find(RowPureContent).prop('tabIndexStart')).toBe((rowIndex * visibleColumnSize) + 1);
});
});
describe('when tabIndexCell prop is disable', () => {
const visibleColumnSize = 3;
beforeEach(() => {
wrapper = shallow(
<SimpleRow
keyField={ keyField }
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
visibleColumnSize={ visibleColumnSize }
/>
);
});
it('should always render tabIndexStart as -1', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.find(RowPureContent)).toHaveLength(1);
expect(wrapper.find(RowPureContent).prop('tabIndexStart')).toBe(-1);
});
});
});
describe('shouldComponentUpdate', () => {
let props;
let nextProps;
describe('if shouldUpdatedByNormalProps return true', () => {
beforeEach(() => {
props = {
keyField,
columns: defaultColumns,
rowIndex: 1,
row,
editable: true
};
wrapper = shallow(
<SimpleRow { ...props } />
);
});
it('should return true', () => {
nextProps = { ...props, rowIndex: 2 };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
it('should set this.shouldUpdateRowContent as true', () => {
nextProps = { ...props, rowIndex: 2 };
wrapper.instance().shouldComponentUpdate(nextProps);
expect(wrapper.instance().shouldUpdateRowContent).toBe(true);
});
});
describe('if shouldUpdatedByNormalProps return false', () => {
beforeEach(() => {
props = {
keyField,
columns: defaultColumns,
rowIndex: 1,
row,
editable: true
};
wrapper = shallow(
<SimpleRow { ...props } />
);
});
it('should return value which depends on the result of shouldUpdatedBySelfProps', () => {
nextProps = { ...props, className: 'test' };
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
it('should always set this.shouldUpdateRowContent as false', () => {
nextProps = { ...props, className: 'test' };
wrapper.instance().shouldComponentUpdate(nextProps);
expect(wrapper.instance().shouldUpdateRowContent).toBe(false);
});
});
});
describe('when style prop is defined', () => {
const customStyle = { backgroundColor: 'red' };
beforeEach(() => {
wrapper = shallow(
<SimpleRow
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
style={ customStyle }
/>);
});
it('should render component with style successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.prop('style')).toEqual(customStyle);
});
});
describe('when className prop is defined', () => {
const className = 'test-class';
beforeEach(() => {
wrapper = shallow(
<SimpleRow
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
className={ className }
/>);
});
it('should render component with className successfully', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.hasClass(className)).toBe(true);
});
});
describe('when attrs prop is defined', () => {
const customClickCallBack = sinon.stub();
const attrs = { 'data-index': 1, onClick: customClickCallBack };
beforeEach(() => {
wrapper = shallow(
<SimpleRow
rowIndex={ rowIndex }
columns={ defaultColumns }
row={ row }
attrs={ attrs }
/>);
});
it('should render component with correct attributes', () => {
expect(wrapper.length).toBe(1);
expect(wrapper.prop('data-index')).toBe(attrs['data-index']);
expect(wrapper.prop('onClick')).toBeDefined();
});
});
});

View File

@@ -1,16 +1,25 @@
import Const from '../../../src/const'; import Const from '../../../src/const';
const { ROW_SELECT_DISABLED, UNABLE_TO_CELL_EDIT } = Const; const { ROW_SELECT_DISABLED } = Const;
export const rowSelectionResolvedProps = { export const rowSelectionResolvedProps = {
mode: ROW_SELECT_DISABLED mode: ROW_SELECT_DISABLED,
selected: [],
hideSelectColumn: true
};
export const expandRowResolvedProps = {
renderer: undefined,
expanded: []
}; };
export const cellEditResolvedProps = { export const cellEditResolvedProps = {
mode: UNABLE_TO_CELL_EDIT mode: null,
nonEditableRows: []
}; };
export default { export default {
cellEdit: cellEditResolvedProps, cellEdit: cellEditResolvedProps,
expandRow: expandRowResolvedProps,
selectRow: rowSelectionResolvedProps selectRow: rowSelectionResolvedProps
}; };

View File

@@ -3,9 +3,17 @@ import Const from '../../../src/const';
const { ROW_SELECT_DISABLED } = Const; const { ROW_SELECT_DISABLED } = Const;
export const rowSelectionResolvedProps = { export const rowSelectionResolvedProps = {
mode: ROW_SELECT_DISABLED mode: ROW_SELECT_DISABLED,
selected: [],
hideSelectColumn: true
};
export const expandRowResolvedProps = {
renderer: undefined,
expanded: []
}; };
export default { export default {
selectRow: rowSelectionResolvedProps selectRow: rowSelectionResolvedProps,
expandRow: expandRowResolvedProps
}; };

111
yarn.lock
View File

@@ -332,6 +332,14 @@ array-unique@^0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
array.prototype.flat@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4"
dependencies:
define-properties "^1.1.2"
es-abstract "^1.10.0"
function-bind "^1.1.1"
arrify@^1.0.0, arrify@^1.0.1: arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@@ -2692,24 +2700,26 @@ entities@^1.1.1, entities@~1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
enzyme-adapter-react-16@1.1.1: enzyme-adapter-react-16.3@1.0.0:
version "1.1.1" version "1.0.0"
resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz#a8f4278b47e082fbca14f5bfb1ee50ee650717b4" resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16.3/-/enzyme-adapter-react-16.3-1.0.0.tgz#d3992301aba46c8cceab21c1d201e85f01c93bfc"
dependencies:
enzyme-adapter-utils "^1.3.0"
lodash "^4.17.4"
object.assign "^4.0.4"
object.values "^1.0.4"
prop-types "^15.6.0"
react-reconciler "^0.7.0"
react-test-renderer "^16.0.0-0"
enzyme-adapter-utils@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.4.0.tgz#c403b81e8eb9953658569e539780964bdc98de62"
dependencies: dependencies:
enzyme-adapter-utils "^1.5.0"
function.prototype.name "^1.1.0"
object.assign "^4.1.0" object.assign "^4.1.0"
prop-types "^15.6.0" object.values "^1.0.4"
prop-types "^15.6.2"
react-is "^16.4.1"
react-reconciler "^0.7.0"
react-test-renderer "~16.3.0-0"
enzyme-adapter-utils@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.5.0.tgz#a020ab3ae79bb1c85e1d51f48f35e995e0eed810"
dependencies:
function.prototype.name "^1.1.0"
object.assign "^4.1.0"
prop-types "^15.6.2"
enzyme-to-json@3.3.4: enzyme-to-json@3.3.4:
version "3.3.4" version "3.3.4"
@@ -2717,20 +2727,21 @@ enzyme-to-json@3.3.4:
dependencies: dependencies:
lodash "^4.17.4" lodash "^4.17.4"
enzyme@3.3.0: enzyme@3.4.0:
version "3.3.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.3.0.tgz#0971abd167f2d4bf3f5bd508229e1c4b6dc50479" resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.4.0.tgz#085c66fe647d8c9c4becd1fee3042c040cda88a6"
dependencies: dependencies:
array.prototype.flat "^1.2.1"
cheerio "^1.0.0-rc.2" cheerio "^1.0.0-rc.2"
function.prototype.name "^1.0.3" function.prototype.name "^1.1.0"
has "^1.0.1" has "^1.0.3"
is-boolean-object "^1.0.0" is-boolean-object "^1.0.0"
is-callable "^1.1.3" is-callable "^1.1.4"
is-number-object "^1.0.3" is-number-object "^1.0.3"
is-string "^1.0.4" is-string "^1.0.4"
is-subset "^0.1.1" is-subset "^0.1.1"
lodash "^4.17.4" lodash "^4.17.4"
object-inspect "^1.5.0" object-inspect "^1.6.0"
object-is "^1.0.1" object-is "^1.0.1"
object.assign "^4.1.0" object.assign "^4.1.0"
object.entries "^1.0.4" object.entries "^1.0.4"
@@ -2750,6 +2761,16 @@ error-ex@^1.2.0, error-ex@^1.3.1:
dependencies: dependencies:
is-arrayish "^0.2.1" is-arrayish "^0.2.1"
es-abstract@^1.10.0:
version "1.12.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
dependencies:
es-to-primitive "^1.1.1"
function-bind "^1.1.1"
has "^1.0.1"
is-callable "^1.1.3"
is-regex "^1.0.4"
es-abstract@^1.6.1, es-abstract@^1.7.0: es-abstract@^1.6.1, es-abstract@^1.7.0:
version "1.10.0" version "1.10.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
@@ -3499,7 +3520,7 @@ function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
function.prototype.name@^1.0.3: function.prototype.name@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327"
dependencies: dependencies:
@@ -3973,6 +3994,12 @@ has@^1.0.1:
dependencies: dependencies:
function-bind "^1.0.2" function-bind "^1.0.2"
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
dependencies:
function-bind "^1.1.1"
hash-base@^2.0.0: hash-base@^2.0.0:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
@@ -4323,6 +4350,10 @@ is-callable@^1.1.1, is-callable@^1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
is-callable@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
is-ci@^1.0.10: is-ci@^1.0.10:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5"
@@ -6002,7 +6033,7 @@ object-hash@^1.1.4:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.2.0.tgz#e96af0e96981996a1d47f88ead8f74f1ebc4422b" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.2.0.tgz#e96af0e96981996a1d47f88ead8f74f1ebc4422b"
object-inspect@^1.5.0: object-inspect@^1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
@@ -6761,6 +6792,13 @@ prop-types@^15.6.0:
loose-envify "^1.3.1" loose-envify "^1.3.1"
object-assign "^4.1.1" object-assign "^4.1.1"
prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies:
loose-envify "^1.3.1"
object-assign "^4.1.1"
proxy-addr@~2.0.2: proxy-addr@~2.0.2:
version "2.0.2" version "2.0.2"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
@@ -6920,15 +6958,19 @@ rc@^1.1.7:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
react-dom@16.3.2: react-dom@16.4.0:
version "16.3.2" version "16.4.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.0.tgz#099f067dd5827ce36a29eaf9a6cdc7cbf6216b1e"
dependencies: dependencies:
fbjs "^0.8.16" fbjs "^0.8.16"
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" object-assign "^4.1.1"
prop-types "^15.6.0" prop-types "^15.6.0"
react-is@^16.3.2, react-is@^16.4.1:
version "16.4.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88"
react-reconciler@^0.7.0: react-reconciler@^0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.7.0.tgz#9614894103e5f138deeeb5eabaf3ee80eb1d026d" resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.7.0.tgz#9614894103e5f138deeeb5eabaf3ee80eb1d026d"
@@ -6945,17 +6987,18 @@ react-test-renderer@16.0.0:
fbjs "^0.8.16" fbjs "^0.8.16"
object-assign "^4.1.1" object-assign "^4.1.1"
react-test-renderer@^16.0.0-0: react-test-renderer@~16.3.0-0:
version "16.2.0" version "16.3.2"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.2.0.tgz#bddf259a6b8fcd8555f012afc8eacc238872a211" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.2.tgz#3d1ed74fda8db42521fdf03328e933312214749a"
dependencies: dependencies:
fbjs "^0.8.16" fbjs "^0.8.16"
object-assign "^4.1.1" object-assign "^4.1.1"
prop-types "^15.6.0" prop-types "^15.6.0"
react-is "^16.3.2"
react@16.3.2: react@16.4.0:
version "16.3.2" version "16.4.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585"
dependencies: dependencies:
fbjs "^0.8.16" fbjs "^0.8.16"
loose-envify "^1.1.0" loose-envify "^1.1.0"