mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2026-08-12 20:00:17 +00:00
Merge pull request #240 from react-bootstrap-table/develop
2018/03/06 release
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
* [mode (**required**)](#mode)
|
||||
|
||||
## Optional
|
||||
* [selected](#selected)
|
||||
* [style](#style)
|
||||
* [classes)](#classes)
|
||||
* [bgColor](#bgColor)
|
||||
@@ -52,6 +53,16 @@ const selectRow = {
|
||||
/>
|
||||
```
|
||||
|
||||
### <a name='selected'>selectRow.selected - [Array]</a>
|
||||
`selectRow.selected` allow you have default selections on table.
|
||||
|
||||
```js
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
selected: [1, 3] // should be a row keys array
|
||||
};
|
||||
```
|
||||
|
||||
### <a name='style'>selectRow.style - [Object | Function]</a>
|
||||
`selectRow.style` allow you to have custom style on selected rows:
|
||||
|
||||
|
||||
@@ -32,9 +32,10 @@ TextEditor.propTypes = {
|
||||
defaultValue: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number
|
||||
]).isRequired
|
||||
])
|
||||
};
|
||||
TextEditor.defaultProps = {
|
||||
className: null
|
||||
className: null,
|
||||
defaultValue: ''
|
||||
};
|
||||
export default TextEditor;
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
import Code from 'components/common/code-block';
|
||||
import { productsGenerator } from 'utils/common';
|
||||
|
||||
const products = productsGenerator();
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
clickToSelect: true,
|
||||
selected: [1, 3]
|
||||
};
|
||||
|
||||
const sourceCode = `\
|
||||
import BootstrapTable from 'react-bootstrap-table-next';
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
text: 'Product ID'
|
||||
}, {
|
||||
dataField: 'name',
|
||||
text: 'Product Name'
|
||||
}, {
|
||||
dataField: 'price',
|
||||
text: 'Product Price'
|
||||
}];
|
||||
|
||||
const selectRow = {
|
||||
mode: 'checkbox',
|
||||
clickToSelect: true,
|
||||
selected: [1, 3]
|
||||
};
|
||||
|
||||
<BootstrapTable
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
selectRow={ selectRow }
|
||||
/>
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } selectRow={ selectRow } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
+25
-6
@@ -38,9 +38,28 @@ const columns = [{
|
||||
<BootstrapTable keyField='id' data={ products } columns={ columns } />
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable keyField="id" data={ products } columns={ columns } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
export default class Test extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { data: products };
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
this.setState(() => {
|
||||
const newProducts = productsGenerator(21);
|
||||
return {
|
||||
data: newProducts
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button className="btn btn-default" onClick={ this.handleClick }>Change Data</button>
|
||||
<BootstrapTable keyField="id" data={ this.state.data } columns={ columns } />
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ import CellEditClassTable from 'examples/cell-edit/cell-edit-class-table';
|
||||
import SingleSelectionTable from 'examples/row-selection/single-selection';
|
||||
import MultipleSelectionTable from 'examples/row-selection/multiple-selection';
|
||||
import ClickToSelectTable from 'examples/row-selection/click-to-select';
|
||||
import DefaultSelectTable from 'examples/row-selection/default-select';
|
||||
import ClickToSelectWithCellEditTable from 'examples/row-selection/click-to-select-with-cell-edit';
|
||||
import SelectionStyleTable from 'examples/row-selection/selection-style';
|
||||
import SelectionClassTable from 'examples/row-selection/selection-class';
|
||||
@@ -189,6 +190,7 @@ storiesOf('Row Selection', module)
|
||||
.add('Single Selection', () => <SingleSelectionTable />)
|
||||
.add('Multiple Selection', () => <MultipleSelectionTable />)
|
||||
.add('Click to Select', () => <ClickToSelectTable />)
|
||||
.add('Default Select', () => <DefaultSelectTable />)
|
||||
.add('Click to Select and Edit Cell', () => <ClickToSelectWithCellEditTable />)
|
||||
.add('Selection Style', () => <SelectionStyleTable />)
|
||||
.add('Selection Class', () => <SelectionClassTable />)
|
||||
|
||||
+3
-1
@@ -70,10 +70,12 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
allRowsSelected: isSelectedAll(store)
|
||||
});
|
||||
|
||||
const tableCaption = (caption && <Caption>{ caption }</Caption>);
|
||||
|
||||
return (
|
||||
<div className="react-bootstrap-table">
|
||||
<table className={ tableClass }>
|
||||
<Caption>{ caption }</Caption>
|
||||
{ tableCaption }
|
||||
<Header
|
||||
columns={ columns }
|
||||
sortField={ store.sortField }
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import _ from './utils';
|
||||
|
||||
const events = [
|
||||
'onClick',
|
||||
'onMouseEnter',
|
||||
'onMouseLeave'
|
||||
];
|
||||
|
||||
export default ExtendBase =>
|
||||
class RowEventDelegater extends ExtendBase {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.clickNum = 0;
|
||||
this.createDefaultEventHandler = this.createDefaultEventHandler.bind(this);
|
||||
this.createClickEventHandler = this.createClickEventHandler.bind(this);
|
||||
}
|
||||
|
||||
createDefaultEventHandler(cb) {
|
||||
return (e) => {
|
||||
const { row, rowIndex } = this.props;
|
||||
cb(e, row, rowIndex);
|
||||
};
|
||||
}
|
||||
|
||||
createClickEventHandler(cb) {
|
||||
return (e) => {
|
||||
const {
|
||||
row,
|
||||
selected,
|
||||
keyField,
|
||||
selectable,
|
||||
rowIndex,
|
||||
selectRow: {
|
||||
onRowSelect,
|
||||
clickToEdit
|
||||
},
|
||||
cellEdit: {
|
||||
mode,
|
||||
DBCLICK_TO_CELL_EDIT,
|
||||
DELAY_FOR_DBCLICK
|
||||
}
|
||||
} = this.props;
|
||||
|
||||
const clickFn = () => {
|
||||
if (cb) {
|
||||
cb(e, row, rowIndex);
|
||||
}
|
||||
if (selectable) {
|
||||
const key = _.get(row, keyField);
|
||||
onRowSelect(key, !selected, rowIndex);
|
||||
}
|
||||
};
|
||||
|
||||
if (mode === DBCLICK_TO_CELL_EDIT && clickToEdit) {
|
||||
this.clickNum += 1;
|
||||
_.debounce(() => {
|
||||
if (this.clickNum === 1) {
|
||||
clickFn();
|
||||
}
|
||||
this.clickNum = 0;
|
||||
}, DELAY_FOR_DBCLICK)();
|
||||
} else {
|
||||
clickFn();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
delegate(attrs = {}) {
|
||||
const newAttrs = {};
|
||||
if (this.props.selectRow && this.props.selectRow.clickToSelect) {
|
||||
newAttrs.onClick = this.createClickEventHandler(attrs.onClick);
|
||||
}
|
||||
Object.keys(attrs).forEach((attr) => {
|
||||
if (!newAttrs[attr]) {
|
||||
if (events.includes(attr)) {
|
||||
newAttrs[attr] = this.createDefaultEventHandler(attrs[attr]);
|
||||
} else {
|
||||
newAttrs[attr] = attrs[attr];
|
||||
}
|
||||
}
|
||||
});
|
||||
return newAttrs;
|
||||
}
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint no-param-reassign: 0 */
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -21,11 +22,21 @@ export default Base =>
|
||||
super(props);
|
||||
this.handleRowSelect = this.handleRowSelect.bind(this);
|
||||
this.handleAllRowsSelect = this.handleAllRowsSelect.bind(this);
|
||||
props.store.selected = this.props.selectRow.selected || [];
|
||||
this.state = {
|
||||
selectedRowKeys: props.store.selected
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.selectRow) {
|
||||
this.store.selected = nextProps.selectRow.selected || [];
|
||||
this.setState(() => ({
|
||||
selectedRowKeys: this.store.selected
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* row selection handler
|
||||
* @param {String} rowKey - row key of what was selected.
|
||||
|
||||
+4
-68
@@ -6,68 +6,10 @@ import PropTypes from 'prop-types';
|
||||
import _ from './utils';
|
||||
import Cell from './cell';
|
||||
import SelectionCell from './row-selection/selection-cell';
|
||||
import eventDelegater from './row-event-delegater';
|
||||
import Const from './const';
|
||||
|
||||
class Row extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.clickNum = 0;
|
||||
this.handleRowClick = this.handleRowClick.bind(this);
|
||||
this.handleSimpleRowClick = this.handleSimpleRowClick.bind(this);
|
||||
}
|
||||
|
||||
handleRowClick(e) {
|
||||
const {
|
||||
row,
|
||||
selected,
|
||||
keyField,
|
||||
selectable,
|
||||
rowIndex,
|
||||
selectRow: {
|
||||
onRowSelect,
|
||||
clickToEdit
|
||||
},
|
||||
cellEdit: {
|
||||
mode,
|
||||
DBCLICK_TO_CELL_EDIT,
|
||||
DELAY_FOR_DBCLICK
|
||||
},
|
||||
attrs
|
||||
} = this.props;
|
||||
|
||||
const clickFn = () => {
|
||||
if (attrs.onClick) {
|
||||
attrs.onClick(e, row, rowIndex);
|
||||
}
|
||||
if (selectable) {
|
||||
const key = _.get(row, keyField);
|
||||
onRowSelect(key, !selected, rowIndex);
|
||||
}
|
||||
};
|
||||
|
||||
if (mode === DBCLICK_TO_CELL_EDIT && clickToEdit) {
|
||||
this.clickNum += 1;
|
||||
_.debounce(() => {
|
||||
if (this.clickNum === 1) {
|
||||
clickFn();
|
||||
}
|
||||
this.clickNum = 0;
|
||||
}, DELAY_FOR_DBCLICK)();
|
||||
} else {
|
||||
clickFn();
|
||||
}
|
||||
}
|
||||
|
||||
handleSimpleRowClick(e) {
|
||||
const {
|
||||
row,
|
||||
rowIndex,
|
||||
attrs
|
||||
} = this.props;
|
||||
|
||||
attrs.onClick(e, row, rowIndex);
|
||||
}
|
||||
|
||||
class Row extends eventDelegater(Component) {
|
||||
render() {
|
||||
const {
|
||||
row,
|
||||
@@ -96,14 +38,8 @@ class Row extends Component {
|
||||
} = cellEdit;
|
||||
|
||||
const key = _.get(row, keyField);
|
||||
const { clickToSelect, hideSelectColumn } = selectRow;
|
||||
|
||||
const trAttrs = { ...attrs };
|
||||
if (clickToSelect) {
|
||||
trAttrs.onClick = this.handleRowClick;
|
||||
} else if (attrs.onClick) {
|
||||
trAttrs.onClick = this.handleSimpleRowClick;
|
||||
}
|
||||
const { hideSelectColumn } = selectRow;
|
||||
const trAttrs = this.delegate(attrs);
|
||||
|
||||
return (
|
||||
<tr style={ style } className={ className } { ...trAttrs }>
|
||||
|
||||
+4
-6
@@ -39,12 +39,10 @@ export default Base =>
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.isDataChanged) {
|
||||
const sortedColumn = nextProps.columns.find(
|
||||
column => column.dataField === nextProps.store.sortField);
|
||||
if (sortedColumn) {
|
||||
nextProps.store.sortBy(sortedColumn);
|
||||
}
|
||||
const sortedColumn = nextProps.columns.find(
|
||||
column => column.dataField === nextProps.store.sortField);
|
||||
if (sortedColumn && sortedColumn.sort) {
|
||||
nextProps.store.sortBy(sortedColumn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import wrapperFactory from '../../src/row-selection/wrapper';
|
||||
|
||||
describe('RowSelectionWrapper', () => {
|
||||
let wrapper;
|
||||
let selectRow;
|
||||
|
||||
const columns = [{
|
||||
dataField: 'id',
|
||||
@@ -25,10 +26,6 @@ describe('RowSelectionWrapper', () => {
|
||||
name: 'B'
|
||||
}];
|
||||
|
||||
const selectRow = {
|
||||
mode: 'radio'
|
||||
};
|
||||
|
||||
const rowIndex = 1;
|
||||
|
||||
const keyField = 'id';
|
||||
@@ -38,6 +35,9 @@ describe('RowSelectionWrapper', () => {
|
||||
const RowSelectionWrapper = wrapperFactory(BootstrapTable);
|
||||
|
||||
beforeEach(() => {
|
||||
selectRow = {
|
||||
mode: 'radio'
|
||||
};
|
||||
wrapper = shallow(
|
||||
<RowSelectionWrapper
|
||||
keyField={ keyField }
|
||||
@@ -54,6 +54,10 @@ describe('RowSelectionWrapper', () => {
|
||||
expect(wrapper.find(BootstrapTable)).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have correct store.selected value', () => {
|
||||
expect(store.selected).toEqual([]);
|
||||
});
|
||||
|
||||
it('should have correct state', () => {
|
||||
expect(wrapper.state().selectedRowKeys).toBeDefined();
|
||||
expect(wrapper.state().selectedRowKeys.length).toEqual(0);
|
||||
@@ -64,6 +68,30 @@ describe('RowSelectionWrapper', () => {
|
||||
expect(wrapper.props().onAllRowsSelect).toBeDefined();
|
||||
});
|
||||
|
||||
describe('when selectRow.selected is defiend', () => {
|
||||
beforeEach(() => {
|
||||
selectRow.mode = 'checkbox';
|
||||
selectRow.selected = [1, 3];
|
||||
wrapper = shallow(
|
||||
<RowSelectionWrapper
|
||||
keyField={ keyField }
|
||||
data={ data }
|
||||
columns={ columns }
|
||||
selectRow={ selectRow }
|
||||
store={ store }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should have correct store.selected value', () => {
|
||||
expect(store.selected).toEqual(selectRow.selected);
|
||||
});
|
||||
|
||||
it('should have correct state', () => {
|
||||
expect(wrapper.state().selectedRowKeys).toEqual(selectRow.selected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selectRow.mode is \'radio\'', () => {
|
||||
const firstSelectedRow = data[0][keyField];
|
||||
const secondSelectedRow = data[1][keyField];
|
||||
|
||||
@@ -799,8 +799,10 @@ describe('Row', () => {
|
||||
selected
|
||||
selectable
|
||||
/>);
|
||||
wrapper.instance().handleRowClick();
|
||||
wrapper.instance().handleRowClick();
|
||||
// console.log(wrapper.instance());
|
||||
const rowClick = wrapper.instance().createClickEventHandler();
|
||||
rowClick();
|
||||
rowClick();
|
||||
});
|
||||
|
||||
it('should increase clickNum as 2', () => {
|
||||
|
||||
@@ -219,30 +219,12 @@ describe('SortWrapper', () => {
|
||||
nextProps = { columns, store };
|
||||
store.sortField = columns[1].dataField;
|
||||
store.sortOrder = Const.SORT_DESC;
|
||||
store.sortBy = sinon.stub();
|
||||
});
|
||||
|
||||
describe('if nextProps.isDataChanged is true', () => {
|
||||
beforeEach(() => {
|
||||
nextProps.isDataChanged = true;
|
||||
store.sortBy = sinon.stub();
|
||||
});
|
||||
|
||||
it('should sorting again', () => {
|
||||
wrapper.instance().componentWillReceiveProps(nextProps);
|
||||
expect(store.sortBy.calledOnce).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('if nextProps.isDataChanged is false', () => {
|
||||
beforeEach(() => {
|
||||
nextProps.isDataChanged = false;
|
||||
store.sortBy = sinon.stub();
|
||||
});
|
||||
|
||||
it('should not sorting', () => {
|
||||
wrapper.instance().componentWillReceiveProps(nextProps);
|
||||
expect(store.sortBy.calledOnce).toBeFalsy();
|
||||
});
|
||||
it('should sorting again', () => {
|
||||
wrapper.instance().componentWillReceiveProps(nextProps);
|
||||
expect(store.sortBy.calledOnce).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user