Compare commits

..

73 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
AllenFang
ae0cd8a32f Publish
- react-bootstrap-table2-editor@1.1.0
 - react-bootstrap-table2-example@1.0.5
 - react-bootstrap-table2-toolkit@1.1.0
 - react-bootstrap-table-next@1.2.0
2018-09-30 14:26:34 +08:00
Allen
51c82cdfb3 Merge pull request #576 from react-bootstrap-table/develop
20180930 release
2018-09-30 14:24:30 +08:00
Allen
8e087329b3 Merge pull request #575 from react-bootstrap-table/bugfix/558
fix #558
2018-09-29 17:17:44 +08:00
AllenFang
ee2885d055 fix #558 2018-09-29 16:13:09 +08:00
Allen
901307e471 Merge pull request #574 from react-bootstrap-table/feat/564
Implement expandRow.onlyOneExpanding
2018-09-29 15:42:29 +08:00
AllenFang
4ff5be706a patch docs and add story for #564 2018-09-29 15:30:04 +08:00
AllenFang
f8a3fedbb2 fix #564 2018-09-29 15:29:42 +08:00
Allen
0bf5831b4e Merge pull request #573 from react-bootstrap-table/feat/567
Implement selectRow.hideSelectAll
2018-09-29 15:21:23 +08:00
AllenFang
dd0b8c6b0f add story and patch docs for #567 2018-09-29 15:13:23 +08:00
AllenFang
8f028d9dd4 fix #567, add selectRow.hideSelectAll 2018-09-29 15:12:27 +08:00
Allen
2c68f22646 fix #543 (#572) 2018-09-29 14:46:29 +08:00
Allen
02d566bb32 Merge pull request #571 from react-bootstrap-table/feat/541
Support default search
2018-09-29 14:37:36 +08:00
AllenFang
2b12045017 patch docs and add story for #541 2018-09-29 14:32:57 +08:00
AllenFang
0cdf086d56 fix #541 2018-09-29 14:31:10 +08:00
Allen
d4fa9a84e3 fix #538 (#570) 2018-09-29 14:00:33 +08:00
Allen
c84fc84b9e Merge pull request #569 from react-bootstrap-table/feat/527
Implement auto select input text when editing cell
2018-09-29 13:40:21 +08:00
AllenFang
ad8cdde513 patch docs and story for #527 2018-09-29 13:30:35 +08:00
AllenFang
db19e7dd9b fix #527 2018-09-29 13:30:35 +08:00
pnthang01
33b36e5108 add onContextMenu event (#556) 2018-09-29 12:54:23 +08:00
Darío Hereñú
7209441eb6 Typo on #16? (#552) 2018-09-21 00:05:46 +08:00
95 changed files with 4607 additions and 2811 deletions

View File

@@ -13,7 +13,7 @@ Rebuilt [react-bootstrap-table](https://github.com/AllenFang/react-bootstrap-tab
* [`react-bootstrap-table2-overlay`](https://www.npmjs.com/package/react-bootstrap-table2-overlay)
* [`react-bootstrap-table2-toolkit`](https://www.npmjs.com/package/react-bootstrap-table2-toolkit)
This can help your application with less bundled size and also help us have clean design to avoid handling to much logic in kernal module(SRP).
This can help your application with less bundled size and also help us have clean design to avoid handling to much logic in kernel module(SRP).
## Migration
If you are the user from legacy [`react-bootstrap-table`](https://github.com/AllenFang/react-bootstrap-table/), please have a look on [this](./docs/migration.md).

View File

@@ -17,6 +17,7 @@
* [hover](#hover)
* [condensed](#condensed)
* [id](#id)
* [tabIndexCell](#tabIndexCell)
* [classes](#classes)
* [wrapperClasses](#wrapperClasses)
* [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>
Customize id on `table` element.
### <a name='tabIndexCell'>tabIndexCell - [Bool]</a>
Enable the `tabIndex` attribute on `<td>` element.
### <a name='classes'>classes - [String]</a>
Customize class on `table` element.

View File

@@ -10,6 +10,7 @@ $ npm install react-bootstrap-table2-editor --save
* [blurToSave](#blurToSave)
* [nonEditableRows](#nonEditableRows)
* [timeToCloseMessage](#timeToCloseMessage)
* [autoSelectText](#autoSelectText)
* [beforeSaveCell](#beforeSaveCell)
* [afterSaveCell](#afterSaveCell)
* [errorMessage](#errorMessage)
@@ -43,6 +44,11 @@ Default is `false`, enable it will be able to save the cell automatically when b
### <a name='nonEditableRows'>cellEdit.nonEditableRows - [Function]</a>
`cellEdit.nonEditableRows` accept a callback function and expect return an array which used to restrict all the columns of some rows as non-editable. So the each item in return array should be rowkey(`keyField`)
### <a name='autoSelectText'>cellEdit.autoSelectText - [Bool]</a>
Default is false, when enable it, `react-bootstrap-table2` will help you to select the text in the text input automatically when editing.
> NOTE: This props only work for `text` and `textarea`.
### <a name='timeToCloseMessage'>cellEdit.timeToCloseMessage - [Function]</a>
If a [`column.validator`](./columns.md#validator) defined and the new value is invalid, `react-bootstrap-table2` will popup a alert at the bottom of editor. `cellEdit.timeToCloseMessage` is a chance to let you decide how long the alert should be stay. Default is 3000 millisecond.

View File

@@ -13,6 +13,7 @@ Available properties in a column object:
* [formatExtraData](#formatExtraData)
* [sort](#sort)
* [sortFunc](#sortFunc)
* [sortCaret](#sortCaret)
* [onSort](#onSort)
* [classes](#classes)
* [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>
It's available to have custom class on table column:

View File

@@ -13,6 +13,8 @@
* [onExpand](#onExpand)
* [onExpandAll](#onExpandAll)
* [showExpandColumn](#showExpandColumn)
* [onlyOneExpanding](#onlyOneExpanding)
* [expandByColumnOnly](#expandByColumnOnly)
* [expandColumnRenderer](#expandColumnRenderer)
* [expandHeaderColumnRenderer](#expandHeaderColumnRenderer)
@@ -127,3 +129,24 @@ const expandRow = {
showExpandColumn: true
};
```
### <a name='onlyOneExpanding'>expandRow.onlyOneExpanding - [Bool]</a>
Default is `false`. Enable this will only allow one row get expand at the same time.
```js
const expandRow = {
renderer: (row) => ...
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,10 +12,12 @@
* [bgColor](#bgColor)
* [nonSelectable)](#nonSelectable)
* [clickToSelect)](#clickToSelect)
* [clickToExpand)](#clickToExpand)
* [clickToEdit](#clickToEdit)
* [onSelect](#onSelect)
* [onSelectAll](#onSelectAll)
* [hideSelectColumn](#hideSelectColumn)
* [hideSelectAll](#hideSelectAll)
* [selectionRenderer](#selectionRenderer)
* [selectionHeaderRenderer](#selectionHeaderRenderer)
@@ -147,6 +149,16 @@ const selectRow = {
> 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)
### <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>
Able to click to edit cell and select row
@@ -222,3 +234,13 @@ const selectRow = {
bgColor: 'red'
};
```
### <a name='hideSelectAll'>selectRow.hideSelectAll - [Bool]</a>
Default is `false`, if you don't want to render the select all checkbox on the header of selection column, give this prop as `true`!
```js
const selectRow = {
mode: 'checkbox',
hideSelectAll: true
};
```

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';
const configureEnzyme = () => {

View File

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

View File

@@ -1,16 +1,16 @@
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 {
EDITTYPE,
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK
} from './src/const';
export default (options = {}) => ({
createContext,
editingCellFactory,
CLICK_TO_CELL_EDIT,
createEditingCell,
withRowLevelCellEdit,
DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK,
options

View File

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

View File

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

@@ -24,6 +24,7 @@ export default (_, onStartEdit) =>
onUpdate: PropTypes.func.isRequired,
onEscape: PropTypes.func.isRequired,
timeToCloseMessage: PropTypes.number,
autoSelectText: PropTypes.bool,
className: PropTypes.string,
style: PropTypes.object
}
@@ -31,6 +32,7 @@ export default (_, onStartEdit) =>
static defaultProps = {
timeToCloseMessage: TIME_TO_CLOSE_MESSAGE,
className: null,
autoSelectText: false,
style: {}
}
@@ -121,7 +123,7 @@ export default (_, onStartEdit) =>
render() {
let editor;
const { row, column, className, style, rowIndex, columnIndex } = this.props;
const { row, column, className, style, rowIndex, columnIndex, autoSelectText } = this.props;
const { dataField } = column;
const value = _.get(row, dataField);
@@ -174,13 +176,13 @@ export default (_, onStartEdit) =>
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.SELECT) {
editor = <DropdownEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.TEXTAREA) {
editor = <TextAreaEditor { ...editorProps } />;
editor = <TextAreaEditor { ...editorProps } autoSelectText={ autoSelectText } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.CHECKBOX) {
editor = <CheckBoxEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.DATE) {
editor = <DateEditor { ...editorProps } />;
} else {
editor = <TextEditor { ...editorProps } />;
editor = <TextEditor { ...editorProps } autoSelectText={ autoSelectText } />;
}
return (

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

@@ -5,9 +5,10 @@ import PropTypes from 'prop-types';
class TextEditor extends Component {
componentDidMount() {
const { defaultValue, didMount } = this.props;
const { defaultValue, didMount, autoSelectText } = this.props;
this.text.value = defaultValue;
this.text.focus();
if (autoSelectText) this.text.select();
if (didMount) didMount();
}
@@ -16,7 +17,7 @@ class TextEditor extends Component {
}
render() {
const { defaultValue, didMount, className, ...rest } = this.props;
const { defaultValue, didMount, className, autoSelectText, ...rest } = this.props;
const editorClass = cs('form-control editor edit-text', className);
return (
<input
@@ -38,11 +39,13 @@ TextEditor.propTypes = {
PropTypes.string,
PropTypes.number
]),
autoSelectText: PropTypes.bool,
didMount: PropTypes.func
};
TextEditor.defaultProps = {
className: null,
defaultValue: '',
autoSelectText: false,
didMount: undefined
};
export default TextEditor;

View File

@@ -10,9 +10,10 @@ class TextAreaEditor extends Component {
}
componentDidMount() {
const { defaultValue, didMount } = this.props;
const { defaultValue, didMount, autoSelectText } = this.props;
this.text.value = defaultValue;
this.text.focus();
if (autoSelectText) this.text.select();
if (didMount) didMount();
}
@@ -28,7 +29,7 @@ class TextAreaEditor extends Component {
}
render() {
const { defaultValue, didMount, className, ...rest } = this.props;
const { defaultValue, didMount, className, autoSelectText, ...rest } = this.props;
const editorClass = cs('form-control editor edit-textarea', className);
return (
<textarea
@@ -52,11 +53,13 @@ TextAreaEditor.propTypes = {
PropTypes.number
]),
onKeyDown: PropTypes.func,
autoSelectText: PropTypes.bool,
didMount: PropTypes.func
};
TextAreaEditor.defaultProps = {
className: '',
defaultValue: '',
autoSelectText: false,
onKeyDown: undefined,
didMount: undefined
};

View File

@@ -3,14 +3,13 @@ import React from 'react';
import { shallow } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils';
import dataOperator from 'react-bootstrap-table-next/src/store/operators';
import BootstrapTable from 'react-bootstrap-table-next/src/bootstrap-table';
import {
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK
} from '../src/const';
import createCellEditContext from '../src/context';
import createCellEditContext, { Consumer } from '../src/context';
import cellEditFactory from '../index';
describe('CellEditContext', () => {
@@ -42,14 +41,7 @@ describe('CellEditContext', () => {
const defaultSelectRow = undefined;
const mockBase = jest.fn((props => (
<BootstrapTable
data={ data }
columns={ columns }
keyField={ keyField }
{ ...props }
/>
)));
const mockBase = jest.fn((() => null));
const handleCellChange = jest.fn();
@@ -75,11 +67,11 @@ describe('CellEditContext', () => {
selectRow={ selectRow }
data={ data }
>
<CellEditContext.Consumer>
<Consumer>
{
cellEditProps => mockBase(cellEditProps)
}
</CellEditContext.Consumer>
</Consumer>
</CellEditContext.Provider>
);
}
@@ -94,10 +86,6 @@ describe('CellEditContext', () => {
expect(CellEditContext.Provider).toBeDefined();
});
it('should have correct Consumer property after calling createCellEditContext', () => {
expect(CellEditContext.Consumer).toBeDefined();
});
it('should have correct state.ridx', () => {
expect(wrapper.state().ridx).toBeNull();
});
@@ -113,14 +101,11 @@ describe('CellEditContext', () => {
it('should pass correct cell editing props to children element', () => {
expect(wrapper.length).toBe(1);
expect(JSON.stringify(mockBase.mock.calls[0])).toEqual(JSON.stringify([{
cellEdit: {
...defaultCellEdit,
CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK,
...wrapper.state(),
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 BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import { productsGenerator } from 'utils/common';
const products = productsGenerator(5000);
const products = productsGenerator(3000);
const columns = [{
dataField: 'id',
@@ -17,16 +16,25 @@ const columns = [{
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 () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
selectRow={ { mode: 'checkbox' } }
cellEdit={ cellEditFactory({
mode: 'click'
}) }
selectRow={ { mode: 'checkbox', clickToSelect: true } }
expandRow={ expandRow }
/>
</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,78 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { jobsGenerator } from 'utils/common';
const jobs = jobsGenerator();
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.TEXTAREA
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.TEXTAREA
}
}];
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={
cellEditFactory({
mode: 'click',
autoSelectText: true
})
}
/>
`;
export default () => (
<div>
<h3>Auto Select Text Input Field When Editing</h3>
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={
cellEditFactory({
mode: 'click',
autoSelectText: true
})
}
/>
<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,73 @@
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 = {
onlyOneExpanding: 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 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>
)
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
`;
export default () => (
<div>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
expandRow={ expandRow }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,59 @@
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,
hideSelectAll: 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 selectRow = {
mode: 'checkbox',
clickToSelect: true,
hideSelectAll: true
};
<BootstrapTable
keyField='id'
data={ products }
columns={ columns }
selectRow={ selectRow }
/>
`;
export default () => (
<div>
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow } />
<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-alert: 0 */
/* eslint no-console: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
@@ -21,7 +21,10 @@ const columns = [{
const rowEvents = {
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 = {
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 () => (
<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 } />
<Code>{ sourceCode }</Code>
</div>

View File

@@ -0,0 +1,83 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
import Code from 'components/common/code-block';
import { productsGenerator } from 'utils/common';
const { SearchBar } = Search;
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';
import ToolkitProvider, { Search } from 'react-bootstrap-table2-toolkit';
const { SearchBar } = Search;
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'price',
text: 'Product Price'
}];
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ { defaultSearch: '2101' } }
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
`;
export default () => (
<div>
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ { defaultSearch: '2101' } }
>
{
props => (
<div>
<h3>Input something at below input field:</h3>
<SearchBar { ...props.searchProps } />
<hr />
<BootstrapTable
{ ...props.baseProps }
/>
</div>
)
}
</ToolkitProvider>
<Code>{ sourceCode }</Code>
</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 Typed from 'typed.js';
const PROJECT_NAME = 'react-bootstrap-table2';
export default class Welcome extends React.Component {
componentDidMount() {
// type.js config
@@ -21,14 +23,21 @@ export default class Welcome extends React.Component {
return (
<div>
<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
className="welcome-sub-title"
ref={ (el) => { this.el = el; } }
/>
</div>
<a href="https://github.com/react-bootstrap-table/react-bootstrap-table2" 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">
<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: '#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="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" />

View File

@@ -1,6 +1,6 @@
{
"name": "react-bootstrap-table2-example",
"version": "1.0.4",
"version": "1.0.7",
"description": "",
"main": "index.js",
"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 CaptionTable from 'examples/basic/caption-table';
import LargeTable from 'examples/basic/large-table';
import ExposedAPITable from 'examples/basic/exposed-function';
import TabIndexCellTable from 'examples/basic/tabindex-column';
// bootstrap 4
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 SortEvents from 'examples/sort/sort-events';
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 HeaderSortingStyleTable from 'examples/sort/header-sorting-style';
@@ -97,8 +100,10 @@ import CellEditHooks from 'examples/cell-edit/cell-edit-hooks-table';
import CellEditValidator from 'examples/cell-edit/cell-edit-validator-table';
import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table';
import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
import AutoSelectTextInput from 'examples/cell-edit/auto-select-text-input-table';
import EditorStyleTable from 'examples/cell-edit/editor-style-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 TextareaEditorTable from 'examples/cell-edit/textarea-editor-table';
import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table';
@@ -112,9 +117,11 @@ import ClickToSelectTable from 'examples/row-selection/click-to-select';
import DefaultSelectTable from 'examples/row-selection/default-select';
import SelectionManagement from 'examples/row-selection/selection-management';
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 SelectionStyleTable from 'examples/row-selection/selection-style';
import SelectionClassTable from 'examples/row-selection/selection-class';
import HideSelectAllTable from 'examples/row-selection/hide-select-all';
import CustomSelectionTable from 'examples/row-selection/custom-selection';
import NonSelectableRowsTable from 'examples/row-selection/non-selectable-rows';
import SelectionBgColorTable from 'examples/row-selection/selection-bgcolor';
@@ -126,6 +133,8 @@ import BasicRowExpand from 'examples/row-expand';
import RowExpandManagement from 'examples/row-expand/expand-management';
import NonExpandableRows from 'examples/row-expand/non-expandable-rows';
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 CustomExpandColumn from 'examples/row-expand/custom-expand-column';
import ExpandHooks from 'examples/row-expand/expand-hooks';
@@ -136,6 +145,7 @@ import CustomPaginationTable from 'examples/pagination/custom-pagination';
// search
import SearchTable from 'examples/search';
import DefaultSearch from 'examples/search/default-search';
import DefaultCustomSearch from 'examples/search/default-custom-search';
import FullyCustomSearch from 'examples/search/fully-custom-search';
import SearchFormattedData from 'examples/search/search-formatted';
@@ -185,7 +195,9 @@ storiesOf('Basic Table', module)
.add('Indication For Empty Table', () => <NoDataTable />)
.add('Customized id and class table', () => <CustomizedIdClassesTable />)
.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)
.addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR))
@@ -263,6 +275,7 @@ storiesOf('Sort Table', module)
.add('Default Sort Direction Table', () => <DefaultSortDirectionTable />)
.add('Sort Events', () => <SortEvents />)
.add('Custom Sort Fuction', () => <CustomSortTable />)
.add('Custom Sort Caret', () => <CustomSortCaretTable />)
.add('Custom Classes on Sorting Header Column', () => <HeaderSortingClassesTable />)
.add('Custom Style on Sorting Header Column', () => <HeaderSortingStyleTable />);
@@ -276,10 +289,12 @@ storiesOf('Cell Editing', module)
.add('Cell Level Editable', () => <CellLevelEditable />)
.add('Rich Hook Functions', () => <CellEditHooks />)
.add('Validation', () => <CellEditValidator />)
.add('Auto Select Text Input', () => <AutoSelectTextInput />)
.add('Custom Cell Style', () => <CellEditStyleTable />)
.add('Custom Cell Classes', () => <CellEditClassTable />)
.add('Custom Editor Classes', () => <EditorClassTable />)
.add('Custom Editor Style', () => <EditorStyleTable />)
.add('DoubleClick to Edit with Selection', () => <DBClickEditWithSelection />)
.add('Dropdown Editor', () => <DropdownEditorTable />)
.add('Textarea Editor', () => <TextareaEditorTable />)
.add('Checkbox Editor', () => <CheckboxEditorTable />)
@@ -294,9 +309,11 @@ storiesOf('Row Selection', module)
.add('Default Select', () => <DefaultSelectTable />)
.add('Selection Management', () => <SelectionManagement />)
.add('Click to Select and Edit Cell', () => <ClickToSelectWithCellEditTable />)
.add('Row Select and Expand', () => <SelectionWithExpansionTable />)
.add('Selection without Data', () => <SelectionNoDataTable />)
.add('Selection Style', () => <SelectionStyleTable />)
.add('Selection Class', () => <SelectionClassTable />)
.add('Hide Select All', () => <HideSelectAllTable />)
.add('Custom Selection', () => <CustomSelectionTable />)
.add('Selection Background Color', () => <SelectionBgColorTable />)
.add('Not Selectabled Rows', () => <NonSelectableRowsTable />)
@@ -309,6 +326,8 @@ storiesOf('Row Expand', module)
.add('Expand Management', () => <RowExpandManagement />)
.add('Non Expandabled Rows', () => <NonExpandableRows />)
.add('Expand Indicator', () => <ExpandColumn />)
.add('Only Expand by Indicator', () => <OnlyExpandByColumn />)
.add('Expand Only One Row at The Same Time', () => <ExpandOnlyOne />)
.add('Custom Expand Indicator', () => <CustomExpandColumn />)
.add('Expand Hooks', () => <ExpandHooks />);
@@ -321,6 +340,7 @@ storiesOf('Pagination', module)
storiesOf('Table Search', module)
.addDecorator(bootstrapStyle())
.add('Basic Search Table', () => <SearchTable />)
.add('Default Search Table', () => <DefaultSearch />)
.add('Default Custom Search', () => <DefaultCustomSearch />)
.add('Fully Custom Search', () => <FullyCustomSearch />)
.add('Search Fromatted Value', () => <SearchFormattedData />)

View File

@@ -1,3 +1,5 @@
$logo-size: 96px;
.welcome {
margin-top: 70px;
text-align: center;
@@ -5,7 +7,22 @@
&-title {
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 {
font-size: 30px;
color: $grey-500;

View File

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

View File

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

View File

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

View File

@@ -68,6 +68,9 @@ export default (
currPage = newPage;
needNewState = true;
}
} else {
this.currPage = nextProps.pagination.options.page;
this.currSizePerPage = nextProps.pagination.options.sizePerPage;
}
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', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({

View File

@@ -63,6 +63,22 @@ const { SearchBar } = Search;
### Search Options
#### defaultSearch - [string]
Accept a string that will be used for default searching when first time table render.
```js
<ToolkitProvider
keyField="id"
data={ products }
columns={ columns }
search={ {
defaultSearch: 'search something here'
} }
>
// ...
</ToolkitProvider>
```
#### searchFormatted - [bool]
If you want to search on the formatted data, you are supposed to enable this props. `react-bootstrap-table2` will check if you define the `column.formatter` when doing search.

View File

@@ -17,6 +17,7 @@ class ToolkitProvider extends statelessDrcorator(React.Component) {
search: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.shape({
defaultSearch: PropTypes.string,
searchFormatted: PropTypes.bool
})
]),
@@ -42,7 +43,7 @@ class ToolkitProvider extends statelessDrcorator(React.Component) {
constructor(props) {
super(props);
this.state = {
searchText: ''
searchText: typeof props.search === 'object' ? (props.search.defaultSearch || '') : ''
};
this._ = null;
this.onSearch = this.onSearch.bind(this);
@@ -85,6 +86,7 @@ class ToolkitProvider extends statelessDrcorator(React.Component) {
return (
<ToolkitContext.Provider value={ {
searchProps: {
searchText: this.state.searchText,
onSearch: this.onSearch
},
csvProps: {

View File

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

View File

@@ -58,7 +58,7 @@ export const save = (
}
) => {
FileSaver.saveAs(
new Blob(['\ufeff', content], { type: 'text/plain;charset=utf-8' }),
new Blob([content], { type: 'text/plain;charset=utf-8' }),
fileName,
noAutoBOM
);

View File

@@ -47,6 +47,7 @@ const SearchBar = ({
style={ style }
onKeyUp={ () => debounceCallback() }
className={ `form-control ${className}` }
defaultValue={ searchText }
placeholder={ placeholder || SearchBar.defaultProps.placeholder }
{ ...rest }
/>

View File

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

View File

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

View File

@@ -3,35 +3,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import cs from 'classnames';
import _ from './utils';
import Row from './row';
import ExpandRow from './row-expand/expand-row';
import RowSection from './row-section';
import SimpleRow from './row/simple-row';
import RowAggregator from './row/aggregate-row';
import RowSection from './row/row-section';
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 {
columns,
data,
tabIndexCell,
keyField,
isEmpty,
noDataIndication,
visibleColumnSize,
cellEdit,
selectRow,
selectedRowKeys,
rowStyle,
rowClasses,
rowEvents,
expandRow
} = props;
const {
bgColor,
nonSelectable
} = selectRow;
} = this.props;
let content;
@@ -42,90 +46,63 @@ const Body = (props) => {
}
content = <RowSection content={ indication } colSpan={ visibleColumnSize } />;
} 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) => {
const key = _.get(row, keyField);
const editable = !(nonEditableRows.length > 0 && nonEditableRows.indexOf(key) > -1);
const selected = selectRow.mode !== Const.ROW_SELECT_DISABLED
? selectedRowKeys.includes(key)
: null;
const attrs = rowEvents || {};
let style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle;
let classes = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses);
if (selected) {
const selectedStyle = _.isFunction(selectRow.style)
? selectRow.style(row, index)
: selectRow.style;
const selectedClasses = _.isFunction(selectRow.classes)
? selectRow.classes(row, index)
: selectRow.classes;
style = {
...style,
...selectedStyle
const baseRowProps = {
key,
row,
tabIndexCell,
columns,
keyField,
cellEdit,
value: key,
rowIndex: index,
visibleColumnSize,
attrs: rowEvents || {},
...additionalRowProps
};
classes = cs(classes, selectedClasses);
if (bgColor) {
style = style || {};
style.backgroundColor = _.isFunction(bgColor) ? bgColor(row, index) : bgColor;
}
}
baseRowProps.style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle;
baseRowProps.className = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses);
const selectable = !nonSelectable || !nonSelectable.includes(key);
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 <RowComponent { ...baseRowProps } />;
});
}
return (
<tbody>{ content }</tbody>
);
};
}
}
Body.propTypes = {
keyField: PropTypes.string.isRequired,
data: PropTypes.array.isRequired,
columns: PropTypes.array.isRequired,
selectRow: PropTypes.object,
selectedRowKeys: PropTypes.array
selectRow: PropTypes.object
};
export default Body;

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,8 @@
/* eslint react/prop-types: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import dataOperator from '../store/operators';
export default (
dataOperator
) => {
const RowExpandContext = React.createContext();
class RowExpandProvider extends React.Component {
@@ -25,12 +23,13 @@ export default (
}
handleRowExpand = (rowKey, expanded, rowIndex, e) => {
const { data, keyField, expandRow: { onExpand } } = this.props;
const { data, keyField, expandRow: { onExpand, onlyOneExpanding } } = this.props;
let currExpanded = [...this.state.expanded];
if (expanded) {
currExpanded.push(rowKey);
if (onlyOneExpanding) currExpanded = [rowKey];
else currExpanded.push(rowKey);
} else {
currExpanded = currExpanded.filter(value => value !== rowKey);
}
@@ -73,8 +72,9 @@ export default (
return (
<RowExpandContext.Provider
value={ {
isAnyExpands: dataOperator.isAnyExpands(data, keyField, this.state.expanded),
...this.props.expandRow,
expanded: this.state.expanded,
isAnyExpands: dataOperator.isAnyExpands(data, keyField, this.state.expanded),
onRowExpand: this.handleRowExpand,
onAllRowExpand: this.handleAllRowExpand
} }
@@ -84,8 +84,8 @@ export default (
);
}
}
return {
export default {
Provider: RowExpandProvider,
Consumer: RowExpandContext.Consumer
};
};

View File

@@ -3,11 +3,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import Const from '../const';
export default (
dataOperator
) => {
const SelectionContext = React.createContext();
import dataOperator from '../store/operators';
import { getSelectionSummary } from '../store/selection';
const SelectionContext = React.createContext();
class SelectionProvider extends React.Component {
static propTypes = {
children: PropTypes.node.isRequired,
@@ -23,7 +22,7 @@ export default (
}
}
state = { selected: (this.props.selectRow && this.props.selectRow.selected) || [] };
state = { selected: this.props.selectRow.selected || [] };
componentWillReceiveProps(nextProps) {
if (nextProps.selectRow) {
@@ -80,19 +79,47 @@ export default (
}
if (onSelectAll) {
onSelectAll(!isUnSelect, dataOperator.getSelectedRows(data, keyField, currSelected), e);
onSelectAll(
!isUnSelect,
dataOperator.getSelectedRows(
data,
keyField,
isUnSelect ? this.state.selected : currSelected
),
e
);
}
this.setState(() => ({ selected: currSelected }));
}
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 (
<SelectionContext.Provider
value={ {
...this.props.selectRow,
selected: this.state.selected,
onRowSelect: this.handleRowSelect,
onAllRowsSelect: this.handleAllRowsSelect
onAllRowsSelect: this.handleAllRowsSelect,
allRowsSelected,
allRowsNotSelected,
checkedStatus
} }
>
{ this.props.children }
@@ -100,8 +127,8 @@ export default (
);
}
}
return {
export default {
Provider: SelectionProvider,
Consumer: SelectionContext.Consumer
};
};

View File

@@ -24,6 +24,7 @@ const HeaderCell = (props) => {
const {
text,
sort,
sortCaret,
filter,
filterRenderer,
headerTitle,
@@ -49,6 +50,7 @@ const HeaderCell = (props) => {
if (headerStyle) {
cellStyle = _.isFunction(headerStyle) ? headerStyle(column, index) : headerStyle;
cellStyle = cellStyle ? { ...cellStyle } : cellStyle;
}
if (headerTitle) {
@@ -68,7 +70,7 @@ const HeaderCell = (props) => {
cellAttrs.className = cs(cellAttrs.className, 'sortable');
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.
cellClasses = cs(
@@ -85,7 +87,7 @@ const HeaderCell = (props) => {
: headerSortingStyle
};
} else {
sortSymbol = <SortSymbol />;
sortSymbol = sortCaret ? sortCaret(undefined, column) : <SortSymbol />;
}
}
@@ -150,6 +152,7 @@ HeaderCell.propTypes = {
onSort: PropTypes.func,
sorting: PropTypes.bool,
sortOrder: PropTypes.oneOf([Const.SORT_ASC, Const.SORT_DESC]),
sortCaret: PropTypes.func,
isLastSorting: PropTypes.bool,
onFilter: PropTypes.func,
onExternalFilter: PropTypes.func

View File

@@ -1,15 +1,14 @@
/* eslint react/require-default-props: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import Const from './const';
import HeaderCell from './header-cell';
import SelectionHeaderCell from './row-selection/selection-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 { ROW_SELECT_DISABLED } = Const;
const {
className,
columns,
@@ -23,20 +22,24 @@ const Header = (props) => {
bootstrap4
} = props;
let SelectionHeaderCellComp = () => null;
let ExpansionHeaderCellComp = () => null;
if (expandRow.showExpandColumn) {
ExpansionHeaderCellComp = withHeaderExpansion(ExpandHeaderCell);
}
if (selectRow) {
SelectionHeaderCellComp = withHeaderSelection(SelectionHeaderCell);
}
return (
<thead>
<tr className={ className }>
<ExpansionHeaderCellComp />
{
(expandRow && expandRow.showExpandColumn)
? <ExpandHeaderCell
onAllRowExpand={ expandRow.onAllRowExpand }
anyExpands={ expandRow.isAnyExpands }
renderer={ expandRow.expandHeaderColumnRenderer }
/> : null
}
{
(selectRow.mode !== ROW_SELECT_DISABLED && !selectRow.hideSelectColumn)
? <SelectionHeaderCell { ...selectRow } /> : null
!selectRow.hideSelectColumn ?
<SelectionHeaderCellComp /> : null
}
{
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 ExpandRowResolver from './expand-row-resolver';
import Const from '../const';
import _ from '../utils';
export default ExtendBase =>
class TableResolver extends
ExpandRowResolver(ColumnResolver(ExtendBase)) {
class TableResolver extends ColumnResolver(ExtendBase) {
validateProps() {
const { keyField } = this.props;
if (!keyField) {
@@ -19,63 +15,4 @@ export default ExtendBase =>
isEmpty() {
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,11 +1,9 @@
import _ from './utils';
import Const from './const';
const events = [
'onClick',
'onDoubleClick',
'onMouseEnter',
'onMouseLeave'
'onMouseLeave',
'onContextMenu'
];
export default ExtendBase =>
@@ -14,7 +12,6 @@ export default ExtendBase =>
super(props);
this.clickNum = 0;
this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this);
this.createClickEventHandler = this.createClickEventHandler.bind(this);
}
createDefaultEventHandler(cb) {
@@ -24,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 = {}) {
const newAttrs = {};
const { expandRow, selectRow } = this.props;
if (expandRow || (selectRow && selectRow.clickToSelect)) {
newAttrs.onClick = this.createClickEventHandler(attrs.onClick);
}
const newAttrs = { ...attrs };
Object.keys(attrs).forEach((attr) => {
if (!newAttrs[attr]) {
if (events.includes(attr)) {
newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]);
} else {
newAttrs[attr] = attrs[attr];
}
}
});
return newAttrs;

View File

@@ -12,7 +12,8 @@ export default class ExpandCell extends Component {
expanded: PropTypes.bool.isRequired,
onRowExpand: PropTypes.func.isRequired,
expandColumnRenderer: PropTypes.func,
rowIndex: PropTypes.number
rowIndex: PropTypes.number,
tabIndex: PropTypes.number
}
constructor() {
@@ -20,17 +21,29 @@ export default class ExpandCell extends Component {
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) {
const { rowKey, expanded, onRowExpand, rowIndex } = this.props;
onRowExpand(rowKey, expanded, rowIndex, e);
onRowExpand(rowKey, !expanded, rowIndex, e);
}
render() {
const { expanded, expandColumnRenderer } = this.props;
const { expanded, expandColumnRenderer, tabIndex } = this.props;
const attrs = {};
if (tabIndex !== -1) attrs.tabIndex = tabIndex;
return (
<td onClick={ this.handleClick }>
<td onClick={ this.handleClick } { ...attrs }>
{
expandColumnRenderer ? expandColumnRenderer({
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 PropTypes from 'prop-types';
export default class SelectionHeaderCell extends Component {
export default class ExpansionHeaderCell extends Component {
static propTypes = {
anyExpands: PropTypes.bool.isRequired,
isAnyExpands: PropTypes.bool.isRequired,
onAllRowExpand: PropTypes.func.isRequired,
renderer: PropTypes.func
expandHeaderColumnRenderer: PropTypes.func
}
constructor() {
@@ -16,13 +16,13 @@ export default class SelectionHeaderCell extends Component {
}
handleCheckBoxClick(e) {
const { anyExpands, onAllRowExpand } = this.props;
const { isAnyExpands, onAllRowExpand } = this.props;
onAllRowExpand(e, !anyExpands);
onAllRowExpand(e, !isAnyExpands);
}
render() {
const { anyExpands, renderer } = this.props;
const { isAnyExpands, expandHeaderColumnRenderer } = this.props;
const attrs = {
onClick: this.handleCheckBoxClick
};
@@ -30,9 +30,9 @@ export default class SelectionHeaderCell extends Component {
return (
<th data-row-selection { ...attrs }>
{
renderer ?
renderer({ isAnyExpands: anyExpands }) :
(anyExpands ? '(-)' : '(+)')
expandHeaderColumnRenderer ?
expandHeaderColumnRenderer({ isAnyExpands }) :
(isAnyExpands ? '(-)' : '(+)')
}
</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,
disabled: PropTypes.bool,
rowIndex: PropTypes.number,
tabIndex: PropTypes.number,
clickToSelect: PropTypes.bool,
selectionRenderer: PropTypes.func
}
@@ -25,9 +26,14 @@ export default class SelectionCell extends Component {
}
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) {
@@ -56,14 +62,18 @@ export default class SelectionCell extends Component {
mode: inputType,
selected,
disabled,
tabIndex,
selectionRenderer
} = this.props;
const attrs = {};
if (tabIndex !== -1) attrs.tabIndex = tabIndex;
return (
<BootstrapContext.Consumer>
{
({ bootstrap4 }) => (
<td onClick={ this.handleClick }>
<td onClick={ this.handleClick } { ...attrs }>
{
selectionRenderer ? selectionRenderer({
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

@@ -27,6 +27,7 @@ export default class SelectionHeaderCell extends Component {
mode: PropTypes.string.isRequired,
checkedStatus: PropTypes.string,
onAllRowsSelect: PropTypes.func,
hideSelectAll: PropTypes.bool,
selectionHeaderRenderer: PropTypes.func
}
@@ -63,7 +64,10 @@ export default class SelectionHeaderCell extends Component {
CHECKBOX_STATUS_CHECKED, CHECKBOX_STATUS_INDETERMINATE, ROW_SELECT_MULTIPLE
} = Const;
const { mode, checkedStatus, selectionHeaderRenderer } = this.props;
const { mode, checkedStatus, selectionHeaderRenderer, hideSelectAll } = this.props;
if (hideSelectAll) {
return <th data-row-selection />;
}
const checked = checkedStatus === CHECKBOX_STATUS_CHECKED;

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,
selected = []
) => {
let allRowsSelected = true;
let allRowsSelected = data.length > 0;
let allRowsNotSelected = true;
const rowKeys = data.map(d => d[keyField]);

View File

@@ -1,11 +1,15 @@
import 'jsdom-global/register';
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import { shallow, mount } from 'enzyme';
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 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';
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', () => {
@@ -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', () => {
@@ -361,11 +254,14 @@ describe('Body', () => {
});
});
describe('when cellEdit.nonEditableRows props is defined', () => {
const nonEditableRows = [data[1].id];
describe('when cellEdit.createContext props is defined', () => {
const EditingCellComponent = () => null;
const RowComponent = props => <Row { ...props } />;
const cellEdit = {
mode: Const.CLICK_TO_CELL_EDIT,
nonEditableRows
options: { onStartEdit: jest.fn() },
createContext: jest.fn(),
createEditingCell: jest.fn().mockReturnValue(EditingCellComponent),
withRowLevelCellEdit: jest.fn().mockReturnValue(RowComponent)
};
beforeEach(() => {
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);
const rows = wrapper.find(Row);
for (let i = 0; i < rows.length; i += 1) {
if (nonEditableRows.indexOf(rows.get(i).props.row[keyField]) > -1) {
expect(rows.get(i).props.editable).toBeFalsy();
} else {
expect(rows.get(i).props.editable).toBeTruthy();
}
}
expect(cellEdit.createEditingCell).toHaveBeenCalledTimes(1);
expect(cellEdit.withRowLevelCellEdit).toHaveBeenCalledTimes(1);
expect(wrapper.find(RowComponent)).toHaveLength(2);
const aRowElement = wrapper.find(RowComponent).get(0);
expect(aRowElement.props.EditingCellComponent).toBeDefined();
});
});
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 selectedRowKey = data[0][keyField];
const selectedRowKeys = [selectedRowKey];
beforeEach(() => {
wrapper = shallow(
wrapper = mount(
<SelectionContext.Provider data={ data } keyField={ keyField } selectRow={ selectRow }>
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
/>
</SelectionContext.Provider>
);
});
it('should render Row component with correct selected prop', () => {
const rows = wrapper.find(Row);
for (let i = 0; i < rows.length; i += 1) {
const row = rows.get(i);
expect(row.props.selected).toBe(selectedRowKeys.indexOf(row.props.row[keyField]) > -1);
}
it('should render RowAggregator component correctly', () => {
const rowAggregator = wrapper.find(RowAggregator);
expect(rowAggregator.get(0).props.selectRow.mode)
.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', () => {
const style = { backgroundColor: 'red' };
describe('when expandRow.renderer is defined correctly', () => {
const expandRow = { renderer: jest.fn() };
beforeEach(() => {
selectRow.style = style;
wrapper = shallow(
wrapper = mount(
<ExpansionContext.Provider data={ data } keyField={ keyField } expandRow={ expandRow }>
<Body
{ ...mockBodyResolvedProps }
data={ data }
columns={ columns }
keyField={ keyField }
selectedRowKeys={ selectedRowKeys }
selectRow={ selectRow }
expandRow={ expandRow }
/>
</ExpansionContext.Provider>
);
});
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.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();
it('should render RowAggregator component correctly', () => {
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();
});
});
});

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('when content is change', () => {
const column = { dataField: '', text: 'Product Name', isDummyField: true };

View File

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

View File

@@ -4,7 +4,7 @@ import { shallow } from 'enzyme';
import dataOperator from '../../src/store/operators';
import BootstrapTable from '../../src/bootstrap-table';
import createSelectionContext from '../../src/contexts/selection-context';
import SelectionContext from '../../src/contexts/selection-context';
describe('DataContext', () => {
let wrapper;
@@ -42,7 +42,6 @@ describe('DataContext', () => {
const defaultSelectRow = {
mode: 'checkbox'
};
const SelectionContext = createSelectionContext(dataOperator);
function shallowContext(selectRow = defaultSelectRow) {
return (
@@ -81,9 +80,13 @@ describe('DataContext', () => {
it('should pass correct sort props to children element', () => {
expect(wrapper.length).toBe(1);
expect(mockBase).toHaveBeenCalledWith({
...defaultSelectRow,
selected: wrapper.state().selected,
onRowSelect: wrapper.instance().handleRowSelect,
onAllRowsSelect: wrapper.instance().handleAllRowsSelect
onAllRowsSelect: wrapper.instance().handleAllRowsSelect,
allRowsNotSelected: true,
allRowsSelected: false,
checkedStatus: 'unchecked'
});
});
});
@@ -220,21 +223,6 @@ describe('DataContext', () => {
it('should set state.selected correctly', () => {
expect(wrapper.state('selected')).toEqual(data.map(d => d[keyField]));
});
});
describe('when isUnSelect argument is true', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultSelectRow,
selected: data.map(d => d[keyField])
}));
wrapper.instance().handleAllRowsSelect(e, true);
});
it('should set state.selected correctly', () => {
expect(wrapper.state('selected')).toEqual([]);
});
});
describe('when selectRow.onSelectAll is defined', () => {
const onSelectAll = jest.fn();
@@ -255,4 +243,39 @@ describe('DataContext', () => {
});
});
});
describe('when isUnSelect argument is true', () => {
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultSelectRow,
selected: data.map(d => d[keyField])
}));
wrapper.instance().handleAllRowsSelect(e, true);
});
it('should set state.selected correctly', () => {
expect(wrapper.state('selected')).toEqual([]);
});
describe('when selectRow.onSelectAll is defined', () => {
const onSelectAll = jest.fn();
beforeEach(() => {
wrapper = shallow(shallowContext({
...defaultSelectRow,
selected: data.map(d => d[keyField]),
onSelectAll
}));
wrapper.instance().handleAllRowsSelect(e, true);
});
it('should call selectRow.onSelectAll correctly', () => {
expect(onSelectAll).toHaveBeenCalledWith(
false,
dataOperator.getSelectedRows(data, keyField, data.map(d => d[keyField])),
e
);
});
});
});
});
});

View File

@@ -403,6 +403,24 @@ describe('HeaderCell', () => {
it('header should render SortSymbol as default', () => {
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', () => {
@@ -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 ', () => {
const classes = 'foo';
const order = Const.SORT_DESC;

View File

@@ -1,8 +1,12 @@
import 'jsdom-global/register';
import React from 'react';
import { shallow } from 'enzyme';
import { shallow, mount } from 'enzyme';
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 Const from '../src/const';
import mockHeaderResolvedProps from './test-helpers/mock/header-resolved-props';
@@ -17,6 +21,16 @@ describe('Header', () => {
text: 'Name'
}];
const data = [{
id: 1,
name: 'A'
}, {
id: 2,
name: 'B'
}];
const keyField = 'id';
describe('simplest header', () => {
beforeEach(() => {
wrapper = shallow(<Header { ...mockHeaderResolvedProps } columns={ columns } />);
@@ -89,12 +103,18 @@ describe('Header', () => {
describe('when selectRow.mode is radio (single selection)', () => {
beforeEach(() => {
const selectRow = { mode: 'radio' };
wrapper = shallow(
wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
selectRow={ selectRow }
/>
</SelectionContext.Provider>
);
});
@@ -105,12 +125,18 @@ describe('Header', () => {
describe('when selectRow.hideSelectColumn is true', () => {
beforeEach(() => {
const selectRow = { mode: 'radio', hideSelectColumn: true };
wrapper = shallow(
wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
selectRow={ selectRow }
/>
</SelectionContext.Provider>
);
});
@@ -146,12 +172,18 @@ describe('Header', () => {
describe('when selectRow.mode is checkbox (multiple selection)', () => {
beforeEach(() => {
const selectRow = { mode: 'checkbox' };
wrapper = shallow(
wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
selectRow={ selectRow }
/>
</SelectionContext.Provider>
);
});
@@ -162,12 +194,18 @@ describe('Header', () => {
describe('when selectRow.hideSelectColumn is true', () => {
beforeEach(() => {
const selectRow = { mode: 'checkbox', hideSelectColumn: true };
wrapper = shallow(
wrapper = mount(
<SelectionContext.Provider
data={ data }
keyField={ keyField }
selectRow={ selectRow }
>
<Header
{ ...mockHeaderResolvedProps }
columns={ columns }
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 sinon from 'sinon';
import { shallow } from 'enzyme';
import { extendTo } from '../test-helpers/mock-component';
import baseResolver from '../../src/props-resolver/index';
import Const from '../../src/const';
describe('TableResolver', () => {
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;
describe('shouldComponentUpdate', () => {
const selected = true;
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);
});
});
let props;
let nextProps;
describe('when selected prop has been changed', () => {
it('should update component', () => {
const nextProps = { selected: !selected };
beforeEach(() => {
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);
});
});

View File

@@ -104,6 +104,22 @@ describe('<SelectionHeaderCell />', () => {
});
describe('render', () => {
describe('when props.hideSelectAll is true', () => {
beforeEach(() => {
const checkedStatus = Const.CHECKBOX_STATUS_CHECKED;
wrapper = shallow(
<SelectionHeaderCell mode="checkbox" checkedStatus={ checkedStatus } hideSelectAll />
);
});
it('should render empty th element', () => {
expect(wrapper.find('th').length).toBe(1);
expect(wrapper.find('th[data-row-selection]').length).toBe(1);
expect(wrapper.find(CheckBox).length).toBe(0);
});
});
describe('when props.mode is radio', () => {
beforeEach(() => {
const checkedStatus = Const.CHECKBOX_STATUS_CHECKED;

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 { shallow } from 'enzyme';
import RowSection from '../src/row-section';
import RowSection from '../../src/row/row-section';
describe('Row', () => {
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';
const { ROW_SELECT_DISABLED, UNABLE_TO_CELL_EDIT } = Const;
const { ROW_SELECT_DISABLED } = Const;
export const rowSelectionResolvedProps = {
mode: ROW_SELECT_DISABLED
mode: ROW_SELECT_DISABLED,
selected: [],
hideSelectColumn: true
};
export const expandRowResolvedProps = {
renderer: undefined,
expanded: []
};
export const cellEditResolvedProps = {
mode: UNABLE_TO_CELL_EDIT
mode: null,
nonEditableRows: []
};
export default {
cellEdit: cellEditResolvedProps,
expandRow: expandRowResolvedProps,
selectRow: rowSelectionResolvedProps
};

View File

@@ -3,9 +3,17 @@ import Const from '../../../src/const';
const { ROW_SELECT_DISABLED } = Const;
export const rowSelectionResolvedProps = {
mode: ROW_SELECT_DISABLED
mode: ROW_SELECT_DISABLED,
selected: [],
hideSelectColumn: true
};
export const expandRowResolvedProps = {
renderer: undefined,
expanded: []
};
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"
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:
version "1.0.1"
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"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
enzyme-adapter-react-16@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz#a8f4278b47e082fbca14f5bfb1ee50ee650717b4"
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"
enzyme-adapter-react-16.3@1.0.0:
version "1.0.0"
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.5.0"
function.prototype.name "^1.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:
version "3.3.4"
@@ -2717,20 +2727,21 @@ enzyme-to-json@3.3.4:
dependencies:
lodash "^4.17.4"
enzyme@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.3.0.tgz#0971abd167f2d4bf3f5bd508229e1c4b6dc50479"
enzyme@3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.4.0.tgz#085c66fe647d8c9c4becd1fee3042c040cda88a6"
dependencies:
array.prototype.flat "^1.2.1"
cheerio "^1.0.0-rc.2"
function.prototype.name "^1.0.3"
has "^1.0.1"
function.prototype.name "^1.1.0"
has "^1.0.3"
is-boolean-object "^1.0.0"
is-callable "^1.1.3"
is-callable "^1.1.4"
is-number-object "^1.0.3"
is-string "^1.0.4"
is-subset "^0.1.1"
lodash "^4.17.4"
object-inspect "^1.5.0"
object-inspect "^1.6.0"
object-is "^1.0.1"
object.assign "^4.1.0"
object.entries "^1.0.4"
@@ -2750,6 +2761,16 @@ error-ex@^1.2.0, error-ex@^1.3.1:
dependencies:
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:
version "1.10.0"
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"
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"
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327"
dependencies:
@@ -3973,6 +3994,12 @@ has@^1.0.1:
dependencies:
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:
version "2.0.2"
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"
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:
version "1.1.0"
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"
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"
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"
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:
version "2.0.2"
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"
strip-json-comments "~2.0.1"
react-dom@16.3.2:
version "16.3.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df"
react-dom@16.4.0:
version "16.4.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.0.tgz#099f067dd5827ce36a29eaf9a6cdc7cbf6216b1e"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0"
object-assign "^4.1.1"
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:
version "0.7.0"
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"
object-assign "^4.1.1"
react-test-renderer@^16.0.0-0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.2.0.tgz#bddf259a6b8fcd8555f012afc8eacc238872a211"
react-test-renderer@~16.3.0-0:
version "16.3.2"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.2.tgz#3d1ed74fda8db42521fdf03328e933312214749a"
dependencies:
fbjs "^0.8.16"
object-assign "^4.1.1"
prop-types "^15.6.0"
react-is "^16.3.2"
react@16.3.2:
version "16.3.2"
resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9"
react@16.4.0:
version "16.4.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585"
dependencies:
fbjs "^0.8.16"
loose-envify "^1.1.0"