diff --git a/docs/cell-edit.md b/docs/cell-edit.md
index 6c46151..cc56f6c 100644
--- a/docs/cell-edit.md
+++ b/docs/cell-edit.md
@@ -62,6 +62,24 @@ const cellEdit = {
}
```
+If you want to perform a async `beforeSaveCell`, you can do it like that:
+
+```js
+const cellEdit: {
+ // omit...
+ beforeSaveCell(oldValue, newValue, row, column, done) {
+ setTimeout(() => {
+ if (confirm('Do you want to accep this change?')) {
+ done(); // contine to save the changes
+ } else {
+ done(false); // reject the changes
+ }
+ }, 0);
+ return { async: true };
+ }
+};
+```
+
### cellEdit.afterSaveCell - [Function]
This callback function will be called after updating cell.
diff --git a/docs/columns.md b/docs/columns.md
index 5bb23a6..34b10c0 100644
--- a/docs/columns.md
+++ b/docs/columns.md
@@ -379,17 +379,27 @@ A new `String` will be the result of element headerAlign.
## column.events - [Object]
-You can assign any [HTML Event](https://www.w3schools.com/tags/ref_eventattributes.asp) on table column via event property:
+You can assign any [HTML Event](https://www.w3schools.com/tags/ref_eventattributes.asp) on table column via `events` property.
+
+`react-bootstrap-table2` currently only support following events which will receive some specific information:
+
+* onClick
+* onDoubleClick
+* onMouseEnter
+* onMouseLeave
+* onContextMenu
```js
{
// omit...
events: {
- onClick: e => { ... }
+ onClick: (e, column, columnIndex, row, rowIndex) => { ... },
}
}
```
+If the events is not listed above, the callback function will only pass the `event` object.
+
## column.headerEvents - [Object]
`headerEvents` same as [`column.events`](#events) but this is for header column.
@@ -543,6 +553,28 @@ The return value can be a bool or an object. If your validation is pass, return
}
```
+If you want to perform a asycn validation, you can do it like this:
+```js
+{
+ // omit...
+ validator: (newValue, row, column, done) => {
+ settimeout(() => {
+ // async validation ok
+ return done();
+
+ // async validation not ok
+ return done({
+ valid: false,
+ message: 'SOME_REASON_HERE'
+ });
+
+ }, 2000);
+ return { async: true };
+ }
+}
+```
+
+
## column.editCellStyle - [Object | Function]
You can use `column.editCellStyle` to custom the style of `
` when cell editing. It like most of customizable functionality, it also accept a callback function with following params:
diff --git a/docs/development.md b/docs/development.md
index ade502e..50a0464 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -3,7 +3,7 @@
### Setup
```bash
$ git clone https://github.com/react-bootstrap-table/react-bootstrap-table2.git
-$ cd react-bootstrap-table
+$ cd react-bootstrap-table2
$ npm install
$ lerna bootstrap # ./node_modules/.bin/lerna bootstrap
```
@@ -25,4 +25,4 @@ $ npm run storybook
$ npm test
$ npm run test:watch # for watch mode
$ npm run test:coverage # generate coverage report
-```
\ No newline at end of file
+```
diff --git a/packages/react-bootstrap-table2-editor/src/context.js b/packages/react-bootstrap-table2-editor/src/context.js
index c3f37c5..e9317e2 100644
--- a/packages/react-bootstrap-table2-editor/src/context.js
+++ b/packages/react-bootstrap-table2-editor/src/context.js
@@ -31,6 +31,7 @@ export default (
constructor(props) {
super(props);
+ this.doUpdate = this.doUpdate.bind(this);
this.startEditing = this.startEditing.bind(this);
this.escapeEditing = this.escapeEditing.bind(this);
this.completeEditing = this.completeEditing.bind(this);
@@ -55,11 +56,36 @@ export default (
}
handleCellUpdate(row, column, newValue) {
- const { keyField, cellEdit, data } = this.props;
- const { beforeSaveCell, afterSaveCell } = cellEdit.options;
+ const { cellEdit } = this.props;
+ const { beforeSaveCell } = cellEdit.options;
const oldValue = _.get(row, column.dataField);
+ const beforeSaveCellDone = (result = true) => {
+ if (result) {
+ this.doUpdate(row, column, newValue);
+ } else {
+ this.escapeEditing();
+ }
+ };
+ if (_.isFunction(beforeSaveCell)) {
+ const result = beforeSaveCell(
+ oldValue,
+ newValue,
+ row,
+ column,
+ beforeSaveCellDone
+ );
+ if (_.isObject(result) && result.async) {
+ return;
+ }
+ }
+ this.doUpdate(row, column, newValue);
+ }
+
+ doUpdate(row, column, newValue) {
+ const { keyField, cellEdit, data } = this.props;
+ const { afterSaveCell } = cellEdit.options;
const rowId = _.get(row, keyField);
- if (_.isFunction(beforeSaveCell)) beforeSaveCell(oldValue, newValue, row, column);
+ const oldValue = _.get(row, column.dataField);
if (isRemoteCellEdit()) {
handleCellChange(rowId, column.dataField, newValue);
} else {
diff --git a/packages/react-bootstrap-table2-editor/src/editing-cell.js b/packages/react-bootstrap-table2-editor/src/editing-cell.js
index 39aa9a5..7c693b1 100644
--- a/packages/react-bootstrap-table2-editor/src/editing-cell.js
+++ b/packages/react-bootstrap-table2-editor/src/editing-cell.js
@@ -44,6 +44,8 @@ export default (_, onStartEdit) =>
this.handleClick = this.handleClick.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.beforeComplete = this.beforeComplete.bind(this);
+ this.asyncbeforeCompete = this.asyncbeforeCompete.bind(this);
+ this.displayErrorMessage = this.displayErrorMessage.bind(this);
this.state = {
invalidMessage: null
};
@@ -79,16 +81,41 @@ export default (_, onStartEdit) =>
}, timeToCloseMessage);
}
+ displayErrorMessage(message) {
+ this.setState(() => ({
+ invalidMessage: message
+ }));
+ this.createTimer();
+ }
+
+ asyncbeforeCompete(newValue) {
+ return (result = { valid: true }) => {
+ const { valid, message } = result;
+ const { onUpdate, row, column } = this.props;
+ if (!valid) {
+ this.displayErrorMessage(message);
+ return;
+ }
+ onUpdate(row, column, newValue);
+ };
+ }
+
beforeComplete(newValue) {
const { onUpdate, row, column } = this.props;
if (_.isFunction(column.validator)) {
- const validateForm = column.validator(newValue, row, column);
- if (_.isObject(validateForm) && !validateForm.valid) {
- this.setState(() => ({
- invalidMessage: validateForm.message
- }));
- this.createTimer();
- return;
+ const validateForm = column.validator(
+ newValue,
+ row,
+ column,
+ this.asyncbeforeCompete(newValue)
+ );
+ if (_.isObject(validateForm)) {
+ if (validateForm.async) {
+ return;
+ } else if (!validateForm.valid) {
+ this.displayErrorMessage(validateForm.message);
+ return;
+ }
}
}
onUpdate(row, column, newValue);
diff --git a/packages/react-bootstrap-table2-editor/test/context.test.js b/packages/react-bootstrap-table2-editor/test/context.test.js
index 03cfd74..57b56bf 100644
--- a/packages/react-bootstrap-table2-editor/test/context.test.js
+++ b/packages/react-bootstrap-table2-editor/test/context.test.js
@@ -235,7 +235,8 @@ describe('CellEditContext', () => {
it('should call cellEdit.beforeSaveCell correctly', () => {
expect(beforeSaveCell).toHaveBeenCalledTimes(1);
- expect(beforeSaveCell).toHaveBeenCalledWith(oldValue, newValue, row, column);
+ expect(beforeSaveCell)
+ .toHaveBeenCalledWith(oldValue, newValue, row, column, expect.anything());
});
});
diff --git a/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-async-hooks-table.js b/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-async-hooks-table.js
new file mode 100644
index 0000000..9839437
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-async-hooks-table.js
@@ -0,0 +1,86 @@
+/* eslint no-unused-vars: 0 */
+/* eslint no-console: 0 */
+/* eslint no-alert: 0 */
+import React from 'react';
+
+import BootstrapTable from 'react-bootstrap-table-next';
+import cellEditFactory from 'react-bootstrap-table2-editor';
+import Code from 'components/common/code-block';
+import { productsGenerator } from 'utils/common';
+
+const products = productsGenerator();
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name'
+}, {
+ dataField: 'price',
+ text: 'Product Price'
+}];
+
+const sourceCode = `\
+import BootstrapTable from 'react-bootstrap-table-next';
+import cellEditFactory from 'react-bootstrap-table2-editor';
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name'
+}, {
+ dataField: 'price',
+ text: 'Product Price'
+}];
+
+function beforeSaveCell(oldValue, newValue, row, column, done) {
+ setTimeout(() => {
+ if (confirm('Do you want to accep this change?')) {
+ done(true);
+ } else {
+ done(false);
+ }
+ }, 0);
+ return { async: true };
+}
+
+
+`;
+
+function beforeSaveCell(oldValue, newValue, row, column, done) {
+ setTimeout(() => {
+ if (confirm('Do you want to accep this change?')) {
+ done(true);
+ } else {
+ done(false);
+ }
+ }, 0);
+ return { async: true };
+}
+
+export default () => (
+
+ You will get a confirm prompt when you try to save a cell
+
+ { sourceCode }
+
+);
diff --git a/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-async-validator-table.js b/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-async-validator-table.js
new file mode 100644
index 0000000..cd3c934
--- /dev/null
+++ b/packages/react-bootstrap-table2-example/examples/cell-edit/cell-edit-async-validator-table.js
@@ -0,0 +1,101 @@
+/* eslint no-unused-vars: 0 */
+import React from 'react';
+import BootstrapTable from 'react-bootstrap-table-next';
+import cellEditFactory from 'react-bootstrap-table2-editor';
+import Code from 'components/common/code-block';
+import { productsGenerator } from 'utils/common';
+
+const products = productsGenerator();
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name'
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ validator: (newValue, row, column, done) => {
+ setTimeout(() => {
+ if (isNaN(newValue)) {
+ return done({
+ valid: false,
+ message: 'Price should be numeric'
+ });
+ }
+ if (newValue < 2000) {
+ return done({
+ valid: false,
+ message: 'Price should bigger than 2000'
+ });
+ }
+ return done();
+ }, 2000);
+ return {
+ async: true
+ };
+ }
+}];
+
+const sourceCode = `\
+import BootstrapTable from 'react-bootstrap-table-next';
+import cellEditFactory from 'react-bootstrap-table2-editor';
+
+const columns = [{
+ dataField: 'id',
+ text: 'Product ID'
+}, {
+ dataField: 'name',
+ text: 'Product Name'
+}, {
+ dataField: 'price',
+ text: 'Product Price',
+ validator: (newValue, row, column, done) => {
+ setTimeout(() => {
+ if (isNaN(newValue)) {
+ return done({
+ valid: false,
+ message: 'Price should be numeric'
+ });
+ }
+ if (newValue < 2000) {
+ return done({
+ valid: false,
+ message: 'Price should bigger than 2000'
+ });
+ }
+ return done();
+ }, 2000);
+ return {
+ async: true
+ };
+ }
+}];
+
+
+`;
+
+export default () => (
+
+ Product Price should bigger than $2000
+
+ { sourceCode }
+
+);
diff --git a/packages/react-bootstrap-table2-example/examples/columns/column-event-table.js b/packages/react-bootstrap-table2-example/examples/columns/column-event-table.js
index 32f0d69..7f3e308 100644
--- a/packages/react-bootstrap-table2-example/examples/columns/column-event-table.js
+++ b/packages/react-bootstrap-table2-example/examples/columns/column-event-table.js
@@ -1,5 +1,6 @@
/* eslint no-unused-vars: 0 */
/* eslint no-alert: 0 */
+/* eslint no-console: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
@@ -12,7 +13,22 @@ const columns = [{
dataField: 'id',
text: 'Product ID',
events: {
- onClick: () => alert('Click on Product ID field')
+ onClick: (e, column, columnIndex, row, rowIndex) => {
+ console.log(e);
+ console.log(column);
+ console.log(columnIndex);
+ console.log(row);
+ console.log(rowIndex);
+ alert('Click on Product ID field');
+ },
+ onMouseEnter: (e, column, columnIndex, row, rowIndex) => {
+ console.log(e);
+ console.log(column);
+ console.log(columnIndex);
+ console.log(row);
+ console.log(rowIndex);
+ console.log('onMouseEnter on Product ID field');
+ }
}
}, {
dataField: 'name',
@@ -29,7 +45,22 @@ const columns = [{
dataField: 'id',
text: 'Product ID',
events: {
- onClick: () => alert('Click on Product ID field')
+ onClick: (e, column, columnIndex, row, rowIndex) => {
+ console.log(e);
+ console.log(column);
+ console.log(columnIndex);
+ console.log(row);
+ console.log(rowIndex);
+ alert('Click on Product ID field');
+ },
+ onMouseEnter: (e, column, columnIndex, row, rowIndex) => {
+ console.log(e);
+ console.log(column);
+ console.log(columnIndex);
+ console.log(row);
+ console.log(rowIndex);
+ console.log('onMouseEnter on Product ID field');
+ }
}
}, {
dataField: 'name',
@@ -44,7 +75,7 @@ const columns = [{
export default () => (
- Try to Click on Product ID columns
+ Try to Click or Mouse over on Product ID columns
{ sourceCode }
diff --git a/packages/react-bootstrap-table2-example/stories/index.js b/packages/react-bootstrap-table2-example/stories/index.js
index 519542e..7ba6b1b 100644
--- a/packages/react-bootstrap-table2-example/stories/index.js
+++ b/packages/react-bootstrap-table2-example/stories/index.js
@@ -97,7 +97,9 @@ import RowLevelEditableTable from 'examples/cell-edit/row-level-editable-table';
import ColumnLevelEditableTable from 'examples/cell-edit/column-level-editable-table';
import CellLevelEditable from 'examples/cell-edit/cell-level-editable-table';
import CellEditHooks from 'examples/cell-edit/cell-edit-hooks-table';
+import AsyncCellEditHooks from 'examples/cell-edit/cell-edit-async-hooks-table';
import CellEditValidator from 'examples/cell-edit/cell-edit-validator-table';
+import AsyncCellEditValidator from 'examples/cell-edit/cell-edit-async-validator-table';
import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table';
import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
import AutoSelectTextInput from 'examples/cell-edit/auto-select-text-input-table';
@@ -288,7 +290,9 @@ storiesOf('Cell Editing', module)
.add('Column Level Editable', () => )
.add('Cell Level Editable', () => )
.add('Rich Hook Functions', () => )
+ .add('Async Hook Functions', () => )
.add('Validation', () => )
+ .add('Async Validation', () => )
.add('Auto Select Text Input', () => )
.add('Custom Cell Style', () => )
.add('Custom Cell Classes', () => )
diff --git a/packages/react-bootstrap-table2/src/bootstrap-table.js b/packages/react-bootstrap-table2/src/bootstrap-table.js
index 2bf37a3..0c5c862 100644
--- a/packages/react-bootstrap-table2/src/bootstrap-table.js
+++ b/packages/react-bootstrap-table2/src/bootstrap-table.js
@@ -15,13 +15,12 @@ class BootstrapTable extends PropsBaseResolver(Component) {
super(props);
this.validateProps();
if (props.registerExposedAPI) {
- const getData = () => this.getData();
- props.registerExposedAPI(getData);
+ props.registerExposedAPI(this.getData);
}
}
// Exposed APIs
- getData = () => {
+ getData() {
return this.props.data;
}
diff --git a/packages/react-bootstrap-table2/src/row-event-delegater.js b/packages/react-bootstrap-table2/src/cell-event-delegater.js
similarity index 80%
rename from packages/react-bootstrap-table2/src/row-event-delegater.js
rename to packages/react-bootstrap-table2/src/cell-event-delegater.js
index 4727134..41e0f71 100644
--- a/packages/react-bootstrap-table2/src/row-event-delegater.js
+++ b/packages/react-bootstrap-table2/src/cell-event-delegater.js
@@ -7,17 +7,16 @@ const events = [
];
export default ExtendBase =>
- class RowEventDelegater extends ExtendBase {
+ class CellEventDelegater extends ExtendBase {
constructor(props) {
super(props);
- this.clickNum = 0;
this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this);
}
createDefaultEventHandler(cb) {
return (e) => {
- const { row, rowIndex } = this.props;
- cb(e, row, rowIndex);
+ const { column, columnIndex } = this.props;
+ cb(e, column, columnIndex);
};
}
diff --git a/packages/react-bootstrap-table2/src/cell.js b/packages/react-bootstrap-table2/src/cell.js
index 70b2633..46b10ea 100644
--- a/packages/react-bootstrap-table2/src/cell.js
+++ b/packages/react-bootstrap-table2/src/cell.js
@@ -2,9 +2,10 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
+import eventDelegater from './cell-event-delegater';
import _ from './utils';
-class Cell extends Component {
+class Cell extends eventDelegater(Component) {
constructor(props) {
super(props);
this.handleEditingCell = this.handleEditingCell.bind(this);
@@ -73,7 +74,7 @@ class Cell extends Component {
formatter,
formatExtraData
} = column;
- const attrs = { ...rest };
+ const attrs = this.delegate({ ...rest });
let content = column.isDummyField ? null : _.get(row, dataField);
if (formatter) {
diff --git a/packages/react-bootstrap-table2/src/row/row-pure-content.js b/packages/react-bootstrap-table2/src/row/row-pure-content.js
index 5b498ff..95ab857 100644
--- a/packages/react-bootstrap-table2/src/row/row-pure-content.js
+++ b/packages/react-bootstrap-table2/src/row/row-pure-content.js
@@ -50,13 +50,21 @@ export default class RowPureContent extends React.Component {
// render cell
let cellTitle;
let cellStyle = {};
- const cellAttrs = {
+ let cellAttrs = {
..._.isFunction(column.attrs)
? column.attrs(content, row, rowIndex, index)
- : column.attrs,
- ...column.events
+ : column.attrs
};
+ if (column.events) {
+ const events = Object.assign({}, column.events);
+ Object.keys(Object.assign({}, column.events)).forEach((key) => {
+ const originFn = events[key];
+ events[key] = (...rest) => originFn(...rest, row, rowIndex);
+ });
+ cellAttrs = { ...cellAttrs, ...events };
+ }
+
const cellClasses = _.isFunction(column.classes)
? column.classes(content, row, rowIndex, index)
: column.classes;
|