mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2025-10-16 11:55:39 +00:00
commit
6913434714
@ -40,7 +40,13 @@ This is a chance that you can connect to your remote server or database to manip
|
||||
For flexibility reason, you can control what functionality should be handled on remote via a object return:
|
||||
|
||||
```js
|
||||
remote={ { filter: true } }
|
||||
remote={ {
|
||||
filter: true,
|
||||
pagination: true,
|
||||
filter: true,
|
||||
sort: true,
|
||||
cellEdit: true
|
||||
} }
|
||||
```
|
||||
|
||||
In above case, only column filter will be handled on remote.
|
||||
@ -53,7 +59,7 @@ A special case for remote pagination:
|
||||
remote={ { pagination: true, filter: false, sort: false } }
|
||||
```
|
||||
|
||||
In pagination case, even you only specified the paignation need to handle as remote, `react-bootstrap-table2` will handle all the table changes(`filter`, `sort` etc) as remote mode, because `react-bootstrap-table` only know the data of current page, but filtering, searching or sort need to work on overall datas.
|
||||
In pagination case, even you only specified the paignation need to handle as remote, `react-bootstrap-table2` will handle all the table changes(`filter`, `sort`) as remote mode, because `react-bootstrap-table` only know the data of current page, but filtering, searching or sort need to work on overall datas.
|
||||
|
||||
### <a name='loading'>loading - [Bool]</a>
|
||||
Telling if table is loading or not, for example: waiting data loading, filtering etc. It's **only** valid when [`remote`](#remote) is enabled.
|
||||
@ -250,6 +256,7 @@ There's only two arguments will be passed to `onTableChange`: `type` and `newSta
|
||||
* `filter`
|
||||
* `pagination`
|
||||
* `sort`
|
||||
* `cellEdit`
|
||||
|
||||
Following is a shape of `newState`
|
||||
|
||||
@ -260,6 +267,11 @@ Following is a shape of `newState`
|
||||
sortField, // newest sort field
|
||||
sortOrder, // newest sort order
|
||||
filters, // an object which have current filter status per column
|
||||
data // when you enable remote sort, you may need to base on data to sort if data is filtered/searched
|
||||
data, // when you enable remote sort, you may need to base on data to sort if data is filtered/searched
|
||||
cellEdit: { // You can only see this prop when type is cellEdit
|
||||
rowId,
|
||||
dataField,
|
||||
newValue
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -5,8 +5,6 @@
|
||||
* [timeToCloseMessage](#timeToCloseMessage)
|
||||
* [beforeSaveCell](#beforeSaveCell)
|
||||
* [afterSaveCell](#afterSaveCell)
|
||||
* [onUpdate](#onUpdate)
|
||||
* [editing](#editing)
|
||||
* [errorMessage](#errorMessage)
|
||||
* [onErrorMessageDisappear](#onErrorMessageDisappear)
|
||||
|
||||
@ -21,9 +19,7 @@ Following is the shape of `cellEdit` object:
|
||||
mode: 'click',
|
||||
blurToSave: true,
|
||||
timeToCloseMessage: 2500,
|
||||
editing: false|true,
|
||||
errorMessage: '',
|
||||
onUpdate: (rowId, dataField, newValue) => { ... },
|
||||
beforeSaveCell: (oldValue, newValue, row, column) => { ... },
|
||||
afterSaveCell: (oldValue, newValue, row, column) => { ... },
|
||||
onErrorMessageDisappear: () => { ... },
|
||||
@ -63,73 +59,8 @@ const cellEdit = {
|
||||
};
|
||||
```
|
||||
|
||||
### <a name='onUpdate'>cellEdit.onUpdate - [Function]</a>
|
||||
If you want to control the cell updating process by yourself, for example, connect with `Redux` or saving data to backend database, `cellEdit.onUpdate` is a great chance you can work on it.
|
||||
|
||||
Firsylt, `react-bootstrap-table2` allow `cellEdit.onUpdate` to return a promise:
|
||||
|
||||
```js
|
||||
const cellEdit = {
|
||||
// omit...
|
||||
onUpdate: (rowId, dataField, newValue) => {
|
||||
return apiCall().then(response => {
|
||||
console.log('update cell to backend successfully');
|
||||
// Actually, you dont do any thing here, we will update the new value when resolve your promise
|
||||
})
|
||||
.catch(err => throw new Error(err.message));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
If your promise is resolved successfully, `react-bootstrap-table2` will default help you to update the new cell value.
|
||||
If your promise is resolved failure, you can throw an `Error` instance, `react-bootstrap-table2` will show up the error message (Same behavior like [`column.validator`](./columns.md#validator)).
|
||||
|
||||
In some case, backend will return a new value to client side and you want to apply this new value instead of the value that user input. In this situation, you can return an object which contain a `value` property:
|
||||
|
||||
```js
|
||||
const cellEdit = {
|
||||
// omit...
|
||||
onUpdate: (rowId, dataField, newValue) => {
|
||||
return apiCall().then(response => {
|
||||
return { value: response.value }; // response.value is from your backend api
|
||||
})
|
||||
.catch(err => throw new Error(err.message));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
If your application integgrate with `Redux`, you may need to dispatch an action in `cellEdit.onUpdate` callback. In this circumstances, you need to return `false` explicity which `react-bootstrap-table2` will stop any operation internally and wait rerender by your application.
|
||||
|
||||
In a simple redux application, you probably need to handle those props by your application:
|
||||
|
||||
* [`cellEdit.editing`](#editing): Is cell still on editing or not? This value should always be `true` when saving cell failure.
|
||||
* [`cellEdit.errorMessage`](#errorMessage): Error message when save the cell failure.
|
||||
* [`cellEdit.onErrorMessageDisappear`](#onErrorMessageDisappear): This callback will be called when error message alert closed automatically.
|
||||
* `cellEdit.onUpdate`
|
||||
|
||||
```js
|
||||
const cellEdit = {
|
||||
editing: this.props.editing,
|
||||
errorMessage: this.props.errorMessage,
|
||||
onErrorMessageDisappear: () => {
|
||||
// cleanErrorMessage is an action creator
|
||||
this.props.dispatch(cleanErrorMessage());
|
||||
},
|
||||
onUpdate: (rowId, dataField, newValue) => {
|
||||
// updateCell is an action creator
|
||||
this.props.dispatch(updateCell(rowId, dataField, newValue)));
|
||||
return false; // Have to return false here
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Please check [this](https://github.com/react-bootstrap-table/react-bootstrap-table2/blob/develop/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-with-redux-table.js) exmaple to learn how use `cellEdit` with a redux application
|
||||
|
||||
### <a name='editing'>cellEdit.editing - [Bool]</a>
|
||||
This only used when you want to control cell saving externally, `cellEdit.editing` will be a flag to tell `react-bootstrap-table2` whether currecnt editing cell is still editing or not. Because, it's possible that some error happen when you saving cell, in this situation, you need to configre this value as `false` to keep the cell as edtiable and show up an error message.
|
||||
|
||||
### <a name='errorMessage'>cellEdit.errorMessage - [String]</a>
|
||||
Same as [`cellEdit.editing`](#editing). This prop is not often used. Only used when you keep the error message in your application state.
|
||||
This prop is not often used. Only used when you keep the error message in your application state and also handle the cell editing on remote mode.
|
||||
|
||||
### <a name='onErrorMessageDisappear'>cellEdit.onErrorMessageDisappear - [Function]</a>
|
||||
This callback function will be called when error message discard.
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
/* eslint arrow-body-style: 0 */
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import BootstrapTable from 'react-bootstrap-table2';
|
||||
import Code from 'components/common/code-block';
|
||||
import { productsGenerator, sleep } 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 = `\
|
||||
class CellEditWithPromise extends Component {
|
||||
handleCellEditing = (rowId, dataField, newValue) => {
|
||||
return sleep(1000).then(() => {
|
||||
if (dataField === 'price' && (newValue < 2000 || isNaN(newValue))) {
|
||||
throw new Error('Product Price should bigger than $2000');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true,
|
||||
onUpdate: this.handleCellEditing
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
class CellEditWithPromise extends Component {
|
||||
handleCellEditing = (rowId, dataField, newValue) => {
|
||||
return sleep(1000).then(() => {
|
||||
if (dataField === 'price' && (newValue < 2000 || isNaN(newValue))) {
|
||||
throw new Error('Product Price should bigger than $2000');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
blurToSave: true,
|
||||
onUpdate: this.handleCellEditing
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CellEditWithPromise;
|
||||
|
||||
@ -1,212 +0,0 @@
|
||||
/* eslint no-unused-vars: 0 */
|
||||
/* eslint react/prop-types: 0 */
|
||||
/* eslint arrow-body-style: 0 */
|
||||
/* eslint consistent-return: 0 */
|
||||
/* eslint no-class-assign: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import thunk from 'redux-thunk';
|
||||
import { Provider, connect } from 'react-redux';
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import BootstrapTable from 'react-bootstrap-table2';
|
||||
import Code from 'components/common/code-block';
|
||||
import { productsGenerator } from 'utils/common';
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const sourceCode = `\
|
||||
/////////////////////// Action Creator ///////////////////////
|
||||
const setErrorMessage = (errorMessage = null) => {
|
||||
return { type: 'SET_ERR_MESSAGE', errorMessage };
|
||||
};
|
||||
|
||||
// Async Action, using redux-thunk
|
||||
const cellEditingAsync = (rowId, dataField, newValue) => {
|
||||
return (dispatch) => {
|
||||
setTimeout(() => {
|
||||
if (dataField === 'price' && (newValue < 2000 || isNaN(newValue))) {
|
||||
dispatch(setErrorMessage('Product Price should bigger than $2000'));
|
||||
} else {
|
||||
dispatch({ type: 'ADD_SUCCESS', rowId, dataField, newValue });
|
||||
}
|
||||
}, 1200);
|
||||
};
|
||||
};
|
||||
|
||||
/////////////////////// Component ///////////////////////
|
||||
class CellEditWithRedux extends Component {
|
||||
// dispatch a async action
|
||||
handleCellEditing = (rowId, dataField, newValue) => {
|
||||
this.props.dispatch(cellEditingAsync(rowId, dataField, newValue));
|
||||
return false;
|
||||
}
|
||||
|
||||
handleErrorMsgDisappear = () => {
|
||||
this.props.dispatch(setErrorMessage());
|
||||
}
|
||||
|
||||
render() {
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
editing: this.props.cellEditing,
|
||||
errorMessage: this.props.errorMessage,
|
||||
onUpdate: this.handleCellEditing,
|
||||
onErrorMessageDisappear: this.handleErrorMsgDisappear
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ this.props.data } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
// connect
|
||||
CellEditWithRedux = connect(state => state)(CellEditWithRedux);
|
||||
|
||||
/////////////////////// Reducer ///////////////////////
|
||||
// initial state object and simple reducers
|
||||
const initialState = {
|
||||
data: productsGenerator(),
|
||||
cellEditing: false,
|
||||
errorMessage: null
|
||||
};
|
||||
|
||||
const reducers = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'ADD_SUCCESS': {
|
||||
const { rowId, dataField, newValue } = action;
|
||||
const data = [...state.data];
|
||||
const rowIndex = data.findIndex(r => r.id === rowId);
|
||||
data[rowIndex][dataField] = newValue;
|
||||
return {
|
||||
data,
|
||||
cellEditing: false,
|
||||
errorMessage: null
|
||||
};
|
||||
}
|
||||
case 'SET_ERR_MESSAGE': {
|
||||
const { errorMessage } = action;
|
||||
return {
|
||||
...state,
|
||||
cellEditing: true,
|
||||
errorMessage
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return { ...state };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////// Index ///////////////////////
|
||||
const store = createStore(reducers, initialState, applyMiddleware(thunk));
|
||||
|
||||
const Index = () => (
|
||||
<Provider store={store}>
|
||||
<CellEditWithRedux />
|
||||
</Provider>
|
||||
);
|
||||
`;
|
||||
|
||||
const setErrorMessage = (errorMessage = null) => {
|
||||
return { type: 'SET_ERR_MESSAGE', errorMessage };
|
||||
};
|
||||
|
||||
// Async Action, using redux-thunk
|
||||
const cellEditingAsync = (rowId, dataField, newValue) => {
|
||||
return (dispatch) => {
|
||||
setTimeout(() => {
|
||||
if (dataField === 'price' && (newValue < 2000 || isNaN(newValue))) {
|
||||
dispatch(setErrorMessage('Product Price should bigger than $2000'));
|
||||
} else {
|
||||
dispatch({ type: 'ADD_SUCCESS', rowId, dataField, newValue });
|
||||
}
|
||||
}, 1200);
|
||||
};
|
||||
};
|
||||
|
||||
class CellEditWithRedux extends Component {
|
||||
// dispatch a async action
|
||||
handleCellEditing = (rowId, dataField, newValue) => {
|
||||
this.props.dispatch(cellEditingAsync(rowId, dataField, newValue));
|
||||
return false;
|
||||
}
|
||||
|
||||
handleErrorMsgDisappear = () => {
|
||||
this.props.dispatch(setErrorMessage());
|
||||
}
|
||||
|
||||
render() {
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
editing: this.props.cellEditing,
|
||||
errorMessage: this.props.errorMessage,
|
||||
onUpdate: this.handleCellEditing,
|
||||
onErrorMessageDisappear: this.handleErrorMsgDisappear
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ this.props.data } columns={ columns } cellEdit={ cellEdit } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
// connect
|
||||
CellEditWithRedux = connect(state => state)(CellEditWithRedux);
|
||||
|
||||
// initial state object and simple reducers
|
||||
const initialState = {
|
||||
data: productsGenerator(),
|
||||
cellEditing: false,
|
||||
errorMessage: null
|
||||
};
|
||||
|
||||
const reducers = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'ADD_SUCCESS': {
|
||||
const { rowId, dataField, newValue } = action;
|
||||
const data = JSON.parse(JSON.stringify(state.data));
|
||||
const rowIndex = data.findIndex(r => r.id === rowId);
|
||||
data[rowIndex][dataField] = newValue;
|
||||
return {
|
||||
data,
|
||||
cellEditing: false,
|
||||
errorMessage: null
|
||||
};
|
||||
}
|
||||
case 'SET_ERR_MESSAGE': {
|
||||
const { errorMessage } = action;
|
||||
return {
|
||||
...state,
|
||||
cellEditing: true,
|
||||
errorMessage
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return { ...state };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const store = createStore(reducers, initialState, applyMiddleware(thunk));
|
||||
|
||||
const Index = () => (
|
||||
<Provider store={ store }>
|
||||
<CellEditWithRedux />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
export default Index;
|
||||
|
||||
165
packages/react-bootstrap-table2-example/examples/remote/remote-celledit.js
vendored
Normal file
165
packages/react-bootstrap-table2-example/examples/remote/remote-celledit.js
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import BootstrapTable 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 RemoteCellEdit = (props) => {
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
errorMessage: props.errorMessage
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BootstrapTable
|
||||
remote={ { cellEdit: true } }
|
||||
keyField="id"
|
||||
data={ props.data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
onTableChange={ props.onTableChange }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
RemoteCellEdit.propTypes = {
|
||||
data: PropTypes.array.isRequired,
|
||||
onTableChange: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
class Container extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: products,
|
||||
errorMessage: null
|
||||
};
|
||||
}
|
||||
|
||||
handleTableChange = (type, { data, cellEdit: { rowId, dataField, newValue } }) => {
|
||||
setTimeout(() => {
|
||||
if (newValue === 'test' && dataField === 'name') {
|
||||
this.setState(() => ({
|
||||
data,
|
||||
errorMessage: 'Oops, product name shouldn't be "test"'
|
||||
}));
|
||||
} else {
|
||||
const result = data.map((row) => {
|
||||
if (row.id === rowId) {
|
||||
const newRow = { ...row };
|
||||
newRow[dataField] = newValue;
|
||||
return newRow;
|
||||
}
|
||||
return row;
|
||||
});
|
||||
this.setState(() => ({
|
||||
data: result,
|
||||
errorMessage: null
|
||||
}));
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RemoteCellEdit
|
||||
data={ this.state.data }
|
||||
errorMessage={ this.state.errorMessage }
|
||||
onTableChange={ this.handleTableChange }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
const RemoteCellEdit = (props) => {
|
||||
const cellEdit = {
|
||||
mode: 'click',
|
||||
errorMessage: props.errorMessage
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BootstrapTable
|
||||
remote={ { cellEdit: true } }
|
||||
keyField="id"
|
||||
data={ props.data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
onTableChange={ props.onTableChange }
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
RemoteCellEdit.propTypes = {
|
||||
data: PropTypes.array.isRequired,
|
||||
onTableChange: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
class Container extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: products,
|
||||
errorMessage: null
|
||||
};
|
||||
}
|
||||
|
||||
handleTableChange = (type, { data, cellEdit: { rowId, dataField, newValue } }) => {
|
||||
setTimeout(() => {
|
||||
if (newValue === 'test' && dataField === 'name') {
|
||||
this.setState(() => ({
|
||||
data,
|
||||
errorMessage: 'Oops, product name shouldn\'t be "test"'
|
||||
}));
|
||||
} else {
|
||||
const result = data.map((row) => {
|
||||
if (row.id === rowId) {
|
||||
const newRow = { ...row };
|
||||
newRow[dataField] = newValue;
|
||||
return newRow;
|
||||
}
|
||||
return row;
|
||||
});
|
||||
this.setState(() => ({
|
||||
data: result,
|
||||
errorMessage: null
|
||||
}));
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<RemoteCellEdit
|
||||
data={ this.state.data }
|
||||
errorMessage={ this.state.errorMessage }
|
||||
onTableChange={ this.handleTableChange }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Container;
|
||||
@ -63,8 +63,6 @@ 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 CellEditWithPromise from 'examples/cell-edit/cell-edit-with-promise-table';
|
||||
import CellEditWithRedux from 'examples/cell-edit/cell-edit-with-redux-table';
|
||||
|
||||
// work on row selection
|
||||
import SingleSelectionTable from 'examples/row-selection/single-selection';
|
||||
@ -91,6 +89,7 @@ import TableOverlay from 'examples/loading-overlay/table-overlay';
|
||||
import RemoteSort from 'examples/remote/remote-sort';
|
||||
import RemoteFilter from 'examples/remote/remote-filter';
|
||||
import RemotePaginationTable from 'examples/remote/remote-pagination';
|
||||
import RemoteCellEdit from 'examples/remote/remote-celledit';
|
||||
import RemoteAll from 'examples/remote/remote-all';
|
||||
|
||||
// css style
|
||||
@ -165,9 +164,7 @@ storiesOf('Cell Editing', module)
|
||||
.add('Rich Hook Functions', () => <CellEditHooks />)
|
||||
.add('Validation', () => <CellEditValidator />)
|
||||
.add('Custom Cell Style When Editing', () => <CellEditStyleTable />)
|
||||
.add('Custom Cell Classes When Editing', () => <CellEditClassTable />)
|
||||
.add('Async Cell Editing(Promise)', () => <CellEditWithPromise />)
|
||||
.add('Async Cell Editing(Redux)', () => <CellEditWithRedux />);
|
||||
.add('Custom Cell Classes When Editing', () => <CellEditClassTable />);
|
||||
|
||||
storiesOf('Row Selection', module)
|
||||
.add('Single Selection', () => <SingleSelectionTable />)
|
||||
@ -194,4 +191,5 @@ storiesOf('Remote', module)
|
||||
.add('Remote Sort', () => <RemoteSort />)
|
||||
.add('Remote Filter', () => <RemoteFilter />)
|
||||
.add('Remote Pagination', () => <RemotePaginationTable />)
|
||||
.add('Remote Cell Editing', () => <RemoteCellEdit />)
|
||||
.add('Remote All', () => <RemoteAll />);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
/* eslint no-param-reassign: 0 */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { filters } from './filter';
|
||||
@ -15,14 +17,20 @@ export default (Base, {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { currFilters: {}, isDataChanged: false };
|
||||
this.state = { currFilters: {}, isDataChanged: props.isDataChanged || false };
|
||||
this.onFilter = this.onFilter.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
componentWillReceiveProps({ isDataChanged, store, columns }) {
|
||||
// consider to use lodash.isEqual
|
||||
if (JSON.stringify(this.state.currFilters) !== JSON.stringify(nextProps.store.filters)) {
|
||||
this.setState(() => ({ isDataChanged: true, currFilters: nextProps.store.filters }));
|
||||
if (JSON.stringify(this.state.currFilters) !== JSON.stringify(store.filters)) {
|
||||
this.setState(() => ({ isDataChanged: true, currFilters: store.filters }));
|
||||
} else if (isDataChanged) {
|
||||
if (!(this.isRemoteFiltering() || this.isRemotePagination()) &&
|
||||
Object.keys(this.state.currFilters).length > 0) {
|
||||
store.filteredData = filters(store, columns, _)(this.state.currFilters);
|
||||
}
|
||||
this.setState(() => ({ isDataChanged }));
|
||||
} else {
|
||||
this.setState(() => ({ isDataChanged: false }));
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ describe('Wrapper', () => {
|
||||
onTableChangeCB.reset();
|
||||
});
|
||||
|
||||
const createTableProps = () => {
|
||||
const createTableProps = (props) => {
|
||||
const tableProps = {
|
||||
keyField: 'id',
|
||||
columns: [{
|
||||
@ -47,7 +47,8 @@ describe('Wrapper', () => {
|
||||
filter: filter(),
|
||||
_,
|
||||
store: new Store('id'),
|
||||
onTableChange: onTableChangeCB
|
||||
onTableChange: onTableChangeCB,
|
||||
...props
|
||||
};
|
||||
tableProps.store.data = data;
|
||||
return tableProps;
|
||||
@ -105,6 +106,17 @@ describe('Wrapper', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.isDataChanged is true and remote is enable', () => {
|
||||
beforeEach(() => {
|
||||
nextProps = createTableProps({ isDataChanged: true });
|
||||
instance.componentWillReceiveProps(nextProps);
|
||||
});
|
||||
|
||||
it('should setting isDataChanged as true', () => {
|
||||
expect(instance.state.isDataChanged).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.store.filters is different from current state.currFilters', () => {
|
||||
beforeEach(() => {
|
||||
nextProps = createTableProps();
|
||||
|
||||
@ -130,13 +130,11 @@ BootstrapTable.propTypes = {
|
||||
filter: PropTypes.object,
|
||||
cellEdit: PropTypes.shape({
|
||||
mode: PropTypes.oneOf([Const.CLICK_TO_CELL_EDIT, Const.DBCLICK_TO_CELL_EDIT]).isRequired,
|
||||
onUpdate: PropTypes.func,
|
||||
onErrorMessageDisappear: PropTypes.func,
|
||||
blurToSave: PropTypes.bool,
|
||||
beforeSaveCell: PropTypes.func,
|
||||
afterSaveCell: PropTypes.func,
|
||||
nonEditableRows: PropTypes.func,
|
||||
editing: PropTypes.bool,
|
||||
timeToCloseMessage: PropTypes.number,
|
||||
errorMessage: PropTypes.string
|
||||
}),
|
||||
@ -146,8 +144,7 @@ BootstrapTable.propTypes = {
|
||||
currEditCell: PropTypes.shape({
|
||||
ridx: PropTypes.number,
|
||||
cidx: PropTypes.number,
|
||||
message: PropTypes.string,
|
||||
editing: PropTypes.bool
|
||||
message: PropTypes.string
|
||||
}),
|
||||
selectRow: PropTypes.shape({
|
||||
mode: PropTypes.oneOf([Const.ROW_SELECT_SINGLE, Const.ROW_SELECT_MULTIPLE]).isRequired,
|
||||
|
||||
@ -1,44 +1,54 @@
|
||||
/* eslint react/prop-types: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import _ from '../utils';
|
||||
import remoteResolver from '../props-resolver/remote-resolver';
|
||||
|
||||
export default (Base, parentProps) =>
|
||||
class CellEditWrapper extends Component {
|
||||
export default Base =>
|
||||
class CellEditWrapper extends remoteResolver(Component) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.startEditing = this.startEditing.bind(this);
|
||||
this.escapeEditing = this.escapeEditing.bind(this);
|
||||
this.completeEditing = this.completeEditing.bind(this);
|
||||
this.handleCellUpdate = this.handleCellUpdate.bind(this);
|
||||
this.updateEditingWithErr = this.updateEditingWithErr.bind(this);
|
||||
this.state = {
|
||||
ridx: null,
|
||||
cidx: null,
|
||||
message: null,
|
||||
editing: false
|
||||
isDataChanged: false
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.cellEdit) {
|
||||
if (nextProps.cellEdit.editing) {
|
||||
if (nextProps.cellEdit && this.isRemoteCellEdit()) {
|
||||
if (nextProps.cellEdit.errorMessage) {
|
||||
this.setState(() => ({
|
||||
...this.state,
|
||||
isDataChanged: false,
|
||||
message: nextProps.cellEdit.errorMessage
|
||||
}));
|
||||
} else {
|
||||
this.setState(() => ({
|
||||
isDataChanged: true
|
||||
}));
|
||||
this.escapeEditing();
|
||||
}
|
||||
} else {
|
||||
this.setState(() => ({
|
||||
isDataChanged: false
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
handleCellUpdate(row, column, newValue) {
|
||||
const { keyField, cellEdit } = this.props;
|
||||
const { keyField, cellEdit, store } = this.props;
|
||||
const { beforeSaveCell, afterSaveCell } = cellEdit;
|
||||
const oldValue = _.get(row, column.dataField);
|
||||
const rowId = _.get(row, keyField);
|
||||
if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
|
||||
if (parentProps.onUpdateCell(rowId, column.dataField, newValue)) {
|
||||
if (this.isRemoteCellEdit()) {
|
||||
this.handleCellChange(rowId, column.dataField, newValue);
|
||||
} else {
|
||||
store.edit(rowId, column.dataField, newValue);
|
||||
if (_.isFunction(afterSaveCell)) afterSaveCell(oldValue, newValue, row, column);
|
||||
this.completeEditing();
|
||||
}
|
||||
@ -49,7 +59,7 @@ export default (Base, parentProps) =>
|
||||
ridx: null,
|
||||
cidx: null,
|
||||
message: null,
|
||||
editing: false
|
||||
isDataChanged: true
|
||||
}));
|
||||
}
|
||||
|
||||
@ -58,7 +68,7 @@ export default (Base, parentProps) =>
|
||||
this.setState(() => ({
|
||||
ridx,
|
||||
cidx,
|
||||
editing: true
|
||||
isDataChanged: false
|
||||
}));
|
||||
};
|
||||
|
||||
@ -69,27 +79,21 @@ export default (Base, parentProps) =>
|
||||
escapeEditing() {
|
||||
this.setState(() => ({
|
||||
ridx: null,
|
||||
cidx: null,
|
||||
editing: false
|
||||
}));
|
||||
}
|
||||
|
||||
updateEditingWithErr(message) {
|
||||
this.setState(() => ({
|
||||
...this.state,
|
||||
message
|
||||
cidx: null
|
||||
}));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isDataChanged, ...rest } = this.state;
|
||||
return (
|
||||
<Base
|
||||
{ ...this.props }
|
||||
isDataChanged={ isDataChanged }
|
||||
data={ this.props.store.data }
|
||||
onCellUpdate={ this.handleCellUpdate }
|
||||
onStartEditing={ this.startEditing }
|
||||
onEscapeEditing={ this.escapeEditing }
|
||||
currEditCell={ { ...this.state } }
|
||||
currEditCell={ { ...rest } }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
39
packages/react-bootstrap-table2/src/container.js
vendored
39
packages/react-bootstrap-table2/src/container.js
vendored
@ -16,11 +16,10 @@ const withDataStore = Base =>
|
||||
this.store = new Store(props.keyField);
|
||||
this.store.data = props.data;
|
||||
this.wrapComponents();
|
||||
this.handleUpdateCell = this.handleUpdateCell.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.store.data = nextProps.data;
|
||||
this.store.setAllData(nextProps.data);
|
||||
}
|
||||
|
||||
wrapComponents() {
|
||||
@ -45,41 +44,13 @@ const withDataStore = Base =>
|
||||
});
|
||||
}
|
||||
|
||||
if (cellEdit) {
|
||||
this.BaseComponent = withCellEdit(this.BaseComponent);
|
||||
}
|
||||
|
||||
if (selectRow) {
|
||||
this.BaseComponent = withSelection(this.BaseComponent);
|
||||
}
|
||||
|
||||
if (cellEdit) {
|
||||
this.BaseComponent = withCellEdit(this.BaseComponent, {
|
||||
ref: node => this.cellEditWrapper = node,
|
||||
onUpdateCell: this.handleUpdateCell
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleUpdateCell(rowId, dataField, newValue) {
|
||||
const { cellEdit } = this.props;
|
||||
// handle cell editing internal
|
||||
if (!cellEdit.onUpdate) {
|
||||
this.store.edit(rowId, dataField, newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle cell editing external
|
||||
const aPromise = cellEdit.onUpdate(rowId, dataField, newValue);
|
||||
if (_.isDefined(aPromise) && aPromise !== false) { // TODO: should be a promise here
|
||||
aPromise.then((result = true) => {
|
||||
const response = result === true ? {} : result;
|
||||
if (_.isObject(response)) {
|
||||
const { value } = response;
|
||||
this.store.edit(rowId, dataField, value || newValue);
|
||||
this.cellEditWrapper.completeEditing();
|
||||
}
|
||||
}).catch((e) => {
|
||||
this.cellEditWrapper.updateEditingWithErr(e.message);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@ -10,7 +10,7 @@ export default ExtendBase =>
|
||||
filters: store.filters,
|
||||
sortField: store.sortField,
|
||||
sortOrder: store.sortOrder,
|
||||
data: store.data,
|
||||
data: store.getAllData(),
|
||||
...state
|
||||
};
|
||||
}
|
||||
@ -30,6 +30,11 @@ export default ExtendBase =>
|
||||
return remote === true || (_.isObject(remote) && remote.sort);
|
||||
}
|
||||
|
||||
isRemoteCellEdit() {
|
||||
const { remote } = this.props;
|
||||
return remote === true || (_.isObject(remote) && remote.cellEdit);
|
||||
}
|
||||
|
||||
handleRemotePageChange() {
|
||||
this.props.onTableChange('pagination', this.getNewestState());
|
||||
}
|
||||
@ -46,4 +51,9 @@ export default ExtendBase =>
|
||||
handleSortChange() {
|
||||
this.props.onTableChange('sort', this.getNewestState());
|
||||
}
|
||||
|
||||
handleCellChange(rowId, dataField, newValue) {
|
||||
const cellEdit = { rowId, dataField, newValue };
|
||||
this.props.onTableChange('cellEdit', this.getNewestState({ cellEdit }));
|
||||
}
|
||||
};
|
||||
|
||||
@ -34,6 +34,10 @@ export default class Store {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
setAllData(data) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
get data() {
|
||||
if (Object.keys(this._filters).length > 0) {
|
||||
return this._filteredData;
|
||||
|
||||
@ -31,13 +31,10 @@ describe('CellEditWrapper', () => {
|
||||
};
|
||||
|
||||
const keyField = 'id';
|
||||
let onUpdateCellCB = sinon.stub();
|
||||
const store = new Store(keyField);
|
||||
store.data = data;
|
||||
|
||||
const CellEditWrapper = wrapperFactory(Container, {
|
||||
onUpdateCell: onUpdateCellCB
|
||||
});
|
||||
const CellEditWrapper = wrapperFactory(Container);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
@ -60,24 +57,24 @@ describe('CellEditWrapper', () => {
|
||||
expect(wrapper.state().ridx).toBeNull();
|
||||
expect(wrapper.state().cidx).toBeNull();
|
||||
expect(wrapper.state().message).toBeNull();
|
||||
expect(wrapper.state().editing).toBeFalsy();
|
||||
expect(wrapper.state().isDataChanged).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should inject correct props to base component', () => {
|
||||
expect(wrapper.props().onCellUpdate).toBeDefined();
|
||||
expect(wrapper.props().onStartEditing).toBeDefined();
|
||||
expect(wrapper.props().onEscapeEditing).toBeDefined();
|
||||
expect(wrapper.props().isDataChanged).toBe(wrapper.state().isDataChanged);
|
||||
expect(wrapper.props().currEditCell).toBeDefined();
|
||||
expect(wrapper.props().currEditCell.ridx).toBeNull();
|
||||
expect(wrapper.props().currEditCell.cidx).toBeNull();
|
||||
expect(wrapper.props().currEditCell.message).toBeNull();
|
||||
expect(wrapper.props().currEditCell.editing).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('when receive new cellEdit prop', () => {
|
||||
const spy = jest.spyOn(CellEditWrapper.prototype, 'escapeEditing');
|
||||
|
||||
describe('and cellEdit.editing is false', () => {
|
||||
describe('and cellEdit is not work on remote', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
@ -88,54 +85,69 @@ describe('CellEditWrapper', () => {
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
wrapper.setProps({ cellEdit: { ...cellEdit, editing: false } });
|
||||
wrapper.setProps({ cellEdit: { ...cellEdit } });
|
||||
});
|
||||
|
||||
it('should call escapeEditing', () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have correct state', () => {
|
||||
expect(wrapper.state().ridx).toBeNull();
|
||||
expect(wrapper.state().cidx).toBeNull();
|
||||
expect(wrapper.state().message).toBeNull();
|
||||
expect(wrapper.state().editing).toBeFalsy();
|
||||
it('should always setting state.isDataChanged as false', () => {
|
||||
expect(wrapper.state().isDataChanged).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('and cellEdit.editing is true', () => {
|
||||
const errorMessage = 'test';
|
||||
describe('and cellEdit is work on remote', () => {
|
||||
let errorMessage;
|
||||
const ridx = 1;
|
||||
const cidx = 2;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
wrapper.setState({ ridx, cidx, editing: true });
|
||||
wrapper.setProps({ cellEdit: { ...cellEdit, editing: true, errorMessage } });
|
||||
describe('and cellEdit.errorMessage is defined', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
remote={ { cellEdit: true } }
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
errorMessage = 'test';
|
||||
wrapper.setState({ ridx, cidx });
|
||||
wrapper.setProps({ cellEdit: { ...cellEdit, errorMessage } });
|
||||
});
|
||||
|
||||
it('should setting correct state', () => {
|
||||
expect(wrapper.state().ridx).toEqual(ridx);
|
||||
expect(wrapper.state().cidx).toEqual(cidx);
|
||||
expect(wrapper.state().isDataChanged).toBeFalsy();
|
||||
expect(wrapper.state().message).toEqual(errorMessage);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have correct state', () => {
|
||||
expect(wrapper.state().ridx).toEqual(ridx);
|
||||
expect(wrapper.state().cidx).toEqual(cidx);
|
||||
expect(wrapper.state().editing).toBeTruthy();
|
||||
expect(wrapper.state().message).toEqual(errorMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('and cellEdit.errorMessage is undefined', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
remote={ { cellEdit: true } }
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
errorMessage = null;
|
||||
wrapper.setState({ ridx, cidx });
|
||||
wrapper.setProps({ cellEdit: { ...cellEdit, errorMessage } });
|
||||
});
|
||||
|
||||
describe('call updateEditingWithErr function', () => {
|
||||
it('should set state.message correctly', () => {
|
||||
const message = 'test';
|
||||
wrapper.instance().updateEditingWithErr(message);
|
||||
expect(wrapper.state().message).toEqual(message);
|
||||
it('should setting correct state', () => {
|
||||
expect(wrapper.state().isDataChanged).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should escape current editing', () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -144,7 +156,6 @@ describe('CellEditWrapper', () => {
|
||||
wrapper.instance().escapeEditing();
|
||||
expect(wrapper.state().ridx).toBeNull();
|
||||
expect(wrapper.state().cidx).toBeNull();
|
||||
expect(wrapper.state().editing).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
@ -155,7 +166,7 @@ describe('CellEditWrapper', () => {
|
||||
wrapper.instance().startEditing(ridx, cidx);
|
||||
expect(wrapper.state().ridx).toEqual(ridx);
|
||||
expect(wrapper.state().cidx).toEqual(cidx);
|
||||
expect(wrapper.state().editing).toBeTruthy();
|
||||
expect(wrapper.state().isDataChanged).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('if selectRow.clickToSelect is defined', () => {
|
||||
@ -199,7 +210,6 @@ describe('CellEditWrapper', () => {
|
||||
wrapper.instance().startEditing(ridx, cidx);
|
||||
expect(wrapper.state().ridx).toEqual(ridx);
|
||||
expect(wrapper.state().cidx).toEqual(cidx);
|
||||
expect(wrapper.state().editing).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -210,7 +220,7 @@ describe('CellEditWrapper', () => {
|
||||
expect(wrapper.state().ridx).toBeNull();
|
||||
expect(wrapper.state().cidx).toBeNull();
|
||||
expect(wrapper.state().message).toBeNull();
|
||||
expect(wrapper.state().editing).toBeFalsy();
|
||||
expect(wrapper.state().isDataChanged).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@ -219,35 +229,75 @@ describe('CellEditWrapper', () => {
|
||||
const column = columns[1];
|
||||
const newValue = 'new name';
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
wrapper.instance().handleCellUpdate(row, column, newValue);
|
||||
describe('when cell edit is work on remote', () => {
|
||||
const spy = jest.spyOn(CellEditWrapper.prototype, 'handleCellChange');
|
||||
const onTableChangeCB = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
remote={ { cellEdit: true } }
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
onTableChange={ onTableChangeCB }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
wrapper.instance().handleCellUpdate(row, column, newValue);
|
||||
});
|
||||
|
||||
it('should calling handleCellChange correctly', () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(spy.mock.calls).toHaveLength(1);
|
||||
expect(spy.mock.calls[0]).toHaveLength(3);
|
||||
expect(spy.mock.calls[0][0]).toEqual(row[keyField]);
|
||||
expect(spy.mock.calls[0][1]).toEqual(column.dataField);
|
||||
expect(spy.mock.calls[0][2]).toEqual(newValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('should calling onUpdateCell callback correctly', () => {
|
||||
expect(onUpdateCellCB.callCount).toBe(1);
|
||||
expect(onUpdateCellCB.calledWith(row.id, column.dataField, newValue)).toBe(true);
|
||||
});
|
||||
describe('when cell edit is not work on remote', () => {
|
||||
const spyOnCompleteEditing = jest.spyOn(CellEditWrapper.prototype, 'completeEditing');
|
||||
const spyOnStoreEdit = jest.spyOn(Store.prototype, 'edit');
|
||||
|
||||
describe('when onUpdateCell function return true', () => {
|
||||
const spy = jest.spyOn(CellEditWrapper.prototype, 'completeEditing');
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
wrapper.instance().handleCellUpdate(row, column, newValue);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
spyOnStoreEdit.mockReset();
|
||||
spyOnCompleteEditing.mockReset();
|
||||
});
|
||||
|
||||
it('should calling props.store.edit', () => {
|
||||
expect(spyOnStoreEdit).toHaveBeenCalled();
|
||||
expect(spyOnStoreEdit.mock.calls).toHaveLength(1);
|
||||
expect(spyOnStoreEdit.mock.calls[0]).toHaveLength(3);
|
||||
expect(spyOnStoreEdit.mock.calls[0][0]).toEqual(row[keyField]);
|
||||
expect(spyOnStoreEdit.mock.calls[0][1]).toEqual(column.dataField);
|
||||
expect(spyOnStoreEdit.mock.calls[0][2]).toEqual(newValue);
|
||||
});
|
||||
|
||||
it('should calling completeEditing function', () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
expect(spyOnCompleteEditing).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('if cellEdit.afterSaveCell prop defined', () => {
|
||||
const aftereSaveCellCallBack = sinon.stub();
|
||||
|
||||
beforeEach(() => {
|
||||
cellEdit.beforeSaveCell = aftereSaveCellCallBack;
|
||||
cellEdit.afterSaveCell = aftereSaveCellCallBack;
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
keyField={ keyField }
|
||||
@ -269,28 +319,6 @@ describe('CellEditWrapper', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when onUpdateCell function return false', () => {
|
||||
const spy = jest.spyOn(CellEditWrapper.prototype, 'completeEditing');
|
||||
|
||||
beforeEach(() => {
|
||||
onUpdateCellCB = sinon.stub().returns(false);
|
||||
wrapper = shallow(
|
||||
<CellEditWrapper
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
wrapper.instance().handleCellUpdate(row, column, newValue);
|
||||
});
|
||||
|
||||
it('shouldn\'t calling completeEditing function', () => {
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('if cellEdit.beforeSaveCell prop defined', () => {
|
||||
const beforeSaveCellCallBack = sinon.stub();
|
||||
beforeEach(() => {
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
/* eslint react/prefer-stateless-function: 0 */
|
||||
/* eslint react/no-multi-comp: 0 */
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import BootstrapTable from '../src/bootstrap-table';
|
||||
import Container from '../src';
|
||||
import { getRowByRowId } from '../src/store/rows';
|
||||
|
||||
describe('container', () => {
|
||||
let wrapper;
|
||||
@ -79,63 +77,6 @@ describe('container', () => {
|
||||
it('should render BootstrapTable component successfully', () => {
|
||||
expect(wrapper.dive().find(BootstrapTable)).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe('for handleUpdateCell function', () => {
|
||||
const rowId = data[1].id;
|
||||
const dataField = columns[1].dataField;
|
||||
const newValue = 'tester';
|
||||
let result;
|
||||
|
||||
describe('when cellEdit.onUpdate callback is not defined', () => {
|
||||
beforeEach(() => {
|
||||
result = wrapper.instance().handleUpdateCell(rowId, dataField, newValue);
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should update store data directly', () => {
|
||||
const store = wrapper.instance().store;
|
||||
const row = getRowByRowId(store)(rowId);
|
||||
expect(row[dataField]).toEqual(newValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when cellEdit.onUpdate callback is define and which return false', () => {
|
||||
beforeEach(() => {
|
||||
cellEdit.onUpdate = sinon.stub().returns(false);
|
||||
wrapper = shallow(
|
||||
<Container
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
cellEdit={ cellEdit }
|
||||
/>
|
||||
);
|
||||
result = wrapper.instance().handleUpdateCell(rowId, dataField, newValue);
|
||||
});
|
||||
|
||||
it('should calling cellEdit.onUpdate callback correctly', () => {
|
||||
expect(cellEdit.onUpdate.callCount).toBe(1);
|
||||
expect(cellEdit.onUpdate.calledWith(rowId, dataField, newValue)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
it('shouldn\'t update store data', () => {
|
||||
const store = wrapper.instance().store;
|
||||
const row = getRowByRowId(store)(rowId);
|
||||
expect(row[dataField]).not.toEqual(newValue);
|
||||
});
|
||||
});
|
||||
|
||||
// We need refactoring handleUpdateCell function for handling promise firstly
|
||||
// then it will be much easier to test
|
||||
describe.skip('when cellEdit.onUpdate callback is define and which return a Promise', () => {});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selectRow prop is defined', () => {
|
||||
|
||||
@ -93,7 +93,6 @@ describe('TableResolver', () => {
|
||||
const expectNonEditableRows = [1, 2];
|
||||
const cellEdit = {
|
||||
mode: Const.DBCLICK_TO_CELL_EDIT,
|
||||
onUpdate: sinon.stub(),
|
||||
blurToSave: true,
|
||||
beforeSaveCell: sinon.stub(),
|
||||
afterSaveCell: sinon.stub(),
|
||||
|
||||
@ -134,6 +134,58 @@ describe('remoteResolver', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isRemoteCellEdit', () => {
|
||||
describe('when remote is false', () => {
|
||||
beforeEach(() => {
|
||||
shallowContainer();
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
expect(wrapper.instance().isRemoteCellEdit()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when remote is true', () => {
|
||||
beforeEach(() => {
|
||||
shallowContainer({ remote: true });
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(wrapper.instance().isRemoteCellEdit()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when remote.cellEdit is true', () => {
|
||||
beforeEach(() => {
|
||||
shallowContainer({ remote: { cellEdit: true } });
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(wrapper.instance().isRemoteCellEdit()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleCellChange', () => {
|
||||
const onTableChangeCB = sinon.stub();
|
||||
const rowId = 1;
|
||||
const dataField = 'name';
|
||||
const newValue = 'test';
|
||||
|
||||
beforeEach(() => {
|
||||
onTableChangeCB.reset();
|
||||
shallowContainer({ onTableChange: onTableChangeCB });
|
||||
wrapper.instance().handleCellChange(rowId, dataField, newValue);
|
||||
});
|
||||
|
||||
it('should calling props.onTableChange correctly', () => {
|
||||
const cellEdit = { rowId, dataField, newValue };
|
||||
expect(onTableChangeCB.calledOnce).toBeTruthy();
|
||||
expect(onTableChangeCB.calledWith(
|
||||
'cellEdit', wrapper.instance().getNewestState({ cellEdit }))).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleSortChange', () => {
|
||||
const onTableChangeCB = sinon.stub();
|
||||
beforeEach(() => {
|
||||
|
||||
@ -273,7 +273,6 @@ describe('Row', () => {
|
||||
cellEdit.cidx = editingColIndex;
|
||||
cellEdit.onUpdate = sinon.stub();
|
||||
cellEdit.onEscape = sinon.stub();
|
||||
cellEdit.onUpdate = sinon.stub();
|
||||
wrapper = shallow(
|
||||
<Row
|
||||
{ ...mockBodyResolvedProps }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user