Compare commits

...

21 Commits

Author SHA1 Message Date
AllenFang
a50148fe85 Publish
- react-bootstrap-table2-example@0.1.8
 - react-bootstrap-table2-filter@0.1.7
 - react-bootstrap-table-next@0.1.11
2018-05-14 23:09:49 +08:00
Allen
c96156503f Merge pull request #336 from react-bootstrap-table/develop
20180513 release
2018-05-14 23:02:15 +08:00
AllenFang
ed2ba2a5c5 fix #334 2018-05-14 22:44:28 +08:00
AllenFang
f87fe3e544 patch docs, example and test for wrapperClasses 2018-05-12 13:54:40 +08:00
MikeSha
43e73313e6 implement wrapperClasses(#325) 2018-05-12 13:40:24 +08:00
Allen
888aa1d08b fix #303 (#330) 2018-05-10 14:07:24 +08:00
AllenFang
028834da8b add release command 2018-05-08 22:33:46 +08:00
AllenFang
8f3b989b00 Publish
- react-bootstrap-table2-editor@0.2.1
2018-05-08 22:23:23 +08:00
AllenFang
fe8761427d Publish
- react-bootstrap-table2-paginator@0.1.3
2018-05-08 22:22:47 +08:00
AllenFang
27a09de008 Publish
- react-bootstrap-table-next@0.1.10
2018-05-08 22:22:10 +08:00
AllenFang
20ba8cc24e Publish
- react-bootstrap-table2-editor@0.2.0
 - react-bootstrap-table2-example@0.1.7
 - react-bootstrap-table2-paginator@0.1.2
 - react-bootstrap-table-next@0.1.9
2018-05-06 16:59:28 +08:00
Allen
b8b52e7fc0 20180507 release #326
20180507 release
2018-05-06 16:57:29 +08:00
Allen
05a8c3be5f fix #309 (#324) 2018-05-06 15:50:18 +08:00
Allen
2f9bedbeeb Merge pull request #323 from react-bootstrap-table/feat/pagination-total
Implement pagination total
2018-05-06 15:16:49 +08:00
AllenFang
01be6fc275 example for #238 2018-05-06 15:08:43 +08:00
AllenFang
c20a4bb220 fix #238 2018-05-06 15:08:20 +08:00
Allen
ed21b3cb65 Merge pull request #322 from react-bootstrap-table/feat/rich-editor
Rich Editors
2018-05-06 14:05:08 +08:00
AllenFang
f2a44c976d patch docs 2018-05-06 13:50:59 +08:00
AllenFang
ca5189d8ad patch tests for rich editors 2018-05-05 18:18:52 +08:00
AllenFang
03f51c36ac add example for rich cell editor 2018-05-05 18:18:52 +08:00
AllenFang
607202b4e9 implement rich cell editor 2018-05-05 18:18:52 +08:00
39 changed files with 1318 additions and 52 deletions

View File

@@ -17,6 +17,7 @@
* [condensed](#condensed) * [condensed](#condensed)
* [id](#id) * [id](#id)
* [classes](#classes) * [classes](#classes)
* [wrapperClasses](#wrapperClasses)
* [cellEdit](#cellEdit) * [cellEdit](#cellEdit)
* [selectRow](#selectRow) * [selectRow](#selectRow)
* [rowStyle](#rowStyle) * [rowStyle](#rowStyle)
@@ -107,6 +108,9 @@ Same as bootstrap `.table-condensed` class for making a table more compact by cu
Customize id on `table` element. Customize id on `table` element.
### <a name='classes'>classes - [String]</a> ### <a name='classes'>classes - [String]</a>
Customize class on `table` element. Customize class on `table` element.
### <a name='wrapperClasses'>wrapperClasses - [String]</a>
Customize class on the outer element which wrap up the `table` element.
### <a name='cellEdit'>cellEdit - [Object]</a> ### <a name='cellEdit'>cellEdit - [Object]</a>
Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail. Makes table cells editable, please see [cellEdit definition](./cell-edit.md) for more detail.

View File

@@ -34,6 +34,8 @@ Available properties in a column object:
* [editCellClasses](#editCellClasses) * [editCellClasses](#editCellClasses)
* [editorStyle](#editorStyle) * [editorStyle](#editorStyle)
* [editorClasses](#editorClasses) * [editorClasses](#editorClasses)
* [editor](#editor)
* [editorRenderer](#editorRenderer)
* [filter](#filter) * [filter](#filter)
* [filterValue](#filterValue) * [filterValue](#filterValue)
@@ -560,6 +562,87 @@ This is almost same as [`column.editCellStyle`](#editCellStyle), but `column.edi
## <a name='editorClasses'>column.editorClasses - [Object | Function]</a> ## <a name='editorClasses'>column.editorClasses - [Object | Function]</a>
This is almost same as [`column.editCellClasses`](#editCellClasses), but `column.editorClasses` is custom the class on editor instead of cell(`td`). This is almost same as [`column.editCellClasses`](#editCellClasses), but `column.editorClasses` is custom the class on editor instead of cell(`td`).
## <a name='editor'>column.editor - [Object]</a>
`column.editor` allow you to custom the type of cell editor by following predefined type:
* Text(Default)
* Dropdown
* Date
* Textarea
* Checkbox
Following is a quite example:
```js
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [
//...
, {
dataField: 'done',
text: 'Done',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}
];
```
If you want more information, please check [here](https://github.com/react-bootstrap-table/react-bootstrap-table2/tree/master/packages/react-bootstrap-table2-editor).
## <a name='editorRenderer'>column.editorRenderer - [Function]</a>
If you feel above predefined editors are not satisfied to your requirement, you can totally custom the editor via `column.editorRenderer`:
```js
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
// Custom Editor
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [
//...
, {
dataField: 'done',
text: 'Done',
editorRenderer: (editorProps, value, row, column, rowIndex, columnIndex) =>
<QualityRanger { ...editorProps } value={ value } />;
}
];
```
## <a name='filter'>column.filter - [Object]</a> ## <a name='filter'>column.filter - [Object]</a>
Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters: Configure `column.filter` will able to setup a column level filter on the header column. Currently, `react-bootstrap-table2` support following filters:

View File

@@ -14,7 +14,8 @@
"test:watch": "jest --watch", "test:watch": "jest --watch",
"storybook": "cd ./packages/react-bootstrap-table2-example && yarn storybook", "storybook": "cd ./packages/react-bootstrap-table2-example && yarn storybook",
"gh-pages:clean": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:clean", "gh-pages:clean": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:clean",
"gh-pages:build": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:build" "gh-pages:build": "cd ./packages/react-bootstrap-table2-example && yarn gh-pages:build",
"release": "yarn install && yarn build && lerna publish"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -85,7 +86,8 @@
}, },
"jest": { "jest": {
"collectCoverageFrom": [ "collectCoverageFrom": [
"packages/**/*.js" "packages/*/src/*.js",
"packages/*/index.js"
], ],
"roots": [ "roots": [
"<rootDir>/packages" "<rootDir>/packages"

View File

@@ -48,6 +48,9 @@ How user save their new editings? We offer two ways:
* Column Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as bool value) * Column Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as bool value)
* Cell Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as a callback function) * Cell Level (Configure [column.editable](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditable-bool-function) as a callback function)
## Validation
[column.validator](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnvalidator-function) will help you to work on it!
## Customize Style/Class ## Customize Style/Class
### Editing Cell ### Editing Cell
@@ -58,6 +61,169 @@ How user save their new editings? We offer two ways:
* Customize the editor style via [column.editorStyle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorstyle-object-function) * Customize the editor style via [column.editorStyle](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorstyle-object-function)
* Customize the editor classname via [column.editoClasses](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorclasses-string-function) * Customize the editor classname via [column.editoClasses](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorclasses-string-function)
## Validation ## Rich Editors
`react-bootstrap-table2` have following predefined editor:
[`column.validator`](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columnvalidator-function) will help you to work on it! * Text(Default)
* Dropdown
* Date
* Textarea
* Checkbox
In a nutshell, you just only give a [column.editor](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditor-object) and define the `type`:
```js
import { Type } from 'react-bootstrap-table2-editor';
const columns = [
..., {
dataField: 'done',
text: 'Done',
editor: {
type: Type.SELECT | Type.TEXTAREA | Type.CHECKBOX | Type.DATE,
... // The rest properties will be rendered into the editor's DOM element
}
}
]
```
In the following, we go though all the predefined editors:
### Dropdown Editor
Dropdown editor give a select menu to choose a data from a list, the `editor.options` is required property for dropdown editor.
```js
import { Type } from 'react-bootstrap-table2-editor';
const columns = [
..., {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.SELECT,
options: [{
value: 'A',
label: 'A'
}, {
value: 'B',
label: 'B'
}, {
value: 'C',
label: 'C'
}, {
value: 'D',
label: 'D'
}, {
value: 'E',
label: 'E'
}]
}
}];
```
### Date Editor
Date editor is use `<input type="date">`, the configuration is very simple:
```js
const columns = [
..., {
dataField: 'inStockDate',
text: 'Stock Date',
formatter: (cell) => {
let dateObj = cell;
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`;
},
editor: {
type: Type.DATE
}
}];
```
### Textarea Editor
Textarea editor is use `<input type="textarea">`, user can press `ENTER` to change line and in the `react-bootstrap-table2`, user allow to save result via press `SHIFT` + `ENTER`.
```js
const columns = [
..., {
dataField: 'comment',
text: 'Product Comments',
editor: {
type: Type.TEXTAREA
}
}];
```
### Checkbox Editor
Checkbox editor allow you to have a pair value choice, the `editor.value` is required value to represent the actual value for check and uncheck.
```js
const columns = [
..., {
dataField: 'comment',
text: 'Product Comments',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}];
```
## Customize Editor
If you feel above predefined editors are not satisfied to your requirement, you can certainly custom the editor via [column.editorRenderer](https://react-bootstrap-table.github.io/react-bootstrap-table2/docs/column-props.html#columneditorrenderer-function). It accept a function and pass following arguments when function called:
* `editorProps`: Some useful attributes you can use on DOM editor, like class, style etc.
* `value`: Current cell value
* `row`: Current row data
* `column`: Current column definition
* `rowIndex`: Current row index
* `columnIndex`: Current column index
> Note when implement a custom React editor component, this component should have a **getValue** function which return current value on editor
> Note when you want to save value, you can call **editorProps.onUpdate** function
Following is a short example:
```js
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [
..., {
dataField: 'quality',
text: 'Product Quality',
editorRenderer: (editorProps, value, row, column, rowIndex, columnIndex) => (
<QualityRanger { ...editorProps } value={ value } />
)
}];
```

View File

@@ -1,6 +1,7 @@
import wrapperFactory from './src/wrapper'; import wrapperFactory from './src/wrapper';
import editingCellFactory from './src/editing-cell'; import editingCellFactory from './src/editing-cell';
import { import {
EDITTYPE,
CLICK_TO_CELL_EDIT, CLICK_TO_CELL_EDIT,
DBCLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT,
DELAY_FOR_DBCLICK DELAY_FOR_DBCLICK
@@ -14,3 +15,5 @@ export default (options = {}) => ({
DELAY_FOR_DBCLICK, DELAY_FOR_DBCLICK,
options options
}); });
export const Type = EDITTYPE;

View File

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

View File

@@ -0,0 +1,61 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class CheckBoxEditor extends Component {
constructor(props) {
super(props);
this.state = {
checked: props.defaultValue.toString() === props.value.split(':')[0]
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
this.checkbox.focus();
}
getValue() {
const [positive, negative] = this.props.value.split(':');
return this.checkbox.checked ? positive : negative;
}
handleChange(e) {
if (this.props.onChange) this.props.onChange(e);
const { target } = e;
this.setState(() => ({ checked: target.checked }));
}
render() {
const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('editor edit-chseckbox checkbox', className);
return (
<input
ref={ node => this.checkbox = node }
type="checkbox"
className={ editorClass }
{ ...rest }
checked={ this.state.checked }
onChange={ this.handleChange }
/>
);
}
}
CheckBoxEditor.propTypes = {
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
]),
value: PropTypes.string,
defaultValue: PropTypes.any,
onChange: PropTypes.func
};
CheckBoxEditor.defaultProps = {
className: '',
value: 'on:off',
defaultValue: false,
onChange: undefined
};
export default CheckBoxEditor;

View File

@@ -2,3 +2,11 @@ export const TIME_TO_CLOSE_MESSAGE = 3000;
export const DELAY_FOR_DBCLICK = 200; export const DELAY_FOR_DBCLICK = 200;
export const CLICK_TO_CELL_EDIT = 'click'; export const CLICK_TO_CELL_EDIT = 'click';
export const DBCLICK_TO_CELL_EDIT = 'dbclick'; export const DBCLICK_TO_CELL_EDIT = 'dbclick';
export const EDITTYPE = {
TEXT: 'text',
SELECT: 'select',
TEXTAREA: 'textarea',
CHECKBOX: 'checkbox',
DATE: 'date'
};

View File

@@ -0,0 +1,42 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class DateEditor extends Component {
componentDidMount() {
const { defaultValue } = this.props;
this.date.valueAsDate = new Date(defaultValue);
this.date.focus();
}
getValue() {
return this.date.value;
}
render() {
const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('form-control editor edit-date', className);
return (
<input
ref={ node => this.date = node }
type="date"
className={ editorClass }
{ ...rest }
/>
);
}
}
DateEditor.propTypes = {
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
]),
defaultValue: PropTypes.string
};
DateEditor.defaultProps = {
className: '',
defaultValue: ''
};
export default DateEditor;

View File

@@ -0,0 +1,61 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class DropDownEditor extends Component {
componentDidMount() {
const { defaultValue } = this.props;
this.select.value = defaultValue;
this.select.focus();
}
getValue() {
return this.select.value;
}
render() {
const { defaultValue, className, options, ...rest } = this.props;
const editorClass = cs('form-control editor edit-select', className);
const attr = {
...rest,
className: editorClass
};
return (
<select
{ ...attr }
ref={ node => this.select = node }
defaultValue={ defaultValue }
>
{
options.map(({ label, value }) => (
<option key={ value } value={ value }>{ label }</option>
))
}
</select>
);
}
}
DropDownEditor.propTypes = {
defaultValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
className: PropTypes.string,
style: PropTypes.object,
options: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
value: PropTypes.any
}))
]).isRequired
};
DropDownEditor.defaultProps = {
className: '',
defaultValue: '',
style: {}
};
export default DropDownEditor;

View File

@@ -6,9 +6,13 @@ import React, { Component } from 'react';
import cs from 'classnames'; import cs from 'classnames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import DropdownEditor from './dropdown-editor';
import TextAreaEditor from './textarea-editor';
import CheckBoxEditor from './checkbox-editor';
import DateEditor from './date-editor';
import TextEditor from './text-editor'; import TextEditor from './text-editor';
import EditorIndicator from './editor-indicator'; import EditorIndicator from './editor-indicator';
import { TIME_TO_CLOSE_MESSAGE } from './const'; import { TIME_TO_CLOSE_MESSAGE, EDITTYPE } from './const';
export default _ => export default _ =>
class EditingCell extends Component { class EditingCell extends Component {
@@ -73,8 +77,8 @@ export default _ =>
}, timeToCloseMessage); }, timeToCloseMessage);
} }
beforeComplete(row, column, newValue) { beforeComplete(newValue) {
const { onUpdate } = this.props; const { onUpdate, row, column } = this.props;
if (_.isFunction(column.validator)) { if (_.isFunction(column.validator)) {
const validateForm = column.validator(newValue, row, column); const validateForm = column.validator(newValue, row, column);
if (_.isObject(validateForm) && !validateForm.valid) { if (_.isObject(validateForm) && !validateForm.valid) {
@@ -89,28 +93,20 @@ export default _ =>
} }
handleBlur() { handleBlur() {
const { onEscape, blurToSave, row, column } = this.props; const { onEscape, blurToSave } = this.props;
if (blurToSave) { if (blurToSave) {
const value = this.editor.text.value; this.beforeComplete(this.editor.getValue());
if (!_.isDefined(value)) {
// TODO: for other custom or embed editor
}
this.beforeComplete(row, column, value);
} else { } else {
onEscape(); onEscape();
} }
} }
handleKeyDown(e) { handleKeyDown(e) {
const { onEscape, row, column } = this.props; const { onEscape } = this.props;
if (e.keyCode === 27) { // ESC if (e.keyCode === 27) { // ESC
onEscape(); onEscape();
} else if (e.keyCode === 13) { // ENTER } else if (e.keyCode === 13) { // ENTER
const value = e.currentTarget.value; this.beforeComplete(this.editor.getValue());
if (!_.isDefined(value)) {
// TODO: for other custom or embed editor
}
this.beforeComplete(row, column, value);
} }
} }
@@ -124,17 +120,13 @@ export default _ =>
} }
render() { render() {
const { invalidMessage } = this.state; let editor;
const { row, column, className, style, rowIndex, columnIndex } = this.props; const { row, column, className, style, rowIndex, columnIndex } = this.props;
const { dataField } = column; const { dataField } = column;
const value = _.get(row, dataField); const value = _.get(row, dataField);
const editorAttrs = { const hasError = _.isDefined(this.state.invalidMessage);
onKeyDown: this.handleKeyDown,
onBlur: this.handleBlur
};
const hasError = _.isDefined(invalidMessage);
let customEditorClass = column.editorClasses || ''; let customEditorClass = column.editorClasses || '';
if (_.isFunction(column.editorClasses)) { if (_.isFunction(column.editorClasses)) {
customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex); customEditorClass = column.editorClasses(value, row, rowIndex, columnIndex);
@@ -150,20 +142,51 @@ export default _ =>
shake: hasError shake: hasError
}, customEditorClass); }, customEditorClass);
let editorProps = {
ref: node => this.editor = node,
defaultValue: value,
style: editorStyle,
className: editorClass,
onKeyDown: this.handleKeyDown,
onBlur: this.handleBlur
};
const isDefaultEditorDefined = _.isObject(column.editor);
if (isDefaultEditorDefined) {
editorProps = {
...editorProps,
...column.editor
};
} else if (_.isFunction(column.editorRenderer)) {
editorProps = {
...editorProps,
onUpdate: this.beforeComplete
};
}
if (_.isFunction(column.editorRenderer)) {
editor = column.editorRenderer(editorProps, value, row, column, rowIndex, columnIndex);
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.SELECT) {
editor = <DropdownEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.TEXTAREA) {
editor = <TextAreaEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.CHECKBOX) {
editor = <CheckBoxEditor { ...editorProps } />;
} else if (isDefaultEditorDefined && column.editor.type === EDITTYPE.DATE) {
editor = <DateEditor { ...editorProps } />;
} else {
editor = <TextEditor { ...editorProps } />;
}
return ( return (
<td <td
className={ cs('react-bootstrap-table-editing-cell', className) } className={ cs('react-bootstrap-table-editing-cell', className) }
style={ style } style={ style }
onClick={ this.handleClick } onClick={ this.handleClick }
> >
<TextEditor { editor }
ref={ node => this.editor = node } { hasError ? <EditorIndicator invalidMessage={ this.state.invalidMessage } /> : null }
defaultValue={ value }
style={ editorStyle }
className={ editorClass }
{ ...editorAttrs }
/>
{ hasError ? <EditorIndicator invalidMessage={ invalidMessage } /> : null }
</td> </td>
); );
} }

View File

@@ -10,6 +10,10 @@ class TextEditor extends Component {
this.text.focus(); this.text.focus();
} }
getValue() {
return this.text.value;
}
render() { render() {
const { defaultValue, className, ...rest } = this.props; const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('form-control editor edit-text', className); const editorClass = cs('form-control editor edit-text', className);

View File

@@ -0,0 +1,60 @@
/* eslint no-return-assign: 0 */
import React, { Component } from 'react';
import cs from 'classnames';
import PropTypes from 'prop-types';
class TextAreaEditor extends Component {
constructor(props) {
super(props);
this.handleKeyDown = this.handleKeyDown.bind(this);
}
componentDidMount() {
const { defaultValue } = this.props;
this.text.value = defaultValue;
this.text.focus();
}
getValue() {
return this.text.value;
}
handleKeyDown(e) {
if (e.keyCode === 13 && !e.shiftKey) return;
if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}
}
render() {
const { defaultValue, className, ...rest } = this.props;
const editorClass = cs('form-control editor edit-textarea', className);
return (
<textarea
ref={ node => this.text = node }
type="textarea"
className={ editorClass }
{ ...rest }
onKeyDown={ this.handleKeyDown }
/>
);
}
}
TextAreaEditor.propTypes = {
className: PropTypes.oneOfType([
PropTypes.string,
PropTypes.object
]),
defaultValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
onKeyDown: PropTypes.func
};
TextAreaEditor.defaultProps = {
className: '',
defaultValue: '',
onKeyDown: undefined
};
export default TextAreaEditor;

View File

@@ -6,7 +6,12 @@ import { shallow, mount } from 'enzyme';
import _ from 'react-bootstrap-table-next/src/utils'; import _ from 'react-bootstrap-table-next/src/utils';
import editingCellFactory from '../src/editing-cell'; import editingCellFactory from '../src/editing-cell';
import * as constants from '../src/const';
import TextEditor from '../src/text-editor'; import TextEditor from '../src/text-editor';
import DateEditor from '../src/date-editor';
import DropDownEditor from '../src/dropdown-editor';
import TextAreaEditor from '../src/textarea-editor';
import CheckBoxEditor from '../src/checkbox-editor';
import EditorIndicator from '../src/editor-indicator'; import EditorIndicator from '../src/editor-indicator';
const EditingCell = editingCellFactory(_); const EditingCell = editingCellFactory(_);
@@ -39,7 +44,7 @@ describe('EditingCell', () => {
beforeEach(() => { beforeEach(() => {
onEscape = sinon.stub(); onEscape = sinon.stub();
onUpdate = sinon.stub(); onUpdate = sinon.stub();
wrapper = shallow( wrapper = mount(
<EditingCell <EditingCell
row={ row } row={ row }
rowIndex={ rowIndex } rowIndex={ rowIndex }
@@ -74,7 +79,8 @@ describe('EditingCell', () => {
it('when press ENTER on TextEditor should call onUpdate correctly', () => { it('when press ENTER on TextEditor should call onUpdate correctly', () => {
const newValue = 'test'; const newValue = 'test';
const textEditor = wrapper.find(TextEditor); const textEditor = wrapper.find(TextEditor);
textEditor.simulate('keyDown', { keyCode: 13, currentTarget: { value: newValue } }); sinon.stub(textEditor.instance(), 'getValue').returns(newValue);
textEditor.simulate('keyDown', { keyCode: 13 });
expect(onUpdate.callCount).toBe(1); expect(onUpdate.callCount).toBe(1);
expect(onUpdate.calledWith(row, column, newValue)).toBe(true); expect(onUpdate.calledWith(row, column, newValue)).toBe(true);
}); });
@@ -311,7 +317,7 @@ describe('EditingCell', () => {
onEscape={ onEscape } onEscape={ onEscape }
/> />
); );
wrapper.instance().beforeComplete(row, column, newValue); wrapper.instance().beforeComplete(newValue);
}); });
it('should call column.validator successfully', () => { it('should call column.validator successfully', () => {
@@ -357,7 +363,17 @@ describe('EditingCell', () => {
text: 'ID', text: 'ID',
validator: validatorCallBack validator: validatorCallBack
}; };
wrapper.instance().beforeComplete(row, column, newValue); wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
wrapper.instance().beforeComplete(newValue);
}); });
it('should call column.validator successfully', () => { it('should call column.validator successfully', () => {
@@ -370,4 +386,156 @@ describe('EditingCell', () => {
}); });
}); });
}); });
describe('if column.editorRenderer is defined', () => {
const TestEditor = () => <input type="text" />;
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editorRenderer: sinon.stub().returns(<TestEditor />)
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should call column.editorRenderer correctly', () => {
expect(column.editorRenderer.callCount).toBe(1);
});
it('should render correctly', () => {
expect(wrapper.find(TestEditor)).toHaveLength(1);
});
});
describe('if column.editor is select', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.SELECT,
options: [{
value: 1,
label: 'A'
}]
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render dropdown editor successfully', () => {
const editor = wrapper.find(DropDownEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
expect(editor.props().options).toEqual(column.editor.options);
});
});
describe('if column.editor is textarea', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.TEXTAREA
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render textarea editor successfully', () => {
const editor = wrapper.find(TextAreaEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
});
});
describe('if column.editor is checkbox', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.CHECKBOX
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render checkbox editor successfully', () => {
const editor = wrapper.find(CheckBoxEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
});
});
describe('if column.editor is date', () => {
beforeEach(() => {
column = {
dataField: 'id',
text: 'ID',
editor: {
type: constants.EDITTYPE.DATE
}
};
wrapper = mount(
<EditingCell
row={ row }
rowIndex={ rowIndex }
columnIndex={ columnIndex }
column={ column }
onUpdate={ onUpdate }
onEscape={ onEscape }
/>
);
});
it('should render date editor successfully', () => {
const editor = wrapper.find(DateEditor);
expect(wrapper.length).toBe(1);
expect(editor.length).toBe(1);
});
});
}); });

View File

@@ -33,6 +33,7 @@ const columns = [{
<BootstrapTable id="bar" keyField='id' data={ products } columns={ columns } /> <BootstrapTable id="bar" keyField='id' data={ products } columns={ columns } />
<BootstrapTable classes="foo" keyField='id' data={ products } columns={ columns } /> <BootstrapTable classes="foo" keyField='id' data={ products } columns={ columns } />
<BootstrapTable wrapperClasses="boo" keyField="id" data={ products } columns={ columns } />
`; `;
export default () => ( export default () => (
@@ -43,6 +44,9 @@ export default () => (
<h4> Customized table className </h4> <h4> Customized table className </h4>
<BootstrapTable classes="foo" keyField="id" data={ products } columns={ columns } /> <BootstrapTable classes="foo" keyField="id" data={ products } columns={ columns } />
<h4> Customized wrapper className </h4>
<BootstrapTable wrapperClasses="boo" keyField="id" data={ products } columns={ columns } />
<Code>{ sourceCode }</Code> <Code>{ sourceCode }</Code>
</div> </div>
); );

View File

@@ -0,0 +1,64 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { todosGenerator } from 'utils/common';
const todos = todosGenerator();
const columns = [{
dataField: 'id',
text: 'Todo ID'
}, {
dataField: 'todo',
text: 'Todo Name'
}, {
dataField: 'done',
text: 'Done',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Todo ID'
}, {
dataField: 'todo',
text: 'Todo Name'
}, {
dataField: 'done',
text: 'Done',
editor: {
type: Type.CHECKBOX,
value: 'Y:N'
}
}];
<BootstrapTable
keyField="id"
data={ todos }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ todos }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,130 @@
/* eslint react/prefer-stateless-function: 0 */
/* eslint no-return-assign: 0 */
/* eslint no-unused-vars: 0 */
import React from 'react';
import PropTypes from 'prop-types';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { productsQualityGenerator } from 'utils/common';
const products = productsQualityGenerator();
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
editorRenderer: (editorProps, value, row, column, rowIndex, columnIndex) => (
<QualityRanger { ...editorProps } value={ value } />
)
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory from 'react-bootstrap-table2-editor';
class QualityRanger extends React.Component {
static propTypes = {
value: PropTypes.number,
onUpdate: PropTypes.func.isRequired
}
static defaultProps = {
value: 0
}
getValue() {
return parseInt(this.range.value, 10);
}
render() {
const { value, onUpdate, ...rest } = this.props;
return [
<input
{ ...rest }
key="range"
ref={ node => this.range = node }
type="range"
min="0"
max="100"
/>,
<button
key="submit"
className="btn btn-default"
onClick={ () => onUpdate(this.getValue()) }
>
done
</button>
];
}
}
const columns = [{
dataField: 'id',
text: 'Product ID'
}, {
dataField: 'name',
text: 'Product Name'
}, {
dataField: 'quality',
text: 'Product Quality',
editorRenderer: (editorProps, value, row, rowIndex, columnIndex) => (
<QualityRanger { ...editorProps } value={ value } />
)
}];
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ products }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,77 @@
/* eslint prefer-template: 0 */
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { stockGenerator } from 'utils/common';
const stocks = stockGenerator();
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'inStockDate',
text: 'Stock Date',
formatter: (cell) => {
let dateObj = cell;
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return `${('0' + dateObj.getDate()).slice(-2)}/${('0' + (dateObj.getMonth() + 1)).slice(-2)}/${dateObj.getFullYear()}`;
},
editor: {
type: Type.DATE
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'ID'
}, {
dataField: 'name',
text: 'Name'
}, {
dataField: 'inStockDate',
text: 'Stock Date',
formatter: (cell) => {
let dateObj = cell;
if (typeof cell !== 'object') {
dateObj = new Date(cell);
}
return \`$\{('0' + dateObj.getDate()).slice(-2)}/$\{('0' + (dateObj.getMonth() + 1)).slice(-2)}/$\{dateObj.getFullYear()}\`;
},
editor: {
type: Type.DATE
}
}];
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ stocks }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,100 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { jobsGenerator } from 'utils/common';
const jobs = jobsGenerator();
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.SELECT,
options: [{
value: 'A',
label: 'A'
}, {
value: 'B',
label: 'B'
}, {
value: 'C',
label: 'C'
}, {
value: 'D',
label: 'D'
}, {
value: 'E',
label: 'E'
}]
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.SELECT,
options: [{
value: 'A',
label: 'A'
}, {
value: 'B',
label: 'B'
}, {
value: 'C',
label: 'C'
}, {
value: 'D',
label: 'D'
}, {
value: 'E',
label: 'E'
}]
}
}];
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -0,0 +1,68 @@
/* eslint react/prefer-stateless-function: 0 */
import React from 'react';
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
import Code from 'components/common/code-block';
import { jobsGenerator } from 'utils/common';
const jobs = jobsGenerator();
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.TEXTAREA
}
}];
const sourceCode = `\
import BootstrapTable from 'react-bootstrap-table-next';
import cellEditFactory, { Type } from 'react-bootstrap-table2-editor';
const columns = [{
dataField: 'id',
text: 'Job ID'
}, {
dataField: 'name',
text: 'Job Name'
}, {
dataField: 'owner',
text: 'Job Owner'
}, {
dataField: 'type',
text: 'Job Type',
editor: {
type: Type.TEXTAREA
}
}];
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
`;
export default () => (
<div>
<h3>Dropdown Editor</h3>
<BootstrapTable
keyField="id"
data={ jobs }
columns={ columns }
cellEdit={ cellEditFactory({ mode: 'click', blurToSave: true }) }
/>
<Code>{ sourceCode }</Code>
</div>
);

View File

@@ -65,6 +65,7 @@ const options = {
prePageTitle: 'Pre page', prePageTitle: 'Pre page',
firstPageTitle: 'Next page', firstPageTitle: 'Next page',
lastPageTitle: 'Last page', lastPageTitle: 'Last page',
showTotal: true,
sizePerPageList: [{ sizePerPageList: [{
text: '5', value: 5 text: '5', value: 5
}, { }, {

View File

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

View File

@@ -1,3 +1,5 @@
/* eslint no-mixed-operators: 0 */
/** /**
* products generator for stories * products generator for stories
* *
@@ -27,12 +29,34 @@ export const productsQualityGenerator = (quantity = 5) =>
quality: index % 3 quality: index % 3
})); }));
const jobType = ['A', 'B', 'C', 'D', 'E'];
const jobOwner = ['Allen', 'Bob', 'Cindy'];
export const jobsGenerator = (quantity = 5) => export const jobsGenerator = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({ Array.from({ length: quantity }, (value, index) => ({
id: index, id: index,
name: `Job name ${index}`, name: `Job name ${index}`,
owner: Math.floor(Math.random() * 3), owner: jobOwner[Math.floor((Math.random() * 2) + 1)],
type: Math.floor(Math.random() * 5) type: jobType[Math.floor((Math.random() * 4) + 1)]
}));
export const todosGenerator = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({
id: index,
todo: `Todo item ${index}`,
done: Math.random() > 0.4 ? 'Y' : 'N'
}));
const startDate = new Date(2017, 0, 1);
const endDate = new Date();
export const stockGenerator = (quantity = 5) =>
Array.from({ length: quantity }, (value, index) => ({
id: index,
name: `Todo item ${index}`,
inStockDate:
new Date(startDate.getTime() + Math.random() * (endDate.getTime() - startDate.getTime()))
})); }));
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

View File

@@ -79,6 +79,11 @@ import CellEditStyleTable from 'examples/cell-edit/cell-edit-style-table';
import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table'; import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
import EditorStyleTable from 'examples/cell-edit/editor-style-table'; import EditorStyleTable from 'examples/cell-edit/editor-style-table';
import EditorClassTable from 'examples/cell-edit/editor-class-table'; import EditorClassTable from 'examples/cell-edit/editor-class-table';
import DropdownEditorTable from 'examples/cell-edit/dropdown-editor-table';
import TextareaEditorTable from 'examples/cell-edit/textarea-editor-table';
import CheckboxEditorTable from 'examples/cell-edit/checkbox-editor-table';
import DateEditorTable from 'examples/cell-edit/date-editor-table';
import CustomEditorTable from 'examples/cell-edit/custom-editor-table';
// work on row selection // work on row selection
import SingleSelectionTable from 'examples/row-selection/single-selection'; import SingleSelectionTable from 'examples/row-selection/single-selection';
@@ -200,7 +205,12 @@ storiesOf('Cell Editing', module)
.add('Custom Cell Style', () => <CellEditStyleTable />) .add('Custom Cell Style', () => <CellEditStyleTable />)
.add('Custom Cell Classes', () => <CellEditClassTable />) .add('Custom Cell Classes', () => <CellEditClassTable />)
.add('Custom Editor Classes', () => <EditorClassTable />) .add('Custom Editor Classes', () => <EditorClassTable />)
.add('Custom Editor Style', () => <EditorStyleTable />); .add('Custom Editor Style', () => <EditorStyleTable />)
.add('Dropdown Editor', () => <DropdownEditorTable />)
.add('Textarea Editor', () => <TextareaEditorTable />)
.add('Checkbox Editor', () => <CheckboxEditorTable />)
.add('Date Editor', () => <DateEditorTable />)
.add('Custom Editor', () => <CustomEditorTable />);
storiesOf('Row Selection', module) storiesOf('Row Selection', module)
.add('Single Selection', () => <SingleSelectionTable />) .add('Single Selection', () => <SingleSelectionTable />)

View File

@@ -5,3 +5,7 @@ table.foo {
table#bar { table#bar {
background-color: $light-blue; background-color: $light-blue;
} }
div.boo {
border: 2px solid salmon;
}

View File

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

View File

@@ -30,13 +30,11 @@ export default (Base, {
// I think this condition only isRemoteFilter is enough // I think this condition only isRemoteFilter is enough
store.filteredData = store.getAllData(); store.filteredData = store.getAllData();
this.setState(() => ({ isDataChanged: true, currFilters: store.filters })); this.setState(() => ({ isDataChanged: true, currFilters: store.filters }));
} else if (isDataChanged) { } else {
if (!isRemoteFilter && Object.keys(this.state.currFilters).length > 0) { if (Object.keys(this.state.currFilters).length > 0) {
store.filteredData = filters(store, columns, _)(this.state.currFilters); store.filteredData = filters(store, columns, _)(this.state.currFilters);
} }
this.setState(() => ({ isDataChanged })); this.setState(() => ({ isDataChanged }));
} else {
this.setState(() => ({ isDataChanged: false }));
} }
} }
@@ -50,7 +48,8 @@ export default (Base, {
onFilter(column, filterType) { onFilter(column, filterType) {
return (filterVal) => { return (filterVal) => {
const { store, columns } = this.props; const { store, columns } = this.props;
const currFilters = Object.assign({}, this.state.currFilters); // watch out here if migration to context API, #334
const currFilters = Object.assign({}, store.filters);
const { dataField, filter } = column; const { dataField, filter } = column;
if (!_.isDefined(filterVal) || filterVal === '') { if (!_.isDefined(filterVal) || filterVal === '') {

View File

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

View File

@@ -1,4 +1,6 @@
/* eslint no-mixed-operators: 0 */ /* eslint no-mixed-operators: 0 */
import Const from './const';
export default ExtendBase => export default ExtendBase =>
class PageResolver extends ExtendBase { class PageResolver extends ExtendBase {
backToPrevPage() { backToPrevPage() {
@@ -27,6 +29,23 @@ export default ExtendBase =>
return pageStartIndex + totalPages - 1; return pageStartIndex + totalPages - 1;
} }
calculateFromTo() {
const {
dataSize,
currPage,
currSizePerPage,
pageStartIndex
} = this.props;
const offset = Math.abs(Const.PAGE_START_INDEX - pageStartIndex);
let from = ((currPage - pageStartIndex) * currSizePerPage);
from = dataSize === 0 ? 0 : from + 1;
let to = Math.min((currSizePerPage * (currPage + offset) - 1), dataSize);
if (to >= dataSize) to -= 1;
return [from, to];
}
calculatePages( calculatePages(
totalPages = this.state.totalPages, totalPages = this.state.totalPages,
lastPage = this.state.lastPage) { lastPage = this.state.lastPage) {

View File

@@ -0,0 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
const PaginationTotal = props => (
<span>
&nbsp;Showing rows { props.from } to&nbsp;{ props.to + 1 } of&nbsp;{ props.dataSize }
</span>
);
PaginationTotal.propTypes = {
from: PropTypes.number.isRequired,
to: PropTypes.number.isRequired,
dataSize: PropTypes.number.isRequired
};
export default PaginationTotal;

View File

@@ -6,6 +6,7 @@ import PropTypes from 'prop-types';
import pageResolver from './page-resolver'; import pageResolver from './page-resolver';
import SizePerPageDropDown from './size-per-page-dropdown'; import SizePerPageDropDown from './size-per-page-dropdown';
import PaginationList from './pagination-list'; import PaginationList from './pagination-list';
import PaginationTotal from './pagination-total';
import Const from './const'; import Const from './const';
class Pagination extends pageResolver(Component) { class Pagination extends pageResolver(Component) {
@@ -89,13 +90,14 @@ class Pagination extends pageResolver(Component) {
render() { render() {
const { totalPages, lastPage, dropdownOpen: open } = this.state; const { totalPages, lastPage, dropdownOpen: open } = this.state;
const { const {
showTotal,
sizePerPageList, sizePerPageList,
currSizePerPage, currSizePerPage,
hideSizePerPage, hideSizePerPage,
hidePageListOnlyOnePage hidePageListOnlyOnePage
} = this.props; } = this.props;
const pages = this.calculatePageStatus(this.calculatePages(totalPages), lastPage); const pages = this.calculatePageStatus(this.calculatePages(totalPages), lastPage);
const [from, to] = this.calculateFromTo();
const pageListClass = cs( const pageListClass = cs(
'react-bootstrap-table-pagination-list', 'react-bootstrap-table-pagination-list',
'col-md-6 col-xs-6 col-sm-6 col-lg-6', { 'col-md-6 col-xs-6 col-sm-6 col-lg-6', {
@@ -117,6 +119,14 @@ class Pagination extends pageResolver(Component) {
/> />
) : null ) : null
} }
{
showTotal ?
<PaginationTotal
from={ from }
to={ to }
dataSize={ this.props.dataSize }
/> : null
}
</div> </div>
<div className={ pageListClass }> <div className={ pageListClass }>
<PaginationList pages={ pages } onPageChange={ this.handleChangePage } /> <PaginationList pages={ pages } onPageChange={ this.handleChangePage } />

View File

@@ -145,6 +145,7 @@ export default (Base, {
alwaysShowAllBtns={ alwaysShowAllBtns } alwaysShowAllBtns={ alwaysShowAllBtns }
hideSizePerPage={ hideSizePerPage } hideSizePerPage={ hideSizePerPage }
hidePageListOnlyOnePage={ hidePageListOnlyOnePage } hidePageListOnlyOnePage={ hidePageListOnlyOnePage }
showTotal={ options.showTotal }
firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT } firstPageText={ options.firstPageText || Const.FIRST_PAGE_TEXT }
prePageText={ options.prePageText || Const.PRE_PAGE_TEXT } prePageText={ options.prePageText || Const.PRE_PAGE_TEXT }
nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT } nextPageText={ options.nextPageText || Const.NEXT_PAGE_TEXT }

View File

@@ -110,6 +110,19 @@ describe('PageResolver', () => {
}); });
}); });
describe('calculateFromTo', () => {
const props = createMockProps();
beforeEach(() => {
const mockElement = React.createElement(MockComponent, props, null);
wrapper = shallow(mockElement);
});
it('should return correct array with from and to value', () => {
const instance = wrapper.instance();
expect(instance.calculateFromTo()).toEqual([1, props.currSizePerPage - 1]);
});
});
describe('calculateTotalPage', () => { describe('calculateTotalPage', () => {
const props = createMockProps(); const props = createMockProps();

View File

@@ -111,6 +111,7 @@ describe('Wrapper', () => {
expect(pagination.prop('nextPageTitle')).toEqual(Const.NEXT_PAGE_TITLE); expect(pagination.prop('nextPageTitle')).toEqual(Const.NEXT_PAGE_TITLE);
expect(pagination.prop('lastPageTitle')).toEqual(Const.LAST_PAGE_TITLE); expect(pagination.prop('lastPageTitle')).toEqual(Const.LAST_PAGE_TITLE);
expect(pagination.prop('hideSizePerPage')).toEqual(Const.HIDE_SIZE_PER_PAGE); expect(pagination.prop('hideSizePerPage')).toEqual(Const.HIDE_SIZE_PER_PAGE);
expect(pagination.prop('showTotal')).toEqual(undefined);
}); });
describe('componentWillReceiveProps', () => { describe('componentWillReceiveProps', () => {
@@ -247,6 +248,20 @@ describe('Wrapper', () => {
}); });
}); });
describe('when options.showTotal is defined', () => {
const props = createTableProps({ options: { showTotal: true } });
beforeEach(() => {
createPaginationWrapper(props);
});
it('should rendering Pagination correctly', () => {
const pagination = wrapper.find(Pagination);
expect(wrapper.length).toBe(1);
expect(pagination.length).toBe(1);
expect(pagination.prop('showTotal')).toBeTruthy();
});
});
describe('when options.pageStartIndex is defined', () => { describe('when options.pageStartIndex is defined', () => {
const pageStartIndex = -1; const pageStartIndex = -1;
const props = createTableProps({ options: { pageStartIndex } }); const props = createTableProps({ options: { pageStartIndex } });

View File

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

View File

@@ -52,9 +52,12 @@ class BootstrapTable extends PropsBaseResolver(Component) {
caption, caption,
rowStyle, rowStyle,
rowClasses, rowClasses,
wrapperClasses,
rowEvents rowEvents
} = this.props; } = this.props;
const tableWrapperClass = cs('react-bootstrap-table', wrapperClasses);
const tableClass = cs('table', { const tableClass = cs('table', {
'table-striped': striped, 'table-striped': striped,
'table-hover': hover, 'table-hover': hover,
@@ -75,7 +78,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
const tableCaption = (caption && <Caption>{ caption }</Caption>); const tableCaption = (caption && <Caption>{ caption }</Caption>);
return ( return (
<div className="react-bootstrap-table"> <div className={ tableWrapperClass }>
<table id={ id } className={ tableClass }> <table id={ id } className={ tableClass }>
{ tableCaption } { tableCaption }
<Header <Header
@@ -120,6 +123,7 @@ BootstrapTable.propTypes = {
hover: PropTypes.bool, hover: PropTypes.bool,
id: PropTypes.string, id: PropTypes.string,
classes: PropTypes.string, classes: PropTypes.string,
wrapperClasses: PropTypes.string,
condensed: PropTypes.bool, condensed: PropTypes.bool,
caption: PropTypes.oneOfType([ caption: PropTypes.oneOfType([
PropTypes.node, PropTypes.node,

View File

@@ -127,11 +127,13 @@ HeaderCell.propTypes = {
sort: PropTypes.bool, sort: PropTypes.bool,
sortFunc: PropTypes.func, sortFunc: PropTypes.func,
onSort: PropTypes.func, onSort: PropTypes.func,
editor: PropTypes.object,
editable: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]), editable: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
editCellStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), editCellStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
editCellClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), editCellClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
editorStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), editorStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
editorClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), editorClasses: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
editorRenderer: PropTypes.func,
validator: PropTypes.func, validator: PropTypes.func,
filter: PropTypes.object, filter: PropTypes.object,
filterValue: PropTypes.func filterValue: PropTypes.func

View File

@@ -2,6 +2,7 @@ import _ from './utils';
const events = [ const events = [
'onClick', 'onClick',
'onDoubleClick',
'onMouseEnter', 'onMouseEnter',
'onMouseLeave' 'onMouseLeave'
]; ];

View File

@@ -74,6 +74,25 @@ describe('BootstrapTable', () => {
}); });
}); });
describe('when props.wrapperClasses was defined', () => {
const classes = 'foo';
beforeEach(() => {
wrapper = shallow(
<BootstrapTable
keyField="id"
columns={ columns }
data={ data }
store={ store }
wrapperClasses={ classes }
/>);
});
it('should display customized classes correctly', () => {
expect(wrapper.find(`.${classes}`).length).toBe(1);
});
});
describe('when props.id was defined', () => { describe('when props.id was defined', () => {
const id = 'foo'; const id = 'foo';