mirror of
https://github.com/gosticks/react-bootstrap-table2.git
synced 2025-10-16 11:55:39 +00:00
Merge pull request #607 from react-bootstrap-table/enhance/598
Enhance/598
This commit is contained in:
commit
ae4d38cae6
@ -17,6 +17,7 @@
|
||||
* [hover](#hover)
|
||||
* [condensed](#condensed)
|
||||
* [id](#id)
|
||||
* [tabIndexCell](#tabIndexCell)
|
||||
* [classes](#classes)
|
||||
* [wrapperClasses](#wrapperClasses)
|
||||
* [headerClasses](#headerClasses)
|
||||
@ -112,6 +113,10 @@ Same as bootstrap `.table-condensed` class for making a table more compact by cu
|
||||
|
||||
### <a name='id'>id - [String]</a>
|
||||
Customize id on `table` element.
|
||||
|
||||
### <a name='tabIndexCell'>tabIndexCell - [Bool]</a>
|
||||
Enable the `tabIndex` attribute on `<td>` element.
|
||||
|
||||
### <a name='classes'>classes - [String]</a>
|
||||
Customize class on `table` element.
|
||||
|
||||
|
||||
54
packages/react-bootstrap-table2-example/examples/basic/tabindex-column.js
vendored
Normal file
54
packages/react-bootstrap-table2-example/examples/basic/tabindex-column.js
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
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 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'
|
||||
}];
|
||||
|
||||
<BootstrapTable
|
||||
keyField='id'
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
selectRow={ { mode: 'checkbox' } }
|
||||
tabIndexCell
|
||||
/>
|
||||
`;
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<BootstrapTable
|
||||
keyField="id"
|
||||
data={ products }
|
||||
columns={ columns }
|
||||
selectRow={ { mode: 'checkbox' } }
|
||||
tabIndexCell
|
||||
/>
|
||||
<Code>{ sourceCode }</Code>
|
||||
</div>
|
||||
);
|
||||
@ -14,6 +14,7 @@ import CustomizedIdClassesTable from 'examples/basic/customized-id-classes';
|
||||
import CaptionTable from 'examples/basic/caption-table';
|
||||
import LargeTable from 'examples/basic/large-table';
|
||||
import ExposedAPITable from 'examples/basic/exposed-function';
|
||||
import TabIndexCellTable from 'examples/basic/tabindex-column';
|
||||
|
||||
// bootstrap 4
|
||||
import Bootstrap4DefaultSortTable from 'examples/bootstrap4/sort';
|
||||
@ -195,7 +196,8 @@ storiesOf('Basic Table', module)
|
||||
.add('Customized id and class table', () => <CustomizedIdClassesTable />)
|
||||
.add('Table with caption', () => <CaptionTable />)
|
||||
.add('Large Table', () => <LargeTable />)
|
||||
.add('Exposed API', () => <ExposedAPITable />);
|
||||
.add('Exposed API', () => <ExposedAPITable />)
|
||||
.add('Enable tabIndex on Cell', () => <TabIndexCellTable />);
|
||||
|
||||
storiesOf('Bootstrap 4', module)
|
||||
.addDecorator(bootstrapStyle(BOOTSTRAP_VERSION.FOUR))
|
||||
|
||||
7
packages/react-bootstrap-table2/src/body.js
vendored
7
packages/react-bootstrap-table2/src/body.js
vendored
@ -5,7 +5,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import _ from './utils';
|
||||
import Row from './row/simple-row';
|
||||
import SimpleRow from './row/simple-row';
|
||||
import RowAggregator from './row/aggregate-row';
|
||||
import RowSection from './row/row-section';
|
||||
import Const from './const';
|
||||
@ -24,6 +24,7 @@ class Body extends React.Component {
|
||||
const {
|
||||
columns,
|
||||
data,
|
||||
tabIndexCell,
|
||||
keyField,
|
||||
isEmpty,
|
||||
noDataIndication,
|
||||
@ -45,7 +46,7 @@ class Body extends React.Component {
|
||||
}
|
||||
content = <RowSection content={ indication } colSpan={ visibleColumnSize } />;
|
||||
} else {
|
||||
let RowComponent = Row;
|
||||
let RowComponent = SimpleRow;
|
||||
const selectRowEnabled = selectRow.mode !== Const.ROW_SELECT_DISABLED;
|
||||
const expandRowEnabled = !!expandRow.renderer;
|
||||
|
||||
@ -73,11 +74,13 @@ class Body extends React.Component {
|
||||
const baseRowProps = {
|
||||
key,
|
||||
row,
|
||||
tabIndexCell,
|
||||
columns,
|
||||
keyField,
|
||||
cellEdit,
|
||||
value: key,
|
||||
rowIndex: index,
|
||||
visibleColumnSize,
|
||||
attrs: rowEvents || {},
|
||||
...additionalRowProps
|
||||
};
|
||||
|
||||
@ -43,6 +43,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
data,
|
||||
columns,
|
||||
keyField,
|
||||
tabIndexCell,
|
||||
id,
|
||||
classes,
|
||||
striped,
|
||||
@ -89,6 +90,7 @@ class BootstrapTable extends PropsBaseResolver(Component) {
|
||||
<Body
|
||||
data={ data }
|
||||
keyField={ keyField }
|
||||
tabIndexCell={ tabIndexCell }
|
||||
columns={ columns }
|
||||
isEmpty={ this.isEmpty() }
|
||||
visibleColumnSize={ this.visibleColumnSize() }
|
||||
@ -118,6 +120,7 @@ BootstrapTable.propTypes = {
|
||||
striped: PropTypes.bool,
|
||||
bordered: PropTypes.bool,
|
||||
hover: PropTypes.bool,
|
||||
tabIndexCell: PropTypes.bool,
|
||||
id: PropTypes.string,
|
||||
classes: PropTypes.string,
|
||||
wrapperClasses: PropTypes.string,
|
||||
|
||||
3
packages/react-bootstrap-table2/src/cell.js
vendored
3
packages/react-bootstrap-table2/src/cell.js
vendored
@ -34,7 +34,8 @@ class Cell extends Component {
|
||||
!_.isEqual(this.props.style, nextProps.style) ||
|
||||
!_.isEqual(this.props.column.formatExtraData, nextProps.column.formatExtraData) ||
|
||||
!_.isEqual(this.props.column.events, nextProps.column.events) ||
|
||||
!_.isEqual(this.props.column.attrs, nextProps.column.attrs);
|
||||
!_.isEqual(this.props.column.attrs, nextProps.column.attrs) ||
|
||||
this.props.tabIndex !== nextProps.tabIndex;
|
||||
return shouldUpdate;
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@ export default class ExpandCell extends Component {
|
||||
expanded: PropTypes.bool.isRequired,
|
||||
onRowExpand: PropTypes.func.isRequired,
|
||||
expandColumnRenderer: PropTypes.func,
|
||||
rowIndex: PropTypes.number
|
||||
rowIndex: PropTypes.number,
|
||||
tabIndex: PropTypes.number
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -20,6 +21,16 @@ export default class ExpandCell extends Component {
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const shouldUpdate =
|
||||
this.props.rowIndex !== nextProps.rowIndex ||
|
||||
this.props.expanded !== nextProps.expanded ||
|
||||
this.props.rowKey !== nextProps.rowKey ||
|
||||
this.props.tabIndex !== nextProps.tabIndex;
|
||||
|
||||
return shouldUpdate;
|
||||
}
|
||||
|
||||
handleClick(e) {
|
||||
const { rowKey, expanded, onRowExpand, rowIndex } = this.props;
|
||||
|
||||
@ -27,10 +38,12 @@ export default class ExpandCell extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { expanded, expandColumnRenderer } = this.props;
|
||||
const { expanded, expandColumnRenderer, tabIndex } = this.props;
|
||||
const attrs = {};
|
||||
if (tabIndex !== -1) attrs.tabIndex = tabIndex;
|
||||
|
||||
return (
|
||||
<td onClick={ this.handleClick }>
|
||||
<td onClick={ this.handleClick } { ...attrs }>
|
||||
{
|
||||
expandColumnRenderer ? expandColumnRenderer({
|
||||
expanded
|
||||
|
||||
@ -15,6 +15,7 @@ export default class SelectionCell extends Component {
|
||||
onRowSelect: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
rowIndex: PropTypes.number,
|
||||
tabIndex: PropTypes.number,
|
||||
clickToSelect: PropTypes.bool,
|
||||
selectionRenderer: PropTypes.func
|
||||
}
|
||||
@ -29,7 +30,8 @@ export default class SelectionCell extends Component {
|
||||
this.props.rowIndex !== nextProps.rowIndex ||
|
||||
this.props.selected !== nextProps.selected ||
|
||||
this.props.disabled !== nextProps.disabled ||
|
||||
this.props.rowKey !== nextProps.rowKey;
|
||||
this.props.rowKey !== nextProps.rowKey ||
|
||||
this.props.tabIndex !== nextProps.tabIndex;
|
||||
|
||||
return shouldUpdate;
|
||||
}
|
||||
@ -60,14 +62,18 @@ export default class SelectionCell extends Component {
|
||||
mode: inputType,
|
||||
selected,
|
||||
disabled,
|
||||
tabIndex,
|
||||
selectionRenderer
|
||||
} = this.props;
|
||||
|
||||
const attrs = {};
|
||||
if (tabIndex !== -1) attrs.tabIndex = tabIndex;
|
||||
|
||||
return (
|
||||
<BootstrapContext.Consumer>
|
||||
{
|
||||
({ bootstrap4 }) => (
|
||||
<td onClick={ this.handleClick }>
|
||||
<td onClick={ this.handleClick } { ...attrs }>
|
||||
{
|
||||
selectionRenderer ? selectionRenderer({
|
||||
mode: inputType,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
/* eslint react/prop-types: 0 */
|
||||
/* eslint no-plusplus: 0 */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from '../utils';
|
||||
@ -55,6 +56,8 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
|
||||
expanded,
|
||||
selected,
|
||||
selectable,
|
||||
visibleColumnSize,
|
||||
tabIndexCell,
|
||||
...rest
|
||||
} = this.props;
|
||||
const key = _.get(row, keyField);
|
||||
@ -66,6 +69,8 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
|
||||
newAttrs.onClick = this.createClickEventHandler(newAttrs.onClick);
|
||||
}
|
||||
|
||||
let tabIndexStart = (rowIndex * visibleColumnSize) + 1;
|
||||
|
||||
return (
|
||||
<tr
|
||||
style={ style }
|
||||
@ -79,6 +84,7 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
|
||||
rowKey={ key }
|
||||
rowIndex={ rowIndex }
|
||||
expanded={ expanded }
|
||||
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
@ -91,6 +97,7 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
|
||||
rowIndex={ rowIndex }
|
||||
selected={ selected }
|
||||
disabled={ !selectable }
|
||||
tabIndex={ tabIndexCell ? tabIndexStart++ : -1 }
|
||||
/>
|
||||
)
|
||||
: null
|
||||
@ -101,6 +108,7 @@ export default class RowAggregator extends shouldUpdater(eventDelegater(React.Co
|
||||
keyField={ keyField }
|
||||
rowIndex={ rowIndex }
|
||||
shouldUpdate={ this.shouldUpdateRowContent }
|
||||
tabIndexStart={ tabIndexCell ? tabIndexStart : -1 }
|
||||
{ ...rest }
|
||||
/>
|
||||
</tr>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/* eslint react/prop-types: 0 */
|
||||
/* eslint react/no-array-index-key: 0 */
|
||||
/* eslint no-plusplus: 0 */
|
||||
import React from 'react';
|
||||
|
||||
import _ from '../utils';
|
||||
@ -25,9 +26,12 @@ export default class RowPureContent extends React.Component {
|
||||
onStart,
|
||||
clickToEdit,
|
||||
dbclickToEdit,
|
||||
EditingCellComponent
|
||||
EditingCellComponent,
|
||||
tabIndexStart
|
||||
} = this.props;
|
||||
|
||||
let tabIndex = tabIndexStart;
|
||||
|
||||
return columns.map((column, index) => {
|
||||
if (!column.hidden) {
|
||||
const { dataField } = column;
|
||||
@ -87,6 +91,10 @@ export default class RowPureContent extends React.Component {
|
||||
editableCell = column.editable(content, row, rowIndex, index);
|
||||
}
|
||||
|
||||
if (tabIndexStart !== -1) {
|
||||
cellAttrs.tabIndex = tabIndex++;
|
||||
}
|
||||
|
||||
return (
|
||||
<Cell
|
||||
key={ `${content}-${index}` }
|
||||
|
||||
@ -7,7 +7,7 @@ import RowPureContent from './row-pure-content';
|
||||
import eventDelegater from './event-delegater';
|
||||
import shouldUpdater from './should-updater';
|
||||
|
||||
class Row extends shouldUpdater(eventDelegater(Component)) {
|
||||
class SimpleRow extends shouldUpdater(eventDelegater(Component)) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.shouldUpdateRowContent = false;
|
||||
@ -26,19 +26,26 @@ class Row extends shouldUpdater(eventDelegater(Component)) {
|
||||
className,
|
||||
style,
|
||||
attrs,
|
||||
visibleColumnSize,
|
||||
tabIndexCell,
|
||||
...rest
|
||||
} = this.props;
|
||||
const trAttrs = this.delegate(attrs);
|
||||
const tabIndexStart = (this.props.rowIndex * visibleColumnSize) + 1;
|
||||
|
||||
return (
|
||||
<tr style={ style } className={ className } { ...trAttrs }>
|
||||
<RowPureContent shouldUpdate={ this.shouldUpdateRowContent } { ...rest } />
|
||||
<RowPureContent
|
||||
shouldUpdate={ this.shouldUpdateRowContent }
|
||||
tabIndexStart={ tabIndexCell ? tabIndexStart : -1 }
|
||||
{ ...rest }
|
||||
/>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Row.propTypes = {
|
||||
SimpleRow.propTypes = {
|
||||
row: PropTypes.object.isRequired,
|
||||
rowIndex: PropTypes.number.isRequired,
|
||||
columns: PropTypes.array.isRequired,
|
||||
@ -47,11 +54,11 @@ Row.propTypes = {
|
||||
attrs: PropTypes.object
|
||||
};
|
||||
|
||||
Row.defaultProps = {
|
||||
SimpleRow.defaultProps = {
|
||||
editable: true,
|
||||
style: {},
|
||||
className: null,
|
||||
attrs: {}
|
||||
};
|
||||
|
||||
export default Row;
|
||||
export default SimpleRow;
|
||||
|
||||
@ -198,6 +198,26 @@ describe('Cell', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when props.tabIndex is change', () => {
|
||||
const column = { dataField: 'name', text: 'Product Name' };
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
row,
|
||||
columnIndex: 1,
|
||||
rowIndex: 1,
|
||||
tabIndex: 5,
|
||||
column
|
||||
};
|
||||
wrapper = shallow(
|
||||
<Cell { ...props } />);
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
nextProps = { ...props, tabIndex: 2 };
|
||||
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if column.isDummyField is true', () => {
|
||||
describe('when content is change', () => {
|
||||
const column = { dataField: '', text: 'Product Name', isDummyField: true };
|
||||
|
||||
@ -57,6 +57,27 @@ describe('<SelectionCell />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when tabIndex prop has been changed', () => {
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
selected: false,
|
||||
mode,
|
||||
rowIndex,
|
||||
disabled: false,
|
||||
tabIndex: 0,
|
||||
rowKey: 1
|
||||
};
|
||||
wrapper = shallow(
|
||||
<SelectionCell { ...props } />
|
||||
);
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
nextProps = { ...props, tabIndex: 2 };
|
||||
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when disabled prop has been changed', () => {
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
|
||||
@ -104,6 +104,47 @@ describe('RowPureContent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when tabIndexStart prop is -1', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<RowPureContent
|
||||
tabIndexStart={ -1 }
|
||||
keyField={ keyField }
|
||||
rowIndex={ rowIndex }
|
||||
columns={ defaultColumns }
|
||||
row={ row }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render tabIndex prop on Cell', () => {
|
||||
wrapper.find(Cell).forEach((cell) => {
|
||||
expect(cell.prop('tabIndex')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when tabIndexStart prop is not -1', () => {
|
||||
const tabIndexStart = 4;
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<RowPureContent
|
||||
tabIndexStart={ tabIndexStart }
|
||||
keyField={ keyField }
|
||||
rowIndex={ rowIndex }
|
||||
columns={ defaultColumns }
|
||||
row={ row }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render correct tabIndex prop on Cell', () => {
|
||||
wrapper.find(Cell).forEach((cell, i) => {
|
||||
expect(cell.prop('tabIndex')).toEqual(tabIndexStart + i);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when editingRowIdx and editingColIdx prop is defined', () => {
|
||||
const editingRowIdx = rowIndex;
|
||||
const editingColIdx = 1;
|
||||
|
||||
@ -57,6 +57,49 @@ describe('SimpleRow', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find(RowPureContent)).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe('when tabIndexCell prop is enable', () => {
|
||||
const visibleColumnSize = 3;
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<SimpleRow
|
||||
keyField={ keyField }
|
||||
rowIndex={ rowIndex }
|
||||
columns={ defaultColumns }
|
||||
row={ row }
|
||||
tabIndexCell
|
||||
visibleColumnSize={ visibleColumnSize }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should render correct tabIndexStart', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find(RowPureContent)).toHaveLength(1);
|
||||
expect(wrapper.find(RowPureContent).prop('tabIndexStart')).toBe((rowIndex * visibleColumnSize) + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when tabIndexCell prop is disable', () => {
|
||||
const visibleColumnSize = 3;
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<SimpleRow
|
||||
keyField={ keyField }
|
||||
rowIndex={ rowIndex }
|
||||
columns={ defaultColumns }
|
||||
row={ row }
|
||||
visibleColumnSize={ visibleColumnSize }
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
it('should always render tabIndexStart as -1', () => {
|
||||
expect(wrapper.length).toBe(1);
|
||||
expect(wrapper.find(RowPureContent)).toHaveLength(1);
|
||||
expect(wrapper.find(RowPureContent).prop('tabIndexStart')).toBe(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldComponentUpdate', () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user