fix selection column broken when bootstrap4

This commit is contained in:
AllenFang 2018-07-22 16:20:53 +08:00
parent f7ba8e377d
commit c0416fc307
5 changed files with 136 additions and 69 deletions

View File

@ -5,6 +5,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Const from '../const';
import { BootstrapContext } from '../contexts/bootstrap';
export default class SelectionCell extends Component {
static propTypes = {
@ -59,21 +60,28 @@ export default class SelectionCell extends Component {
} = this.props;
return (
<td onClick={ this.handleClick }>
<BootstrapContext.Consumer>
{
selectionRenderer ? selectionRenderer({
mode: inputType,
checked: selected,
disabled
}) : (
<input
type={ inputType }
checked={ selected }
disabled={ disabled }
/>
({ bootstrap4 }) => (
<td onClick={ this.handleClick }>
{
selectionRenderer ? selectionRenderer({
mode: inputType,
checked: selected,
disabled
}) : (
<input
type={ inputType }
checked={ selected }
disabled={ disabled }
className={ bootstrap4 ? 'selection-input-4' : '' }
/>
)
}
</td>
)
}
</td>
</BootstrapContext.Consumer>
);
}
}

View File

@ -2,11 +2,13 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Const from '../const';
import { BootstrapContext } from '../contexts/bootstrap';
export const CheckBox = ({ checked, indeterminate }) => (
export const CheckBox = ({ className, checked, indeterminate }) => (
<input
type="checkbox"
checked={ checked }
className={ className }
ref={ (input) => {
if (input) input.indeterminate = indeterminate; // eslint-disable-line no-param-reassign
} }
@ -15,7 +17,8 @@ export const CheckBox = ({ checked, indeterminate }) => (
CheckBox.propTypes = {
checked: PropTypes.bool.isRequired,
indeterminate: PropTypes.bool.isRequired
indeterminate: PropTypes.bool.isRequired,
className: PropTypes.string
};
export default class SelectionHeaderCell extends Component {
@ -67,26 +70,36 @@ export default class SelectionHeaderCell extends Component {
const attrs = {};
let content;
if (selectionHeaderRenderer) {
content = selectionHeaderRenderer({
mode,
checked,
indeterminate
});
attrs.onClick = this.handleCheckBoxClick;
} else if (mode === ROW_SELECT_MULTIPLE) {
content = (
<CheckBox
{ ...this.props }
checked={ checked }
indeterminate={ indeterminate }
/>
);
if (selectionHeaderRenderer || mode === ROW_SELECT_MULTIPLE) {
attrs.onClick = this.handleCheckBoxClick;
}
return (
<th data-row-selection { ...attrs }>{ content }</th>
<BootstrapContext.Consumer>
{
({ bootstrap4 }) => {
if (selectionHeaderRenderer) {
content = selectionHeaderRenderer({
mode,
checked,
indeterminate
});
} else if (mode === ROW_SELECT_MULTIPLE) {
content = (
<CheckBox
{ ...this.props }
checked={ checked }
className={ bootstrap4 ? 'selection-input-4' : '' }
indeterminate={ indeterminate }
/>
);
}
return (
<th data-row-selection { ...attrs }>{ content }</th>
);
}
}
</BootstrapContext.Consumer>
);
}
}

View File

@ -59,6 +59,11 @@
width: 30px;
}
th > .selection-input-4,
td > .selection-input-4 {
margin: -4px;
}
td.react-bs-table-no-data {
text-align: center;
}

View File

@ -1,7 +1,9 @@
import 'jsdom-global/register';
import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import { shallowWithContext } from '../test-helpers/new-context';
import SelectionCell from '../../src/row-selection/selection-cell';
describe('<SelectionCell />', () => {
@ -52,14 +54,14 @@ describe('<SelectionCell />', () => {
describe('when disabled prop is false', () => {
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionCell
selected
rowKey={ rowKey }
mode={ mode }
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
/>
/>, { bootstrap4: false }
);
wrapper.find('td').simulate('click');
});
@ -78,7 +80,7 @@ describe('<SelectionCell />', () => {
describe('when disabled prop is true', () => {
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionCell
selected
rowKey={ rowKey }
@ -86,7 +88,7 @@ describe('<SelectionCell />', () => {
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
disabled
/>
/>, { bootstrap4: false }
);
wrapper.find('td').simulate('click');
});
@ -102,14 +104,14 @@ describe('<SelectionCell />', () => {
describe('if selectRow.mode is radio', () => {
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionCell
selected
rowKey={ rowKey }
mode="radio"
rowIndex={ rowIndex }
onRowSelect={ mockOnRowSelect }
/>
/>, { bootstrap4: false }
);
});
@ -118,38 +120,27 @@ describe('<SelectionCell />', () => {
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(1);
expect(mockOnRowSelect.calledWith(rowKey, true, rowIndex)).toBe(true);
// second click
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(2);
expect(mockOnRowSelect.calledWith(rowKey, true, rowIndex)).toBe(true);
});
});
describe('if selectRow.mode is checkbox', () => {
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionCell
rowKey={ rowKey }
mode="checkbox"
rowIndex={ rowIndex }
selected
onRowSelect={ mockOnRowSelect }
/>
/>, { bootstrap4: false }
);
});
it('should be called with correct paramters', () => {
// first click
wrapper.setProps({ selected: true });
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(1);
expect(mockOnRowSelect.calledWith(rowKey, false, rowIndex)).toBe(true);
// second click
wrapper.setProps({ selected: false });
wrapper.find('td').simulate('click');
expect(mockOnRowSelect.callCount).toBe(2);
expect(mockOnRowSelect.calledWith(rowKey, true, rowIndex)).toBe(true);
expect(mockOnRowSelect.calledWith(rowKey, false, rowIndex, undefined)).toBe(true);
});
});
});
@ -159,33 +150,33 @@ describe('<SelectionCell />', () => {
const selected = true;
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
/>
/>, { bootstrap4: false }
);
});
it('should render component correctly', () => {
expect(wrapper.find('td').length).toBe(1);
expect(wrapper.find('input').length).toBe(1);
expect(wrapper.find('input')).toHaveLength(1);
expect(wrapper.find('input').get(0).props.type).toBe(mode);
expect(wrapper.find('input').get(0).props.checked).toBe(selected);
});
describe('when disabled prop give as true', () => {
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
disabled
/>
/>, { bootstrap4: false }
);
});
@ -200,14 +191,14 @@ describe('<SelectionCell />', () => {
beforeEach(() => {
selectionRenderer.mockClear();
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
selectionRenderer={ selectionRenderer }
/>
/>, { bootstrap4: false }
);
});
@ -224,5 +215,23 @@ describe('<SelectionCell />', () => {
});
});
});
describe('when bootstrap4 context is true', () => {
beforeEach(() => {
wrapper = shallowWithContext(
<SelectionCell
rowKey={ 1 }
mode={ mode }
rowIndex={ rowIndex }
selected={ selected }
/>, { bootstrap4: true }
);
});
it('should render component correctly', () => {
expect(wrapper.find('td').length).toBe(1);
expect(wrapper.find('.selection-input-4')).toHaveLength(1);
});
});
});
});

View File

@ -2,6 +2,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import sinon from 'sinon';
import { shallowWithContext } from '../test-helpers/new-context';
import Const from '../../src/const';
import SelectionHeaderCell, { CheckBox } from '../../src/row-selection/selection-header-cell';
@ -11,7 +12,7 @@ describe('<SelectionHeaderCell />', () => {
describe('shouldComponentUpdate', () => {
describe('when props.mode is radio', () => {
it('should not update component', () => {
wrapper = shallow(<SelectionHeaderCell mode="radio" />);
wrapper = shallow(<SelectionHeaderCell mode="radio" />, { bootstrap4: false });
expect(wrapper.instance().shouldComponentUpdate({})).toBe(false);
});
@ -24,7 +25,9 @@ describe('<SelectionHeaderCell />', () => {
const nextProps = { checkedStatus };
wrapper = shallow(
<SelectionHeaderCell mode="checkbox" checkedStatus={ checkedStatus } />);
<SelectionHeaderCell mode="checkbox" checkedStatus={ checkedStatus } />,
{ bootstrap4: false }
);
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(false);
});
@ -37,7 +40,9 @@ describe('<SelectionHeaderCell />', () => {
const nextProps = { checkedStatus };
wrapper = shallow(
<SelectionHeaderCell mode="checkbox" checkedStatus={ CHECKBOX_STATUS_INDETERMINATE } />);
<SelectionHeaderCell mode="checkbox" checkedStatus={ CHECKBOX_STATUS_INDETERMINATE } />,
{ bootstrap4: false }
);
expect(wrapper.instance().shouldComponentUpdate(nextProps)).toBe(true);
});
@ -57,12 +62,14 @@ describe('<SelectionHeaderCell />', () => {
describe('if props.mode is radio', () => {
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionHeaderCell
mode="radio"
checkedStatus={ Const.CHECKBOX_STATUS_CHECKED }
onAllRowsSelect={ mockOnAllRowsSelect }
/>);
/>,
{ bootstrap4: false }
);
});
it('should do nothing', () => {
@ -75,12 +82,14 @@ describe('<SelectionHeaderCell />', () => {
describe('if props.mode is checkbox', () => {
beforeEach(() => {
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionHeaderCell
mode="checkbox"
checkedStatus={ Const.CHECKBOX_STATUS_CHECKED }
onAllRowsSelect={ mockOnAllRowsSelect }
/>);
/>,
{ bootstrap4: false }
);
});
it('should call handleCheckBoxClick', () => {
@ -98,7 +107,10 @@ describe('<SelectionHeaderCell />', () => {
beforeEach(() => {
const checkedStatus = Const.CHECKBOX_STATUS_CHECKED;
wrapper = shallow(<SelectionHeaderCell mode="radio" checkedStatus={ checkedStatus } />);
wrapper = shallowWithContext(
<SelectionHeaderCell mode="radio" checkedStatus={ checkedStatus } />,
{ bootstrap4: false }
);
});
it('should not render checkbox', () => {
@ -112,7 +124,10 @@ describe('<SelectionHeaderCell />', () => {
const checkedStatus = Const.CHECKBOX_STATUS_CHECKED;
beforeEach(() => {
wrapper = shallow(<SelectionHeaderCell mode="checkbox" checkedStatus={ checkedStatus } />);
wrapper = shallowWithContext(
<SelectionHeaderCell mode="checkbox" checkedStatus={ checkedStatus } />,
{ bootstrap4: false }
);
});
it('should render checkbox', () => {
@ -134,12 +149,13 @@ describe('<SelectionHeaderCell />', () => {
beforeEach(() => {
selectionHeaderRenderer.mockClear();
wrapper = shallow(
wrapper = shallowWithContext(
<SelectionHeaderCell
mode="checkbox"
checkedStatus={ checkedStatus }
selectionHeaderRenderer={ selectionHeaderRenderer }
/>
/>,
{ bootstrap4: false }
);
});
@ -156,6 +172,22 @@ describe('<SelectionHeaderCell />', () => {
});
});
});
describe('when bootstrap4 context is true', () => {
beforeEach(() => {
const checkedStatus = Const.CHECKBOX_STATUS_CHECKED;
wrapper = shallowWithContext(
<SelectionHeaderCell mode="checkbox" checkedStatus={ checkedStatus } />,
{ bootstrap4: true }
);
});
it('should not render checkbox', () => {
expect(wrapper.find('th').length).toBe(1);
expect(wrapper.find('.selection-input-4').length).toBe(1);
});
});
});
});