mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2026-06-28 13:10:03 +00:00
fix #60
* implement cell editor * keyField shouldn't be editable * add additional check to avoid some bugs * add jsdom for enzyme mount * add some helpers class for enzyme mounting * add testing for cellEdit * use npm instead * add test for TableResolver * table-layout: fixed; to fix the columns width when content of td changed * add stories for cell edit * add document for cellEdit
This commit is contained in:
11
.travis.yml
11
.travis.yml
@@ -5,7 +5,8 @@ node_js:
|
||||
- "6"
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
|
||||
branches:
|
||||
only:
|
||||
@@ -13,8 +14,8 @@ branches:
|
||||
# - master
|
||||
- develop
|
||||
|
||||
before_install:
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash -s
|
||||
- export PATH="$HOME/.yarn/bin:$PATH"
|
||||
# before_install:
|
||||
# - curl -o- -L https://yarnpkg.com/install.sh | bash -s
|
||||
# - export PATH="$HOME/.yarn/bin:$PATH"
|
||||
|
||||
install: yarn --frozen-lockfile
|
||||
# install: yarn --frozen-lockfile
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
|
||||
## Props on BootstrapTable
|
||||
|
||||
#### Required
|
||||
* [keyField (**required**)](#keyField)
|
||||
* [data (**required**)](#data)
|
||||
* [columns (**required**)](#columns)
|
||||
|
||||
#### Optional
|
||||
* [striped](#striped)
|
||||
* [bordered](#bordered)
|
||||
* [hover](#hover)
|
||||
* [condensed](#condensed)
|
||||
* [cellEdit](#cellEdit)
|
||||
|
||||
### <a name='keyField'>keyField(**required**) - [String]</a>
|
||||
`keyField` is a prop to tell `react-bootstrap-table2` which column is unigue key.
|
||||
|
||||
@@ -18,4 +30,29 @@ Same as `.table-bordered` class for adding borders on all sides of the table and
|
||||
### <a name='hover'>hover - [Bool]</a>
|
||||
Same as `.table-hover` class for adding a hover effect (grey background color) on table rows
|
||||
### <a name='condensed'>condensed - [Bool]</a>
|
||||
Same as `.table-condensed` class for makeing a table more compact by cutting cell padding in half
|
||||
Same as `.table-condensed` class for makeing a table more compact by cutting cell padding in half
|
||||
|
||||
### <a name='cellEdit'>cellEdit - [Bool]</a>
|
||||
Assign a valid `cellEdit` object can enable the cell editing on the cell. The default usage is click/dbclick to trigger cell editing and press `ENTER` to save cell or press `ESC` to cancel editing.
|
||||
|
||||
> Note: The `keyField` column can't be edited
|
||||
|
||||
Following is a `cellEdit` object:
|
||||
```js
|
||||
{
|
||||
mode: 'click',
|
||||
blurToSave: true,
|
||||
onEditing: (rowId, dataField, newValue) => { ... },
|
||||
beforeSaveCell: (oldValue, newValue, row, column) => { ... },
|
||||
afterSaveCell: (oldValue, newValue, row, column) => { ... },
|
||||
nonEditableRows: () => { ... }
|
||||
}
|
||||
```
|
||||
#### <a name='cellEdit.mode'>cellEdit.mode - [String]</a>
|
||||
`cellEdit.mode` possible value is `click` and `dbclick`. It's required value that tell `react-bootstrap-table2` how to trigger the cell editing.
|
||||
|
||||
#### <a name='cellEdit.blurToSave'>cellEdit.blurToSave - [Bool]</a>
|
||||
Default is `false`, enable it will be able to save the cell automatically when blur from the cell editor.
|
||||
|
||||
#### <a name='cellEdit.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`)
|
||||
|
||||
@@ -25,6 +25,7 @@ Available properties in a column object:
|
||||
* [headerEvents](#headerEvents)
|
||||
* [headerAlign](#headerAlign)
|
||||
* [headerAttrs](#headerAttrs)
|
||||
* [editable](#editable)
|
||||
|
||||
Following is a most simplest and basic usage:
|
||||
|
||||
@@ -411,5 +412,9 @@ Additionally, customize the header attributes by a `2-arguments` callback functi
|
||||
|
||||
A new `Object` will be the result of element headerAttrs.
|
||||
|
||||
#### * Caution
|
||||
Same as [column.attrs](#attrs), it has lower priority and will be overwrited when other props related to HTML attributes were given.
|
||||
> Caution:
|
||||
> Same as [column.attrs](#attrs), it has lower priority and will be
|
||||
> overwrited when other props related to HTML attributes were given.
|
||||
|
||||
## <a name='editable'>column.editable - [Bool]</a>
|
||||
`column.editable` default is true, means every column is editable if you configure [`cellEdit`](./README.md#cellEdit). But you can disable some columns editable via setting `false`.
|
||||
@@ -42,6 +42,8 @@
|
||||
"eslint-plugin-react": "^7.2.1",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"jest": "^20.0.4",
|
||||
"jsdom": "^11.2.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"lerna": "^2.0.0",
|
||||
"node-sass": "^4.5.3",
|
||||
"react-test-renderer": "^15.6.1",
|
||||
@@ -71,7 +73,7 @@
|
||||
],
|
||||
"testEnvironment": "node",
|
||||
"testMatch": [
|
||||
"**/test/**/*.js"
|
||||
"**/test/**/*.test.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
54
packages/react-bootstrap-table2-example/examples/cell-edit/blur-to-save-table.js
vendored
Normal file
54
packages/react-bootstrap-table2-example/examples/cell-edit/blur-to-save-table.js
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
|
||||
import { BootstrapTableful } from 'react-bootstrap-table2';
|
||||
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 = `\
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true
|
||||
};
|
||||
|
||||
<BootstrapTableful
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
`;
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true
|
||||
};
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTableful keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
58
packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-hooks-table.js
vendored
Normal file
58
packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-hooks-table.js
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
/* eslint no-console: 0 */
|
||||
import React from 'react';
|
||||
|
||||
import { BootstrapTableful } from 'react-bootstrap-table2';
|
||||
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 = `\
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
beforeSaveCell: (oldValue, newValue, row, column) => { console.log('Before Saving Cell!!'); },
|
||||
afterSaveCell: (oldValue, newValue, row, column) => { console.log('After Saving Cell!!'); }
|
||||
};
|
||||
|
||||
<BootstrapTableful
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
`;
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
beforeSaveCell: (oldValue, newValue, row, column) => { console.log('Before Saving Cell!!'); },
|
||||
afterSaveCell: (oldValue, newValue, row, column) => { console.log('After Saving Cell!!'); }
|
||||
};
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTableful keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
52
packages/react-bootstrap-table2-example/examples/cell-edit/click-to-edit-table.js
vendored
Normal file
52
packages/react-bootstrap-table2-example/examples/cell-edit/click-to-edit-table.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
|
||||
import { BootstrapTableful } from 'react-bootstrap-table2';
|
||||
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 = `\
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click'
|
||||
};
|
||||
|
||||
<BootstrapTableful
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
`;
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click'
|
||||
};
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTableful keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
57
packages/react-bootstrap-table2-example/examples/cell-edit/column-level-editable-table.js
vendored
Normal file
57
packages/react-bootstrap-table2-example/examples/cell-edit/column-level-editable-table.js
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
|
||||
import { BootstrapTableful } from 'react-bootstrap-table2';
|
||||
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',
|
||||
editable: false
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const sourceCode = `\
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
// Product Name column can't be edit anymore
|
||||
editable: false
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true
|
||||
};
|
||||
|
||||
<BootstrapTableful
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
`;
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true
|
||||
};
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTableful keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
52
packages/react-bootstrap-table2-example/examples/cell-edit/dbclick-to-edit-table.js
vendored
Normal file
52
packages/react-bootstrap-table2-example/examples/cell-edit/dbclick-to-edit-table.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
|
||||
import { BootstrapTableful } from 'react-bootstrap-table2';
|
||||
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 = `\
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'dbclick'
|
||||
};
|
||||
|
||||
<BootstrapTableful
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
`;
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'dbclick'
|
||||
};
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTableful keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
57
packages/react-bootstrap-table2-example/examples/cell-edit/row-level-editable-table.js
vendored
Normal file
57
packages/react-bootstrap-table2-example/examples/cell-edit/row-level-editable-table.js
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
|
||||
import { BootstrapTableful } from 'react-bootstrap-table2';
|
||||
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 = `\
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true,
|
||||
// Product ID: 0, 3 will be non-editable
|
||||
nonEditableRows: () => [0, 3]
|
||||
};
|
||||
|
||||
<BootstrapTableful
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
`;
|
||||
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true,
|
||||
nonEditableRows: () => [0, 3]
|
||||
};
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTableful keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
@@ -35,6 +35,14 @@ import HeaderColumnAttrsTable from 'examples/header-columns/column-attrs-table';
|
||||
import EnableSortTable from 'examples/sort/enable-sort-table';
|
||||
import CustomSortTable from 'examples/sort/custom-sort-table';
|
||||
|
||||
// cell editing
|
||||
import ClickToEditTable from 'examples/cell-edit/click-to-edit-table';
|
||||
import DoubleClickToEditTable from 'examples/cell-edit/dbclick-to-edit-table';
|
||||
import BlurToSaveTable from 'examples/cell-edit/blur-to-save-table';
|
||||
import RowLevelEditableTable from 'examples/cell-edit/row-level-editable-table';
|
||||
import ColumnLevelEditableTable from 'examples/cell-edit/column-level-editable-table';
|
||||
import CellEditHooks from 'examples/cell-edit/cell-edit-hooks-table';
|
||||
|
||||
// css style
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import 'stories/stylesheet/tomorrow.min.css';
|
||||
@@ -77,3 +85,11 @@ storiesOf('Work on Header Columns', module)
|
||||
storiesOf('Sort Table', module)
|
||||
.add('Enable Sort', () => <EnableSortTable />)
|
||||
.add('Custom Sort Fuction', () => <CustomSortTable />);
|
||||
|
||||
storiesOf('Cell Editing', module)
|
||||
.add('Click to Edit', () => <ClickToEditTable />)
|
||||
.add('DoubleClick to Edit', () => <DoubleClickToEditTable />)
|
||||
.add('Blur to Save Cell', () => <BlurToSaveTable />)
|
||||
.add('Row Level Editable', () => <RowLevelEditableTable />)
|
||||
.add('Column Level Editable', () => <ColumnLevelEditableTable />)
|
||||
.add('Rich Hook Functions', () => <CellEditHooks />);
|
||||
|
||||
26
packages/react-bootstrap-table2/src/body.js
vendored
26
packages/react-bootstrap-table2/src/body.js
vendored
@@ -13,7 +13,8 @@ const Body = (props) => {
|
||||
keyField,
|
||||
isEmpty,
|
||||
noDataIndication,
|
||||
visibleColumnSize
|
||||
visibleColumnSize,
|
||||
cellEdit
|
||||
} = props;
|
||||
|
||||
let content;
|
||||
@@ -22,14 +23,21 @@ const Body = (props) => {
|
||||
const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication;
|
||||
content = <RowSection content={ indication } colSpan={ visibleColumnSize } />;
|
||||
} else {
|
||||
content = data.map((row, index) => (
|
||||
<Row
|
||||
key={ _.get(row, keyField) }
|
||||
row={ row }
|
||||
rowIndex={ index }
|
||||
columns={ columns }
|
||||
/>
|
||||
));
|
||||
content = data.map((row, index) => {
|
||||
const key = _.get(row, keyField);
|
||||
const editable = !(cellEdit && cellEdit.nonEditableRows.indexOf(key) > -1);
|
||||
return (
|
||||
<Row
|
||||
key={ key }
|
||||
row={ row }
|
||||
keyField={ keyField }
|
||||
rowIndex={ index }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
editable={ editable }
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,6 +7,8 @@ import Header from './header';
|
||||
import Body from './body';
|
||||
import Store from './store/base';
|
||||
import PropsBaseResolver from './props-resolver';
|
||||
import Const from './const';
|
||||
import _ from './utils';
|
||||
|
||||
class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
constructor(props) {
|
||||
@@ -16,8 +18,15 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
this.store = !store ? new Store(props) : store;
|
||||
|
||||
this.handleSort = this.handleSort.bind(this);
|
||||
this.startEditing = this.startEditing.bind(this);
|
||||
this.escapeEditing = this.escapeEditing.bind(this);
|
||||
this.completeEditing = this.completeEditing.bind(this);
|
||||
this.state = {
|
||||
data: this.store.get()
|
||||
data: this.store.get(),
|
||||
currEditCell: {
|
||||
ridx: null,
|
||||
cidx: null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,6 +48,12 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
'table-condensed': condensed
|
||||
});
|
||||
|
||||
const cellEditInfo = this.resolveCellEditProps({
|
||||
onStart: this.startEditing,
|
||||
onEscape: this.escapeEditing,
|
||||
onComplete: this.completeEditing
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="react-bootstrap-table-container">
|
||||
<table className={ tableClass }>
|
||||
@@ -55,6 +70,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
isEmpty={ this.isEmpty() }
|
||||
visibleColumnSize={ this.visibleColumnSize() }
|
||||
noDataIndication={ noDataIndication }
|
||||
cellEdit={ cellEditInfo }
|
||||
/>
|
||||
</table>
|
||||
</div>
|
||||
@@ -70,6 +86,39 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
completeEditing(row, column, newValue) {
|
||||
const { cellEdit, keyField } = this.props;
|
||||
const { beforeSaveCell, onEditing, afterSaveCell } = cellEdit;
|
||||
const oldValue = _.get(row, column.dataField);
|
||||
const rowId = _.get(row, keyField);
|
||||
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
|
||||
onEditing(rowId, column.dataField, newValue);
|
||||
if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column);
|
||||
|
||||
this.setState(() => {
|
||||
return {
|
||||
data: this.store.get(),
|
||||
currEditCell: { ridx: null, cidx: null }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
startEditing(ridx, cidx) {
|
||||
this.setState(() => {
|
||||
return {
|
||||
currEditCell: { ridx, cidx }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
escapeEditing() {
|
||||
this.setState(() => {
|
||||
return {
|
||||
currEditCell: { ridx: null, cidx: null }
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.propTypes = {
|
||||
@@ -81,7 +130,15 @@ BootstrapTable.propTypes = {
|
||||
striped: PropTypes.bool,
|
||||
bordered: PropTypes.bool,
|
||||
hover: PropTypes.bool,
|
||||
condensed: PropTypes.bool
|
||||
condensed: PropTypes.bool,
|
||||
cellEdit: PropTypes.shape({
|
||||
mode: PropTypes.oneOf([Const.CLICK_TO_CELL_EDIT, Const.DBCLICK_TO_CELL_EDIT]).isRequired,
|
||||
onEditing: PropTypes.func.isRequired,
|
||||
blurToSave: PropTypes.bool,
|
||||
beforeSaveCell: PropTypes.func,
|
||||
afterSaveCell: PropTypes.func,
|
||||
nonEditableRows: PropTypes.func
|
||||
})
|
||||
};
|
||||
|
||||
BootstrapTable.defaultProps = {
|
||||
|
||||
142
packages/react-bootstrap-table2/src/cell.js
vendored
142
packages/react-bootstrap-table2/src/cell.js
vendored
@@ -1,63 +1,103 @@
|
||||
import React from 'react';
|
||||
/* eslint react/prop-types: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Const from './const';
|
||||
import _ from './utils';
|
||||
|
||||
const Cell = ({ row, rowIndex, column, columnIndex }) => {
|
||||
const {
|
||||
dataField,
|
||||
hidden,
|
||||
formatter,
|
||||
formatExtraData,
|
||||
style,
|
||||
classes,
|
||||
title,
|
||||
events,
|
||||
align,
|
||||
attrs
|
||||
} = column;
|
||||
let cellTitle;
|
||||
let cellStyle = {};
|
||||
let content = _.get(row, dataField);
|
||||
|
||||
const cellAttrs = {
|
||||
..._.isFunction(attrs) ? attrs(content, row, rowIndex, columnIndex) : attrs,
|
||||
...events
|
||||
};
|
||||
|
||||
const cellClasses = _.isFunction(classes)
|
||||
? classes(content, row, rowIndex, columnIndex)
|
||||
: classes;
|
||||
|
||||
if (style) {
|
||||
cellStyle = _.isFunction(style) ? style(content, row, rowIndex, columnIndex) : style;
|
||||
class Cell extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleEditingCell = this.handleEditingCell.bind(this);
|
||||
}
|
||||
|
||||
if (title) {
|
||||
cellTitle = _.isFunction(title) ? title(content, row, rowIndex, columnIndex) : content;
|
||||
cellAttrs.title = cellTitle;
|
||||
handleEditingCell(e) {
|
||||
const { editMode, column, onStart, rowIndex, columnIndex } = this.props;
|
||||
const { events } = column;
|
||||
if (events) {
|
||||
if (editMode === Const.CLICK_TO_CELL_EDIT) {
|
||||
const customClick = events.onClick;
|
||||
if (_.isFunction(customClick)) customClick(e);
|
||||
} else {
|
||||
const customDbClick = events.onDoubleClick;
|
||||
if (_.isFunction(customDbClick)) customDbClick(e);
|
||||
}
|
||||
}
|
||||
onStart(rowIndex, columnIndex);
|
||||
}
|
||||
|
||||
if (formatter) {
|
||||
content = column.formatter(content, row, rowIndex, formatExtraData);
|
||||
render() {
|
||||
const {
|
||||
row,
|
||||
rowIndex,
|
||||
column,
|
||||
columnIndex,
|
||||
editMode,
|
||||
editable
|
||||
} = this.props;
|
||||
const {
|
||||
dataField,
|
||||
hidden,
|
||||
formatter,
|
||||
formatExtraData,
|
||||
style,
|
||||
classes,
|
||||
title,
|
||||
events,
|
||||
align,
|
||||
attrs
|
||||
} = column;
|
||||
let cellTitle;
|
||||
let cellStyle = {};
|
||||
let content = _.get(row, dataField);
|
||||
|
||||
const cellAttrs = {
|
||||
..._.isFunction(attrs) ? attrs(content, row, rowIndex, columnIndex) : attrs,
|
||||
...events
|
||||
};
|
||||
|
||||
const cellClasses = _.isFunction(classes)
|
||||
? classes(content, row, rowIndex, columnIndex)
|
||||
: classes;
|
||||
|
||||
if (style) {
|
||||
cellStyle = _.isFunction(style) ? style(content, row, rowIndex, columnIndex) : style;
|
||||
}
|
||||
|
||||
if (title) {
|
||||
cellTitle = _.isFunction(title) ? title(content, row, rowIndex, columnIndex) : content;
|
||||
cellAttrs.title = cellTitle;
|
||||
}
|
||||
|
||||
if (formatter) {
|
||||
content = column.formatter(content, row, rowIndex, formatExtraData);
|
||||
}
|
||||
|
||||
if (align) {
|
||||
cellStyle.textAlign =
|
||||
_.isFunction(align) ? align(content, row, rowIndex, columnIndex) : align;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
cellStyle.display = 'none';
|
||||
}
|
||||
|
||||
if (cellClasses) cellAttrs.className = cellClasses;
|
||||
|
||||
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
|
||||
|
||||
if (editable && editMode !== Const.UNABLE_TO_CELL_EDIT) {
|
||||
if (editMode === Const.CLICK_TO_CELL_EDIT) { // click to edit
|
||||
cellAttrs.onClick = this.handleEditingCell;
|
||||
} else { // dbclick to edit
|
||||
cellAttrs.onDoubleClick = this.handleEditingCell;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<td { ...cellAttrs }>{ content }</td>
|
||||
);
|
||||
}
|
||||
|
||||
if (align) {
|
||||
cellStyle.textAlign = _.isFunction(align) ? align(content, row, rowIndex, columnIndex) : align;
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
cellStyle.display = 'none';
|
||||
}
|
||||
|
||||
if (cellClasses) cellAttrs.className = cellClasses;
|
||||
|
||||
if (!_.isEmptyObject(cellStyle)) cellAttrs.style = cellStyle;
|
||||
|
||||
return (
|
||||
<td { ...cellAttrs }>{ content }</td>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Cell.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
|
||||
5
packages/react-bootstrap-table2/src/const.js
vendored
5
packages/react-bootstrap-table2/src/const.js
vendored
@@ -1,4 +1,7 @@
|
||||
export default {
|
||||
SORT_ASC: 'asc',
|
||||
SORT_DESC: 'desc'
|
||||
SORT_DESC: 'desc',
|
||||
UNABLE_TO_CELL_EDIT: 'none',
|
||||
CLICK_TO_CELL_EDIT: 'click',
|
||||
DBCLICK_TO_CELL_EDIT: 'dbclick'
|
||||
};
|
||||
|
||||
66
packages/react-bootstrap-table2/src/editing-cell.js
vendored
Normal file
66
packages/react-bootstrap-table2/src/editing-cell.js
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/* eslint react/prop-types: 0 */
|
||||
/* eslint no-return-assign: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import _ from './utils';
|
||||
import TextEditor from './text-editor';
|
||||
|
||||
class EditingCell extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
}
|
||||
|
||||
handleBlur() {
|
||||
const { onEscape, onComplete, blurToSave, row, column } = this.props;
|
||||
if (blurToSave) {
|
||||
const value = this.editor.text.value;
|
||||
if (!_.isDefined(value)) {
|
||||
// TODO: for other custom or embed editor
|
||||
}
|
||||
onComplete(row, column, value);
|
||||
} else {
|
||||
onEscape();
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown(e) {
|
||||
const { onEscape, onComplete, row, column } = this.props;
|
||||
if (e.keyCode === 27) { // ESC
|
||||
onEscape();
|
||||
} else if (e.keyCode === 13) { // ENTER
|
||||
const value = e.currentTarget.value;
|
||||
if (!_.isDefined(value)) {
|
||||
// TODO: for other custom or embed editor
|
||||
}
|
||||
onComplete(row, column, value);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { row, column } = this.props;
|
||||
const { dataField } = column;
|
||||
|
||||
const value = _.get(row, dataField);
|
||||
const editorAttrs = {
|
||||
onKeyDown: this.handleKeyDown,
|
||||
onBlur: this.handleBlur
|
||||
};
|
||||
return (
|
||||
<td>
|
||||
<TextEditor ref={ node => this.editor = node } defaultValue={ value } { ...editorAttrs } />
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EditingCell.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
column: PropTypes.object.isRequired,
|
||||
onComplete: PropTypes.func.isRequired,
|
||||
onEscape: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default EditingCell;
|
||||
@@ -1,4 +1,6 @@
|
||||
import ColumnResolver from './column-resolver';
|
||||
import Const from '../const';
|
||||
import _ from '../utils';
|
||||
|
||||
export default ExtendBase =>
|
||||
class TableResolver extends ColumnResolver(ExtendBase) {
|
||||
@@ -15,4 +17,27 @@ export default ExtendBase =>
|
||||
isEmpty() {
|
||||
return this.props.data.length === 0;
|
||||
}
|
||||
|
||||
resolveCellEditProps(options) {
|
||||
const { cellEdit } = this.props;
|
||||
const { currEditCell } = this.state;
|
||||
const nonEditableRows =
|
||||
(cellEdit && _.isFunction(cellEdit.nonEditableRows)) ? cellEdit.nonEditableRows() : [];
|
||||
const cellEditInfo = {
|
||||
...currEditCell,
|
||||
nonEditableRows
|
||||
};
|
||||
|
||||
if (_.isDefined(cellEdit)) {
|
||||
return {
|
||||
...cellEdit,
|
||||
...cellEditInfo,
|
||||
...options
|
||||
};
|
||||
}
|
||||
return {
|
||||
mode: Const.UNABLE_TO_CELL_EDIT,
|
||||
...cellEditInfo
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
75
packages/react-bootstrap-table2/src/row.js
vendored
75
packages/react-bootstrap-table2/src/row.js
vendored
@@ -1,25 +1,64 @@
|
||||
/* eslint react/prop-types: 0 */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import _ from './utils';
|
||||
import Cell from './cell';
|
||||
import EditingCell from './editing-cell';
|
||||
|
||||
const Row = ({ row, rowIndex, columns }) => (
|
||||
<tr>
|
||||
{
|
||||
columns.map((column, index) =>
|
||||
(
|
||||
<Cell
|
||||
key={ _.get(row, column.dataField) }
|
||||
row={ row }
|
||||
rowIndex={ rowIndex }
|
||||
columnIndex={ index }
|
||||
column={ column }
|
||||
/>
|
||||
))
|
||||
}
|
||||
</tr>
|
||||
);
|
||||
const Row = (props) => {
|
||||
const {
|
||||
row,
|
||||
columns,
|
||||
keyField,
|
||||
rowIndex,
|
||||
cellEdit,
|
||||
editable: editableRow
|
||||
} = props;
|
||||
const {
|
||||
ridx: editingRowIdx,
|
||||
cidx: editingColIdx,
|
||||
mode,
|
||||
onStart,
|
||||
onEscape,
|
||||
onComplete,
|
||||
blurToSave
|
||||
} = cellEdit;
|
||||
return (
|
||||
<tr>
|
||||
{
|
||||
columns.map((column, index) => {
|
||||
let editable = _.isDefined(column.editable) ? column.editable : true;
|
||||
if (column.dataField === keyField || !editableRow) editable = false;
|
||||
if (rowIndex === editingRowIdx && index === editingColIdx) {
|
||||
return (
|
||||
<EditingCell
|
||||
key={ _.get(row, column.dataField) }
|
||||
row={ row }
|
||||
column={ column }
|
||||
blurToSave={ blurToSave }
|
||||
onComplete={ onComplete }
|
||||
onEscape={ onEscape }
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Cell
|
||||
key={ _.get(row, column.dataField) }
|
||||
row={ row }
|
||||
rowIndex={ rowIndex }
|
||||
columnIndex={ index }
|
||||
column={ column }
|
||||
editMode={ mode }
|
||||
editable={ editable }
|
||||
onStart={ onStart }
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
Row.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
@@ -27,4 +66,8 @@ Row.propTypes = {
|
||||
columns: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
Row.defaultProps = {
|
||||
editable: true
|
||||
};
|
||||
|
||||
export default Row;
|
||||
|
||||
@@ -7,11 +7,20 @@ const withStateful = (Base) => {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.store = new Store(props);
|
||||
this.edit = this.edit.bind(this);
|
||||
}
|
||||
|
||||
edit(rowId, dataField, newValue) {
|
||||
this.store.edit(rowId, dataField, newValue);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props } = this;
|
||||
return <Base { ...props } store={ this.store } />;
|
||||
const newProps = { ...props };
|
||||
if (newProps.cellEdit && !newProps.cellEdit.onEditing) {
|
||||
newProps.cellEdit.onEditing = this.edit;
|
||||
}
|
||||
return <Base { ...newProps } store={ this.store } />;
|
||||
}
|
||||
}
|
||||
return StatefulComponent;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { sort } from './sort';
|
||||
import Const from '../const';
|
||||
import _ from '../utils';
|
||||
|
||||
export default class Store {
|
||||
constructor(props) {
|
||||
const { data } = props;
|
||||
const { data, keyField } = props;
|
||||
this.keyField = keyField;
|
||||
this.data = data ? data.slice() : [];
|
||||
|
||||
this.sortOrder = undefined;
|
||||
@@ -25,7 +27,16 @@ export default class Store {
|
||||
this.sortField = dataField;
|
||||
}
|
||||
|
||||
edit(rowId, dataField, newValue) {
|
||||
const row = this.getRowByRowId(rowId);
|
||||
if (row) _.set(row, dataField, newValue);
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
getRowByRowId(rowId) {
|
||||
return this.get().find(row => _.get(row, this.keyField) === rowId);
|
||||
}
|
||||
}
|
||||
|
||||
21
packages/react-bootstrap-table2/src/text-editor.js
vendored
Normal file
21
packages/react-bootstrap-table2/src/text-editor.js
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/* eslint no-return-assign: 0 */
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class TextEditor extends Component {
|
||||
componentDidMount() {
|
||||
this.text.focus();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<input
|
||||
ref={ node => this.text = node }
|
||||
type="text"
|
||||
className="form-control editor edit-text"
|
||||
{ ...this.props }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TextEditor;
|
||||
29
packages/react-bootstrap-table2/src/utils.js
vendored
29
packages/react-bootstrap-table2/src/utils.js
vendored
@@ -1,11 +1,16 @@
|
||||
/* eslint no-empty: 0 */
|
||||
/* eslint no-param-reassign: 0 */
|
||||
|
||||
function get(target, field) {
|
||||
const pathArray = [field]
|
||||
function splitNested(str) {
|
||||
return [str]
|
||||
.join('.')
|
||||
.replace(/\[/g, '.')
|
||||
.replace(/\]/g, '')
|
||||
.split('.');
|
||||
}
|
||||
|
||||
function get(target, field) {
|
||||
const pathArray = splitNested(field);
|
||||
let result;
|
||||
try {
|
||||
result = pathArray.reduce((curr, path) => curr[path], target);
|
||||
@@ -13,6 +18,25 @@ function get(target, field) {
|
||||
return result;
|
||||
}
|
||||
|
||||
function set(target, field, value, safe = false) {
|
||||
const pathArray = splitNested(field);
|
||||
let level = 0;
|
||||
pathArray.reduce((a, b) => {
|
||||
level += 1;
|
||||
if (typeof a[b] === 'undefined') {
|
||||
if (!safe) throw new Error(`${a}.${b} is undefined`);
|
||||
a[b] = {};
|
||||
return a[b];
|
||||
}
|
||||
|
||||
if (level === pathArray.length) {
|
||||
a[b] = value;
|
||||
return value;
|
||||
}
|
||||
return a[b];
|
||||
}, target);
|
||||
}
|
||||
|
||||
function isFunction(obj) {
|
||||
return obj && (typeof obj === 'function');
|
||||
}
|
||||
@@ -46,6 +70,7 @@ function isDefined(value) {
|
||||
|
||||
export default {
|
||||
get,
|
||||
set,
|
||||
isFunction,
|
||||
isObject,
|
||||
isEmptyObject,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
.react-bootstrap-table-container {
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
th.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { shallow } from 'enzyme';
|
||||
|
||||
import Body from '../src/body';
|
||||
import Row from '../src/row';
|
||||
import Const from '../src/const';
|
||||
import RowSection from '../src/row-section';
|
||||
|
||||
describe('Body', () => {
|
||||
@@ -111,4 +112,35 @@ describe('Body', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when cellEdit.nonEditableRows props is defined', () => {
|
||||
const nonEditableRows = [data[1].id];
|
||||
const keyField = 'id';
|
||||
const cellEdit = {
|
||||
mode: Const.CLICK_TO_CELL_EDIT,
|
||||
nonEditableRows
|
||||
};
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<Body
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
keyField={ keyField }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render Row component with correct editable prop', () => {
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Header from '../src/header';
|
||||
import Body from '../src/body';
|
||||
import BootstrapTable from '../src/bootstrap-table';
|
||||
import Const from '../src/const';
|
||||
|
||||
describe('BootstrapTable', () => {
|
||||
let wrapper;
|
||||
@@ -36,6 +38,12 @@ describe('BootstrapTable', () => {
|
||||
expect(wrapper.find('.react-bootstrap-table-container').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should have correct default state', () => {
|
||||
expect(wrapper.state().currEditCell).toBeDefined();
|
||||
expect(wrapper.state().currEditCell.ridx).toBeNull();
|
||||
expect(wrapper.state().currEditCell.cidx).toBeNull();
|
||||
});
|
||||
|
||||
it('should have table-bordered class as default', () => {
|
||||
expect(wrapper.find('table.table-bordered').length).toBe(1);
|
||||
});
|
||||
@@ -80,4 +88,36 @@ describe('BootstrapTable', () => {
|
||||
expect(wrapper.find('table.table-condensed').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when cellEdit props is defined', () => {
|
||||
const nonEditableRows = [data[1].id];
|
||||
const cellEdit = {
|
||||
mode: Const.CLICK_TO_CELL_EDIT,
|
||||
onEditing: sinon.stub(),
|
||||
nonEditableRows: () => nonEditableRows
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
columns={ columns }
|
||||
data={ data }
|
||||
bordered={ false }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should resolve correct cellEdit object to Body component', () => {
|
||||
const body = wrapper.find(Body);
|
||||
expect(body.length).toBe(1);
|
||||
expect(body.props().cellEdit.nonEditableRows).toEqual(nonEditableRows);
|
||||
expect(body.props().cellEdit.ridx).toEqual(wrapper.state().currEditCell.ridx);
|
||||
expect(body.props().cellEdit.cidx).toEqual(wrapper.state().currEditCell.cidx);
|
||||
expect(body.props().cellEdit.onStart).toBeDefined();
|
||||
expect(body.props().cellEdit.onEscape).toBeDefined();
|
||||
expect(body.props().cellEdit.onComplete).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Const from '../src/const';
|
||||
import Cell from '../src/cell';
|
||||
|
||||
describe('Cell', () => {
|
||||
@@ -447,4 +448,98 @@ describe('Cell', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when editable prop is true', () => {
|
||||
let onStartCallBack;
|
||||
const rowIndex = 1;
|
||||
const columnIndex = 1;
|
||||
const column = {
|
||||
dataField: 'id',
|
||||
text: 'ID'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
onStartCallBack = sinon.stub().withArgs(rowIndex, columnIndex);
|
||||
});
|
||||
|
||||
describe(`and editMode is ${Const.CLICK_TO_CELL_EDIT}`, () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<Cell
|
||||
row={ row }
|
||||
rowIndex={ rowIndex }
|
||||
column={ column }
|
||||
columnIndex={ columnIndex }
|
||||
editable
|
||||
editMode={ Const.CLICK_TO_CELL_EDIT }
|
||||
onStart={ onStartCallBack }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render onClick attribute', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('td').prop('onClick')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should call onStart correctly when clicking cell', () => {
|
||||
wrapper.find('td').simulate('click');
|
||||
expect(onStartCallBack.callCount).toBe(1);
|
||||
expect(onStartCallBack.calledWith(rowIndex, columnIndex)).toBe(true);
|
||||
});
|
||||
|
||||
describe('if when column.events.onClick prop is defined', () => {
|
||||
beforeEach(() => {
|
||||
column.events = {
|
||||
onClick: sinon.stub()
|
||||
};
|
||||
});
|
||||
it('should calling custom onClick callback also', () => {
|
||||
wrapper.find('td').simulate('click');
|
||||
expect(onStartCallBack.callCount).toBe(1);
|
||||
expect(column.events.onClick.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe(`and editMode is ${Const.DBCLICK_TO_CELL_EDIT}`, () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<Cell
|
||||
row={ row }
|
||||
rowIndex={ 1 }
|
||||
column={ column }
|
||||
columnIndex={ 1 }
|
||||
editable
|
||||
editMode={ Const.DBCLICK_TO_CELL_EDIT }
|
||||
onStart={ onStartCallBack }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render onDoubleClick attribute', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('td').prop('onDoubleClick')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should call onStart correctly when double clicking cell', () => {
|
||||
wrapper.find('td').simulate('doubleclick');
|
||||
expect(onStartCallBack.callCount).toBe(1);
|
||||
expect(onStartCallBack.calledWith(rowIndex, columnIndex)).toBe(true);
|
||||
});
|
||||
|
||||
describe('if when column.events.onDoubleClick prop is defined', () => {
|
||||
beforeEach(() => {
|
||||
column.events = {
|
||||
onDoubleClick: sinon.stub()
|
||||
};
|
||||
});
|
||||
it('should calling custom onDoubleClick callback also', () => {
|
||||
wrapper.find('td').simulate('doubleclick');
|
||||
expect(onStartCallBack.callCount).toBe(1);
|
||||
expect(column.events.onDoubleClick.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
93
packages/react-bootstrap-table2/test/editing-cell.test.js
Normal file
93
packages/react-bootstrap-table2/test/editing-cell.test.js
Normal file
@@ -0,0 +1,93 @@
|
||||
import 'jsdom-global/register';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
|
||||
import { TableRowWrapper } from './test-helpers/table-wrapper';
|
||||
import EditingCell from '../src/editing-cell';
|
||||
import TextEditor from '../src/text-editor';
|
||||
|
||||
|
||||
describe('EditingCell', () => {
|
||||
let wrapper;
|
||||
let onEscape;
|
||||
let onComplete;
|
||||
const row = {
|
||||
id: 1,
|
||||
name: 'A'
|
||||
};
|
||||
|
||||
const column = {
|
||||
dataField: 'id',
|
||||
text: 'ID'
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
onComplete = sinon.stub();
|
||||
onEscape = sinon.stub();
|
||||
wrapper = shallow(
|
||||
<EditingCell
|
||||
row={ row }
|
||||
column={ column }
|
||||
onEscape={ onEscape }
|
||||
onComplete={ onComplete }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render default editor successfully', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('td').length).toBe(1);
|
||||
expect(wrapper.find(TextEditor).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should render TextEditor with correct props', () => {
|
||||
const textEditor = wrapper.find(TextEditor);
|
||||
expect(textEditor.props().defaultValue).toEqual(row[column.dataField]);
|
||||
expect(textEditor.props().onKeyDown).toBeDefined();
|
||||
expect(textEditor.props().onBlur).toBeDefined();
|
||||
});
|
||||
|
||||
it('when press ENTER on TextEditor should call onComplete correctly', () => {
|
||||
const newValue = 'test';
|
||||
const textEditor = wrapper.find(TextEditor);
|
||||
textEditor.simulate('keyDown', { keyCode: 13, currentTarget: { value: newValue } });
|
||||
expect(onComplete.callCount).toBe(1);
|
||||
expect(onComplete.calledWith(row, column, newValue)).toBe(true);
|
||||
});
|
||||
|
||||
it('when press ESC on TextEditor should call onEscape correctly', () => {
|
||||
const textEditor = wrapper.find(TextEditor);
|
||||
textEditor.simulate('keyDown', { keyCode: 27 });
|
||||
expect(onEscape.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('when blur from TextEditor should call onEscape correctly', () => {
|
||||
const textEditor = wrapper.find(TextEditor);
|
||||
textEditor.simulate('blur');
|
||||
expect(onEscape.callCount).toBe(1);
|
||||
});
|
||||
|
||||
describe('if blurToSave prop is true', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TableRowWrapper>
|
||||
<EditingCell
|
||||
row={ row }
|
||||
column={ column }
|
||||
onEscape={ onEscape }
|
||||
onComplete={ onComplete }
|
||||
blurToSave
|
||||
/>
|
||||
</TableRowWrapper>
|
||||
);
|
||||
});
|
||||
|
||||
it('when blur from TextEditor should call onComplete correctly', () => {
|
||||
const textEditor = wrapper.find(TextEditor);
|
||||
textEditor.simulate('blur');
|
||||
expect(onComplete.callCount).toBe(1);
|
||||
expect(onComplete.calledWith(row, column, `${row[column.dataField]}`)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
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';
|
||||
const columns = [{
|
||||
dataField: keyField,
|
||||
text: 'ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Name'
|
||||
}];
|
||||
const data = [{
|
||||
id: 1,
|
||||
name: 'A'
|
||||
}, {
|
||||
id: 2,
|
||||
name: 'B'
|
||||
}];
|
||||
const ExtendBase = baseResolver(Component);
|
||||
const BootstrapTableMock = extendTo(ExtendBase);
|
||||
let wrapper;
|
||||
|
||||
describe('validateProps', () => {
|
||||
describe('if keyField is defined and columns is all visible', () => {
|
||||
beforeEach(() => {
|
||||
const mockElement = React.createElement(BootstrapTableMock, {
|
||||
data, columns, keyField
|
||||
}, null);
|
||||
wrapper = shallow(mockElement);
|
||||
});
|
||||
|
||||
it('should not throw any errors', () => {
|
||||
expect(() => wrapper.instance().validateProps()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('if keyField is not defined on props', () => {
|
||||
beforeEach(() => {
|
||||
const mockElement = React.createElement(BootstrapTableMock, {
|
||||
data, columns
|
||||
}, null);
|
||||
wrapper = shallow(mockElement);
|
||||
});
|
||||
|
||||
it('should throw error', () => {
|
||||
expect(() =>
|
||||
wrapper.instance().validateProps()
|
||||
).toThrow(new Error('Please specify a field as key via keyField'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('if columns is all unvisible', () => {
|
||||
beforeEach(() => {
|
||||
const mockElement = React.createElement(BootstrapTableMock, {
|
||||
data, keyField, columns: []
|
||||
}, null);
|
||||
wrapper = shallow(mockElement);
|
||||
});
|
||||
|
||||
it('should throw error', () => {
|
||||
expect(() =>
|
||||
wrapper.instance().validateProps()
|
||||
).toThrow(new Error('No any visible columns detect'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveCellEditProps', () => {
|
||||
describe('if cellEdit prop not defined', () => {
|
||||
beforeEach(() => {
|
||||
const mockElement = React.createElement(BootstrapTableMock, {
|
||||
data, keyField, columns
|
||||
}, null);
|
||||
wrapper = shallow(mockElement);
|
||||
});
|
||||
|
||||
it('should resolve a default cellEdit instance', () => {
|
||||
const cellEdit = wrapper.instance().resolveCellEditProps();
|
||||
expect(cellEdit).toBeDefined();
|
||||
expect(cellEdit.mode).toEqual(Const.UNABLE_TO_CELL_EDIT);
|
||||
expect(cellEdit.nonEditableRows.length).toEqual(0);
|
||||
expect(cellEdit.ridx).toBeNull();
|
||||
expect(cellEdit.cidx).toBeNull();
|
||||
});
|
||||
|
||||
it('should resolve a default cellEdit instance even if state.currEditCell changed', () => {
|
||||
const ridx = 1;
|
||||
const cidx = 1;
|
||||
wrapper.setState({ currEditCell: { ridx, cidx } });
|
||||
const cellEdit = wrapper.instance().resolveCellEditProps();
|
||||
expect(cellEdit).toBeDefined();
|
||||
expect(cellEdit.ridx).toEqual(ridx);
|
||||
expect(cellEdit.cidx).toEqual(cidx);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('if cellEdit prop defined', () => {
|
||||
const expectNonEditableRows = [1, 2];
|
||||
const cellEdit = {
|
||||
mode: Const.DBCLICK_TO_CELL_EDIT,
|
||||
onEditing: sinon.stub(),
|
||||
blurToSave: true,
|
||||
beforeSaveCell: sinon.stub(),
|
||||
afterSaveCell: sinon.stub(),
|
||||
nonEditableRows: sinon.stub().returns(expectNonEditableRows)
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
const mockElement = React.createElement(BootstrapTableMock, {
|
||||
data, keyField, columns, cellEdit
|
||||
}, null);
|
||||
wrapper = shallow(mockElement);
|
||||
});
|
||||
|
||||
it('should resolve a cellEdit correctly', () => {
|
||||
const cellEditInfo = wrapper.instance().resolveCellEditProps();
|
||||
expect(cellEditInfo).toBeDefined();
|
||||
expect(cellEditInfo.ridx).toBeNull();
|
||||
expect(cellEditInfo.cidx).toBeNull();
|
||||
expect(cellEditInfo.mode).toEqual(cellEdit.mode);
|
||||
expect(cellEditInfo.onEditing).toEqual(cellEdit.onEditing);
|
||||
expect(cellEditInfo.blurToSave).toEqual(cellEdit.blurToSave);
|
||||
expect(cellEditInfo.beforeSaveCell).toEqual(cellEdit.beforeSaveCell);
|
||||
expect(cellEditInfo.afterSaveCell).toEqual(cellEdit.afterSaveCell);
|
||||
expect(cellEditInfo.nonEditableRows).toEqual(expectNonEditableRows);
|
||||
});
|
||||
|
||||
it('should attach options to cellEdit props', () => {
|
||||
const something = {
|
||||
test: 1,
|
||||
cb: sinon.stub()
|
||||
};
|
||||
const cellEditInfo = wrapper.instance().resolveCellEditProps(something);
|
||||
expect(cellEditInfo).toBeDefined();
|
||||
expect(cellEditInfo.test).toEqual(something.test);
|
||||
expect(cellEditInfo.cb).toEqual(something.cb);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,27 +1,36 @@
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import Cell from '../src/cell';
|
||||
import Row from '../src/row';
|
||||
import Const from '../src/const';
|
||||
import EditingCell from '../src/editing-cell';
|
||||
|
||||
const defaultColumns = [{
|
||||
dataField: 'id',
|
||||
text: 'ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Price'
|
||||
}];
|
||||
|
||||
describe('Row', () => {
|
||||
let wrapper;
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Name'
|
||||
}];
|
||||
|
||||
const row = {
|
||||
id: 1,
|
||||
name: 'A'
|
||||
name: 'A',
|
||||
price: 1000
|
||||
};
|
||||
|
||||
describe('simplest row', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<Row rowIndex={ 1 } columns={ columns } row={ row } />);
|
||||
wrapper = shallow(
|
||||
<Row rowIndex={ 1 } columns={ defaultColumns } row={ row } cellEdit={ {} } />);
|
||||
});
|
||||
|
||||
it('should render successfully', () => {
|
||||
@@ -30,4 +39,158 @@ describe('Row', () => {
|
||||
expect(wrapper.find(Cell).length).toBe(Object.keys(row).length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when cellEdit prop is defined', () => {
|
||||
let columns;
|
||||
let cellEdit;
|
||||
const rowIndex = 1;
|
||||
const keyField = 'id';
|
||||
|
||||
beforeEach(() => {
|
||||
columns = defaultColumns;
|
||||
cellEdit = {
|
||||
mode: Const.CLICK_TO_CELL_EDIT
|
||||
};
|
||||
wrapper = shallow(
|
||||
<Row
|
||||
row={ row }
|
||||
rowIndex={ rowIndex }
|
||||
columns={ columns }
|
||||
keyField={ keyField }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
columns = undefined;
|
||||
cellEdit = undefined;
|
||||
});
|
||||
|
||||
it('Cell component should receive correct editable props', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
for (let i = 0; i < columns.length; i += 1) {
|
||||
const column = columns[i];
|
||||
if (column.dataField === keyField) {
|
||||
expect(wrapper.find(Cell).get(i).props.editable).toBeFalsy();
|
||||
} else {
|
||||
expect(wrapper.find(Cell).get(i).props.editable).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('Cell component should receive correct editMode props', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
for (let i = 0; i < columns.length; i += 1) {
|
||||
expect(wrapper.find(Cell).get(i).props.editMode).toEqual(cellEdit.mode);
|
||||
}
|
||||
});
|
||||
|
||||
describe('when have column.editable defined false', () => {
|
||||
const nonEditableColIndex = 1;
|
||||
beforeEach(() => {
|
||||
columns[nonEditableColIndex].editable = false;
|
||||
wrapper = shallow(
|
||||
<Row
|
||||
row={ row }
|
||||
rowIndex={ rowIndex }
|
||||
columns={ columns }
|
||||
keyField={ keyField }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('Cell component should receive correct editMode props', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
for (let i = 0; i < columns.length; i += 1) {
|
||||
const column = columns[i];
|
||||
if (i === nonEditableColIndex || column.dataField === keyField) {
|
||||
expect(wrapper.find(Cell).get(i).props.editable).toBeFalsy();
|
||||
} else {
|
||||
expect(wrapper.find(Cell).get(i).props.editable).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Means user defined cellEdit.nonEditableRows
|
||||
// and some rows will be treated as noneditable by this rules
|
||||
describe('when editable prop is false', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<Row
|
||||
row={ row }
|
||||
rowIndex={ rowIndex }
|
||||
columns={ columns }
|
||||
keyField={ keyField }
|
||||
cellEdit={ cellEdit }
|
||||
editable={ false }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('All the Cell components should be noneditable', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
for (let i = 0; i < columns.length; i += 1) {
|
||||
expect(wrapper.find(Cell).get(i).props.editable).toBeFalsy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Means a cell now is undering editing
|
||||
describe('when cellEdit.ridx and cellEdit.cidx is defined', () => {
|
||||
describe('and cellEdit.ridx is match to current row index', () => {
|
||||
const editingColIndex = 1;
|
||||
beforeEach(() => {
|
||||
cellEdit.ridx = rowIndex;
|
||||
cellEdit.cidx = editingColIndex;
|
||||
cellEdit.onComplete = sinon.stub();
|
||||
cellEdit.onEscape = sinon.stub();
|
||||
wrapper = shallow(
|
||||
<Row
|
||||
row={ row }
|
||||
rowIndex={ 1 }
|
||||
columns={ columns }
|
||||
keyField={ keyField }
|
||||
cellEdit={ cellEdit }
|
||||
editable={ false }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render EditingCell correctly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find(EditingCell).length).toBe(1);
|
||||
expect(wrapper.find('tr').children().at(editingColIndex).type()).toEqual(EditingCell);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and cellEdit.ridx is not match to current row index', () => {
|
||||
const editingColIndex = 1;
|
||||
beforeEach(() => {
|
||||
cellEdit.ridx = 3;
|
||||
cellEdit.cidx = editingColIndex;
|
||||
cellEdit.onComplete = sinon.stub();
|
||||
cellEdit.onEscape = sinon.stub();
|
||||
wrapper = shallow(
|
||||
<Row
|
||||
row={ row }
|
||||
rowIndex={ 1 }
|
||||
columns={ columns }
|
||||
keyField={ keyField }
|
||||
cellEdit={ cellEdit }
|
||||
editable={ false }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render any EditingCell component', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find(EditingCell).length).toBe(0);
|
||||
expect(wrapper.find(Cell).length).toBe(columns.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,15 +3,16 @@ import Const from '../../src/const';
|
||||
|
||||
describe('Store Base', () => {
|
||||
let store;
|
||||
const data = [
|
||||
{ id: 3, name: 'name2' },
|
||||
{ id: 2, name: 'ABC' },
|
||||
{ id: 4, name: '123tester' },
|
||||
{ id: 1, name: '!@#' }
|
||||
];
|
||||
let data;
|
||||
|
||||
beforeEach(() => {
|
||||
store = new Base({ data });
|
||||
data = [
|
||||
{ id: 3, name: 'name2' },
|
||||
{ id: 2, name: 'ABC' },
|
||||
{ id: 4, name: '123tester' },
|
||||
{ id: 1, name: '!@#' }
|
||||
];
|
||||
store = new Base({ data, keyField: 'id' });
|
||||
});
|
||||
|
||||
describe('initialize', () => {
|
||||
@@ -72,4 +73,40 @@ describe('Store Base', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRowByRowId', () => {
|
||||
it('should return data if specified id existing', () => {
|
||||
const result = store.getRowByRowId(3);
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return undefined if specified id existing', () => {
|
||||
const result = store.getRowByRowId(88);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('edit', () => {
|
||||
it('should update a specified field correctly', () => {
|
||||
const newValue = 'newValue';
|
||||
const dataField = 'name';
|
||||
const rowId = 2;
|
||||
store.edit(rowId, dataField, newValue);
|
||||
|
||||
const row = store.data.find(d => d[store.keyField] === rowId);
|
||||
expect(row[dataField]).toEqual(newValue);
|
||||
});
|
||||
|
||||
it('should not throw any error even if rowId is not existing', () => {
|
||||
expect(() => {
|
||||
store.edit('123', 'name', 'value');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throwing error if dataField is not existing', () => {
|
||||
expect(() => {
|
||||
store.edit(2, 'non_exist_field', 'value');
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
15
packages/react-bootstrap-table2/test/test-helpers/mock-component.js
vendored
Normal file
15
packages/react-bootstrap-table2/test/test-helpers/mock-component.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
export const extendTo = Base =>
|
||||
class MockComponent extends Base {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: this.props.data,
|
||||
currEditCell: {
|
||||
ridx: null,
|
||||
cidx: null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() { return null; }
|
||||
};
|
||||
11
packages/react-bootstrap-table2/test/test-helpers/table-wrapper.js
vendored
Normal file
11
packages/react-bootstrap-table2/test/test-helpers/table-wrapper.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
/* eslint react/prop-types: 0 */
|
||||
import React from 'react';
|
||||
|
||||
export const TableRowWrapper = props => (
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>{ props.children }</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
24
packages/react-bootstrap-table2/test/text-editor.test.js
Normal file
24
packages/react-bootstrap-table2/test/text-editor.test.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import TextEditor from '../src/text-editor';
|
||||
|
||||
describe('TextEditor', () => {
|
||||
let wrapper;
|
||||
const value = 'test';
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<TextEditor
|
||||
defaultValue={ value }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render TextEditor correctly', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find('input').length).toBe(1);
|
||||
expect(wrapper.find('input').prop('type')).toEqual('text');
|
||||
expect(wrapper.find('.form-control.editor.edit-text').length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -22,6 +22,42 @@ describe('Utils', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('set', () => {
|
||||
const newValue = 'test';
|
||||
const data = {
|
||||
name: 'A',
|
||||
address: {
|
||||
road: 'BCD',
|
||||
postal: '1234-12345',
|
||||
city: {
|
||||
name: 'B'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
it('should set data successfully', () => {
|
||||
_.set(data, 'name', newValue);
|
||||
_.set(data, 'address.road', newValue);
|
||||
_.set(data, 'address.city.name', newValue);
|
||||
expect(data.name).toEqual(newValue);
|
||||
expect(data.address.road).toEqual(newValue);
|
||||
expect(data.address.city.name).toEqual(newValue);
|
||||
});
|
||||
|
||||
it('should throw error if target not existing', () => {
|
||||
expect(() => {
|
||||
_.set(data, 'address.not.existing', newValue);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('should not throw error if target not existing but with safe=true', () => {
|
||||
expect(() => {
|
||||
_.set(data, 'address.not.existing', newValue, true);
|
||||
}).not.toThrow();
|
||||
expect(data.address.not.existing).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isObject', () => {
|
||||
describe('when given Object', () => {
|
||||
it('should return true', () => {
|
||||
|
||||
Reference in New Issue
Block a user