mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 11:10:01 +08:00
Optimize table selection (#3757)
* Extract SelectionRadio, SelectionCheckbox, SelectionCheckboxAll * Add some tests
This commit is contained in:
parent
be9ed0388c
commit
0e6ac6bc4c
81
components/table/SelectionBox.tsx
Normal file
81
components/table/SelectionBox.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Checkbox from '../checkbox';
|
||||||
|
import Radio from '../radio';
|
||||||
|
import { Store } from './createStore';
|
||||||
|
|
||||||
|
export interface SelectionBoxProps {
|
||||||
|
store: Store;
|
||||||
|
type: string;
|
||||||
|
defaultSelection: string[];
|
||||||
|
rowIndex: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
onChange: (e) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SelectionBox extends React.Component<SelectionBoxProps, any> {
|
||||||
|
unsubscribe: () => void;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
checked: this.getCheckState(props),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
const { store } = this.props;
|
||||||
|
this.unsubscribe = store.subscribe(() => {
|
||||||
|
const checked = this.getCheckState(this.props);
|
||||||
|
if (checked !== this.state.checked) {
|
||||||
|
this.setState({ checked });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCheckState(props) {
|
||||||
|
const { store, defaultSelection, rowIndex } = props;
|
||||||
|
let checked = false;
|
||||||
|
if (store.getState().selectionDirty) {
|
||||||
|
checked = store.getState().selectedRowKeys.indexOf(rowIndex) >= 0;
|
||||||
|
} else {
|
||||||
|
checked = (store.getState().selectedRowKeys.indexOf(rowIndex) >= 0 ||
|
||||||
|
defaultSelection.indexOf(rowIndex) >= 0);
|
||||||
|
}
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { type, rowIndex, disabled, onChange } = this.props;
|
||||||
|
const { checked } = this.state;
|
||||||
|
|
||||||
|
if (type === 'radio') {
|
||||||
|
return (
|
||||||
|
<Radio
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={onChange}
|
||||||
|
value={rowIndex}
|
||||||
|
checked={checked}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
checked={checked}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
114
components/table/SelectionCheckboxAll.tsx
Normal file
114
components/table/SelectionCheckboxAll.tsx
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Checkbox from '../checkbox';
|
||||||
|
import { Store } from './createStore';
|
||||||
|
|
||||||
|
export interface SelectionCheckboxAllProps {
|
||||||
|
store: Store;
|
||||||
|
disabled: boolean;
|
||||||
|
getCheckboxPropsByItem: (item) => any;
|
||||||
|
getRecordKey: (record, index?) => string;
|
||||||
|
data: any[];
|
||||||
|
onChange: (e) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SelectionCheckboxAll extends React.Component<SelectionCheckboxAllProps, any> {
|
||||||
|
unsubscribe: () => void;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
checked: this.getCheckState(),
|
||||||
|
indeterminate: this.getIndeterminateState(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe() {
|
||||||
|
const { store } = this.props;
|
||||||
|
this.unsubscribe = store.subscribe(() => {
|
||||||
|
const checked = this.getCheckState();
|
||||||
|
const indeterminate = this.getIndeterminateState();
|
||||||
|
if (checked !== this.state.checked) {
|
||||||
|
this.setState({ checked });
|
||||||
|
}
|
||||||
|
if (indeterminate !== this.state.indeterminate) {
|
||||||
|
this.setState({ indeterminate });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSelection(data, type, byDefaultChecked) {
|
||||||
|
const { store, getCheckboxPropsByItem, getRecordKey } = this.props;
|
||||||
|
// type should be 'every' | 'some'
|
||||||
|
if (type === 'every' || type === 'some') {
|
||||||
|
return (
|
||||||
|
byDefaultChecked
|
||||||
|
? data[type](item => getCheckboxPropsByItem(item).defaultChecked)
|
||||||
|
: data[type]((item, i) =>
|
||||||
|
store.getState().selectedRowKeys.indexOf(getRecordKey(item, i)) >= 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCheckState() {
|
||||||
|
const { store, data } = this.props;
|
||||||
|
let checked;
|
||||||
|
if (!data.length) {
|
||||||
|
checked = false;
|
||||||
|
} else {
|
||||||
|
checked = store.getState().selectionDirty
|
||||||
|
? this.checkSelection(data, 'every', false)
|
||||||
|
: (
|
||||||
|
this.checkSelection(data, 'every', false) ||
|
||||||
|
this.checkSelection(data, 'every', true)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndeterminateState() {
|
||||||
|
const { store, data } = this.props;
|
||||||
|
let indeterminate;
|
||||||
|
if (!data.length) {
|
||||||
|
indeterminate = false;
|
||||||
|
} else {
|
||||||
|
indeterminate = store.getState().selectionDirty
|
||||||
|
? (
|
||||||
|
this.checkSelection(data, 'some', false) &&
|
||||||
|
!this.checkSelection(data, 'every', false)
|
||||||
|
)
|
||||||
|
: ((this.checkSelection(data, 'some', false) &&
|
||||||
|
!this.checkSelection(data, 'every', false)) ||
|
||||||
|
(this.checkSelection(data, 'some', true) &&
|
||||||
|
!this.checkSelection(data, 'every', true))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return indeterminate;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { disabled, onChange } = this.props;
|
||||||
|
const { checked, indeterminate } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
checked={checked}
|
||||||
|
indeterminate={indeterminate}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import RcTable from 'rc-table';
|
import RcTable from 'rc-table';
|
||||||
import Checkbox from '../checkbox';
|
|
||||||
import Radio from '../radio';
|
|
||||||
import FilterDropdown from './filterDropdown';
|
import FilterDropdown from './filterDropdown';
|
||||||
import Pagination, { PaginationProps } from '../pagination';
|
import Pagination, { PaginationProps } from '../pagination';
|
||||||
import Icon from '../icon';
|
import Icon from '../icon';
|
||||||
@ -11,6 +9,9 @@ import { flatArray, treeMap } from './util';
|
|||||||
import assign from 'object-assign';
|
import assign from 'object-assign';
|
||||||
import splitObject from '../_util/splitObject';
|
import splitObject from '../_util/splitObject';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
|
import createStore, { Store } from './createStore';
|
||||||
|
import SelectionBox from './SelectionBox';
|
||||||
|
import SelectionCheckboxAll from './SelectionCheckboxAll';
|
||||||
|
|
||||||
function noop() {
|
function noop() {
|
||||||
}
|
}
|
||||||
@ -134,6 +135,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
|
|
||||||
context: TableContext;
|
context: TableContext;
|
||||||
CheckboxPropsCache: Object;
|
CheckboxPropsCache: Object;
|
||||||
|
store: Store;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -149,9 +151,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
|
|
||||||
this.state = assign({}, this.getSortStateFromColumns(), {
|
this.state = assign({}, this.getSortStateFromColumns(), {
|
||||||
// 减少状态
|
// 减少状态
|
||||||
selectedRowKeys: (props.rowSelection || {}).selectedRowKeys || [],
|
|
||||||
filters: this.getFiltersFromColumns(),
|
filters: this.getFiltersFromColumns(),
|
||||||
selectionDirty: false,
|
|
||||||
pagination: this.hasPagination() ?
|
pagination: this.hasPagination() ?
|
||||||
assign({}, defaultPagination, pagination, {
|
assign({}, defaultPagination, pagination, {
|
||||||
current: pagination.defaultCurrent || pagination.current || 1,
|
current: pagination.defaultCurrent || pagination.current || 1,
|
||||||
@ -160,9 +160,14 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.CheckboxPropsCache = {};
|
this.CheckboxPropsCache = {};
|
||||||
|
|
||||||
|
this.store = createStore({
|
||||||
|
selectedRowKeys: (props.rowSelection || {}).selectedRowKeys || [],
|
||||||
|
selectionDirty: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCheckboxPropsByItem(item) {
|
getCheckboxPropsByItem = (item) => {
|
||||||
const { rowSelection = {} } = this.props;
|
const { rowSelection = {} } = this.props;
|
||||||
if (!rowSelection.getCheckboxProps) {
|
if (!rowSelection.getCheckboxProps) {
|
||||||
return {};
|
return {};
|
||||||
@ -204,14 +209,14 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
// dataSource 的变化会清空选中项
|
// dataSource 的变化会清空选中项
|
||||||
if ('dataSource' in nextProps &&
|
if ('dataSource' in nextProps &&
|
||||||
nextProps.dataSource !== this.props.dataSource) {
|
nextProps.dataSource !== this.props.dataSource) {
|
||||||
this.setState({
|
this.store.setState({
|
||||||
selectionDirty: false,
|
selectionDirty: false,
|
||||||
});
|
});
|
||||||
this.CheckboxPropsCache = {};
|
this.CheckboxPropsCache = {};
|
||||||
}
|
}
|
||||||
if (nextProps.rowSelection &&
|
if (nextProps.rowSelection &&
|
||||||
'selectedRowKeys' in nextProps.rowSelection) {
|
'selectedRowKeys' in nextProps.rowSelection) {
|
||||||
this.setState({
|
this.store.setState({
|
||||||
selectedRowKeys: nextProps.rowSelection.selectedRowKeys || [],
|
selectedRowKeys: nextProps.rowSelection.selectedRowKeys || [],
|
||||||
});
|
});
|
||||||
const { rowSelection } = this.props;
|
const { rowSelection } = this.props;
|
||||||
@ -246,7 +251,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
setSelectedRowKeys(selectedRowKeys, { selectWay, record, checked, changeRowKeys }: any) {
|
setSelectedRowKeys(selectedRowKeys, { selectWay, record, checked, changeRowKeys }: any) {
|
||||||
const { rowSelection = {} } = this.props;
|
const { rowSelection = {} } = this.props;
|
||||||
if (rowSelection && !('selectedRowKeys' in rowSelection)) {
|
if (rowSelection && !('selectedRowKeys' in rowSelection)) {
|
||||||
this.setState({ selectedRowKeys });
|
this.store.setState({ selectedRowKeys });
|
||||||
}
|
}
|
||||||
const data = this.getFlatData();
|
const data = this.getFlatData();
|
||||||
if (!rowSelection.onChange && !rowSelection[selectWay]) {
|
if (!rowSelection.onChange && !rowSelection[selectWay]) {
|
||||||
@ -385,7 +390,6 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const newState = {
|
const newState = {
|
||||||
selectionDirty: false,
|
|
||||||
pagination,
|
pagination,
|
||||||
filters: {},
|
filters: {},
|
||||||
};
|
};
|
||||||
@ -409,6 +413,9 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState(newState, () => {
|
this.setState(newState, () => {
|
||||||
|
this.store.setState({
|
||||||
|
selectionDirty: false,
|
||||||
|
});
|
||||||
const onChange = this.props.onChange;
|
const onChange = this.props.onChange;
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||||
@ -422,15 +429,15 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
|
|
||||||
handleSelect = (record, rowIndex, e) => {
|
handleSelect = (record, rowIndex, e) => {
|
||||||
const checked = e.target.checked;
|
const checked = e.target.checked;
|
||||||
const defaultSelection = this.state.selectionDirty ? [] : this.getDefaultSelection();
|
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection();
|
||||||
let selectedRowKeys = this.state.selectedRowKeys.concat(defaultSelection);
|
let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
|
||||||
let key = this.getRecordKey(record, rowIndex);
|
let key = this.getRecordKey(record, rowIndex);
|
||||||
if (checked) {
|
if (checked) {
|
||||||
selectedRowKeys.push(this.getRecordKey(record, rowIndex));
|
selectedRowKeys.push(this.getRecordKey(record, rowIndex));
|
||||||
} else {
|
} else {
|
||||||
selectedRowKeys = selectedRowKeys.filter((i) => key !== i);
|
selectedRowKeys = selectedRowKeys.filter((i) => key !== i);
|
||||||
}
|
}
|
||||||
this.setState({
|
this.store.setState({
|
||||||
selectionDirty: true,
|
selectionDirty: true,
|
||||||
});
|
});
|
||||||
this.setSelectedRowKeys(selectedRowKeys, {
|
this.setSelectedRowKeys(selectedRowKeys, {
|
||||||
@ -442,11 +449,11 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
|
|
||||||
handleRadioSelect = (record, rowIndex, e) => {
|
handleRadioSelect = (record, rowIndex, e) => {
|
||||||
const checked = e.target.checked;
|
const checked = e.target.checked;
|
||||||
const defaultSelection = this.state.selectionDirty ? [] : this.getDefaultSelection();
|
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection();
|
||||||
let selectedRowKeys = this.state.selectedRowKeys.concat(defaultSelection);
|
let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
|
||||||
let key = this.getRecordKey(record, rowIndex);
|
let key = this.getRecordKey(record, rowIndex);
|
||||||
selectedRowKeys = [key];
|
selectedRowKeys = [key];
|
||||||
this.setState({
|
this.store.setState({
|
||||||
selectionDirty: true,
|
selectionDirty: true,
|
||||||
});
|
});
|
||||||
this.setSelectedRowKeys(selectedRowKeys, {
|
this.setSelectedRowKeys(selectedRowKeys, {
|
||||||
@ -459,8 +466,8 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
handleSelectAllRow = (e) => {
|
handleSelectAllRow = (e) => {
|
||||||
const checked = e.target.checked;
|
const checked = e.target.checked;
|
||||||
const data = this.getFlatCurrentPageData();
|
const data = this.getFlatCurrentPageData();
|
||||||
const defaultSelection = this.state.selectionDirty ? [] : this.getDefaultSelection();
|
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection();
|
||||||
const selectedRowKeys = this.state.selectedRowKeys.concat(defaultSelection);
|
const selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
|
||||||
const changableRowKeys = data
|
const changableRowKeys = data
|
||||||
.filter(item => !this.getCheckboxPropsByItem(item).disabled)
|
.filter(item => !this.getCheckboxPropsByItem(item).disabled)
|
||||||
.map((item, i) => this.getRecordKey(item, i));
|
.map((item, i) => this.getRecordKey(item, i));
|
||||||
@ -482,7 +489,7 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.setState({
|
this.store.setState({
|
||||||
selectionDirty: true,
|
selectionDirty: true,
|
||||||
});
|
});
|
||||||
this.setSelectedRowKeys(selectedRowKeys, {
|
this.setSelectedRowKeys(selectedRowKeys, {
|
||||||
@ -503,7 +510,6 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
pagination.onChange(pagination.current);
|
pagination.onChange(pagination.current);
|
||||||
|
|
||||||
const newState = {
|
const newState = {
|
||||||
selectionDirty: false,
|
|
||||||
pagination,
|
pagination,
|
||||||
};
|
};
|
||||||
// Controlled current prop will not respond user interaction
|
// Controlled current prop will not respond user interaction
|
||||||
@ -514,6 +520,10 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
}
|
}
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
|
|
||||||
|
this.store.setState({
|
||||||
|
selectionDirty: false,
|
||||||
|
});
|
||||||
|
|
||||||
const onChange = this.props.onChange;
|
const onChange = this.props.onChange;
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
onChange.apply(null, this.prepareParamsArguments(assign({}, this.state, {
|
||||||
@ -523,48 +533,31 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSelectionRadio = (_, record, index) => {
|
renderSelectionBox = (type) => {
|
||||||
let rowIndex = this.getRecordKey(record, index); // 从 1 开始
|
return (_, record, index) => {
|
||||||
const props = this.getCheckboxPropsByItem(record);
|
let rowIndex = this.getRecordKey(record, index); // 从 1 开始
|
||||||
let checked;
|
const props = this.getCheckboxPropsByItem(record);
|
||||||
if (this.state.selectionDirty) {
|
const handleChange = (e) => {
|
||||||
checked = this.state.selectedRowKeys.indexOf(rowIndex) >= 0;
|
type === 'radio' ? this.handleRadioSelect(record, rowIndex, e) :
|
||||||
} else {
|
this.handleSelect(record, rowIndex, e);
|
||||||
checked = (this.state.selectedRowKeys.indexOf(rowIndex) >= 0 ||
|
};
|
||||||
this.getDefaultSelection().indexOf(rowIndex) >= 0);
|
|
||||||
}
|
return (
|
||||||
return (
|
<span onClick={stopPropagation}>
|
||||||
<span onClick={stopPropagation}>
|
<SelectionBox
|
||||||
<Radio disabled={props.disabled}
|
type={type}
|
||||||
onChange={(e) => this.handleRadioSelect(record, rowIndex, e)}
|
store={this.store}
|
||||||
value={rowIndex} checked={checked}
|
rowIndex={rowIndex}
|
||||||
/>
|
disabled={props.disabled}
|
||||||
</span>
|
onChange={handleChange}
|
||||||
);
|
defaultSelection={this.getDefaultSelection()}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSelectionCheckBox = (_, record, index) => {
|
getRecordKey = (record, index?): string => {
|
||||||
let rowIndex = this.getRecordKey(record, index); // 从 1 开始
|
|
||||||
let checked;
|
|
||||||
if (this.state.selectionDirty) {
|
|
||||||
checked = this.state.selectedRowKeys.indexOf(rowIndex) >= 0;
|
|
||||||
} else {
|
|
||||||
checked = (this.state.selectedRowKeys.indexOf(rowIndex) >= 0 ||
|
|
||||||
this.getDefaultSelection().indexOf(rowIndex) >= 0);
|
|
||||||
}
|
|
||||||
const props = this.getCheckboxPropsByItem(record);
|
|
||||||
return (
|
|
||||||
<span onClick={stopPropagation}>
|
|
||||||
<Checkbox
|
|
||||||
checked={checked}
|
|
||||||
disabled={props.disabled}
|
|
||||||
onChange={(e) => this.handleSelect(record, rowIndex, e)}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getRecordKey(record, index?): string {
|
|
||||||
const rowKey = this.props.rowKey;
|
const rowKey = this.props.rowKey;
|
||||||
if (typeof rowKey === 'function') {
|
if (typeof rowKey === 'function') {
|
||||||
return rowKey(record, index);
|
return rowKey(record, index);
|
||||||
@ -576,19 +569,6 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
return recordKey;
|
return recordKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSelection(data, type, byDefaultChecked) {
|
|
||||||
// type should be 'every' | 'some'
|
|
||||||
if (type === 'every' || type === 'some') {
|
|
||||||
return (
|
|
||||||
byDefaultChecked
|
|
||||||
? data[type](item => this.getCheckboxPropsByItem(item).defaultChecked)
|
|
||||||
: data[type]((item, i) =>
|
|
||||||
this.state.selectedRowKeys.indexOf(this.getRecordKey(item, i)) >= 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRowSelection() {
|
renderRowSelection() {
|
||||||
const { prefixCls, rowSelection } = this.props;
|
const { prefixCls, rowSelection } = this.props;
|
||||||
const columns = this.props.columns.concat();
|
const columns = this.props.columns.concat();
|
||||||
@ -599,51 +579,23 @@ export default class Table<T> extends React.Component<TableProps<T>, any> {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
let checked;
|
const selectionColumn: TableColumnConfig<any> = {
|
||||||
let indeterminate;
|
key: 'selection-column',
|
||||||
if (!data.length) {
|
render: this.renderSelectionBox(rowSelection.type),
|
||||||
checked = false;
|
className: `${prefixCls}-selection-column`,
|
||||||
indeterminate = false;
|
};
|
||||||
} else {
|
if (rowSelection.type !== 'radio') {
|
||||||
checked = this.state.selectionDirty
|
|
||||||
? this.checkSelection(data, 'every', false)
|
|
||||||
: (
|
|
||||||
this.checkSelection(data, 'every', false) ||
|
|
||||||
this.checkSelection(data, 'every', true)
|
|
||||||
);
|
|
||||||
indeterminate = this.state.selectionDirty
|
|
||||||
? (
|
|
||||||
this.checkSelection(data, 'some', false) &&
|
|
||||||
!this.checkSelection(data, 'every', false)
|
|
||||||
)
|
|
||||||
: ((this.checkSelection(data, 'some', false) &&
|
|
||||||
!this.checkSelection(data, 'every', false)) ||
|
|
||||||
(this.checkSelection(data, 'some', true) &&
|
|
||||||
!this.checkSelection(data, 'every', true))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let selectionColumn;
|
|
||||||
if (rowSelection.type === 'radio') {
|
|
||||||
selectionColumn = {
|
|
||||||
key: 'selection-column',
|
|
||||||
render: this.renderSelectionRadio,
|
|
||||||
className: `${prefixCls}-selection-column`,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const checkboxAllDisabled = data.every(item => this.getCheckboxPropsByItem(item).disabled);
|
const checkboxAllDisabled = data.every(item => this.getCheckboxPropsByItem(item).disabled);
|
||||||
const checkboxAll = (
|
selectionColumn.title = (
|
||||||
<Checkbox checked={checked}
|
<SelectionCheckboxAll
|
||||||
indeterminate={indeterminate}
|
store={this.store}
|
||||||
|
data={data}
|
||||||
|
getCheckboxPropsByItem={this.getCheckboxPropsByItem}
|
||||||
|
getRecordKey={this.getRecordKey}
|
||||||
disabled={checkboxAllDisabled}
|
disabled={checkboxAllDisabled}
|
||||||
onChange={this.handleSelectAllRow}
|
onChange={this.handleSelectAllRow}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
selectionColumn = {
|
|
||||||
key: 'selection-column',
|
|
||||||
title: checkboxAll,
|
|
||||||
render: this.renderSelectionCheckBox,
|
|
||||||
className: `${prefixCls}-selection-column`,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if (columns.some(column => column.fixed === 'left' || column.fixed === true)) {
|
if (columns.some(column => column.fixed === 'left' || column.fixed === true)) {
|
||||||
selectionColumn.fixed = 'left';
|
selectionColumn.fixed = 'left';
|
||||||
|
38
components/table/createStore.tsx
Normal file
38
components/table/createStore.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import assign from 'object-assign';
|
||||||
|
|
||||||
|
export interface Store {
|
||||||
|
setState: (Object) => void;
|
||||||
|
getState: () => any;
|
||||||
|
subscribe: (listener: () => void) => () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function createStore(initialState): Store {
|
||||||
|
let state = initialState;
|
||||||
|
const listeners: any[] = [];
|
||||||
|
|
||||||
|
function setState(partial) {
|
||||||
|
state = assign({}, state, partial);
|
||||||
|
for (let i = 0; i < listeners.length; i++) {
|
||||||
|
listeners[i]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function subscribe(listener) {
|
||||||
|
listeners.push(listener);
|
||||||
|
|
||||||
|
return function unsubscribe() {
|
||||||
|
const index = listeners.indexOf(listener);
|
||||||
|
listeners.splice(index, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setState,
|
||||||
|
getState,
|
||||||
|
subscribe,
|
||||||
|
};
|
||||||
|
}
|
87
tests/table/SelectionBox.test.js
Normal file
87
tests/table/SelectionBox.test.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import createStore from '../../components/table/createStore';
|
||||||
|
import SelectionBox from '../../components/table/SelectionBox';
|
||||||
|
import TestUtils from 'react-addons-test-utils';
|
||||||
|
|
||||||
|
describe('SelectionBox', () => {
|
||||||
|
it('unchecked by selectedRowKeys ', () => {
|
||||||
|
const store = createStore({
|
||||||
|
selectedRowKeys: [],
|
||||||
|
selectionDirty: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = TestUtils.renderIntoDocument(
|
||||||
|
<SelectionBox
|
||||||
|
store={store}
|
||||||
|
rowIndex="1"
|
||||||
|
disabled={false}
|
||||||
|
onChange={() => {}}
|
||||||
|
defaultSelection={[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(instance.state).toEqual({ checked: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checked by selectedRowKeys ', () => {
|
||||||
|
const store = createStore({
|
||||||
|
selectedRowKeys: ['1'],
|
||||||
|
selectionDirty: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = TestUtils.renderIntoDocument(
|
||||||
|
<SelectionBox
|
||||||
|
store={store}
|
||||||
|
rowIndex="1"
|
||||||
|
disabled={false}
|
||||||
|
onChange={() => {}}
|
||||||
|
defaultSelection={[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(instance.state).toEqual({ checked: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checked by defaultSelection', () => {
|
||||||
|
const store = createStore({
|
||||||
|
selectedRowKeys: [],
|
||||||
|
selectionDirty: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = TestUtils.renderIntoDocument(
|
||||||
|
<SelectionBox
|
||||||
|
store={store}
|
||||||
|
rowIndex="1"
|
||||||
|
disabled={false}
|
||||||
|
onChange={() => {}}
|
||||||
|
defaultSelection={['1']}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(instance.state).toEqual({ checked: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checked when store change', () => {
|
||||||
|
const store = createStore({
|
||||||
|
selectedRowKeys: [],
|
||||||
|
selectionDirty: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = TestUtils.renderIntoDocument(
|
||||||
|
<SelectionBox
|
||||||
|
store={store}
|
||||||
|
rowIndex="1"
|
||||||
|
disabled={false}
|
||||||
|
onChange={() => {}}
|
||||||
|
defaultSelection={[]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
store.setState({
|
||||||
|
selectedRowKeys: ['1'],
|
||||||
|
selectionDirty: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(instance.state).toEqual({ checked: true });
|
||||||
|
});
|
||||||
|
})
|
91
tests/table/Table.test.js
Normal file
91
tests/table/Table.test.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import createStore from '../../components/table/createStore';
|
||||||
|
import Table from '../../components/table';
|
||||||
|
import TestUtils from 'react-addons-test-utils';
|
||||||
|
|
||||||
|
describe('Table', () => {
|
||||||
|
describe('row selection', () => {
|
||||||
|
it('allow select by checkbox', () => {
|
||||||
|
const columns = [{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
}];
|
||||||
|
|
||||||
|
const data = [{
|
||||||
|
name: 'Jack',
|
||||||
|
}, {
|
||||||
|
name: 'Lucy',
|
||||||
|
}];
|
||||||
|
|
||||||
|
const instance = TestUtils.renderIntoDocument(
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={data}
|
||||||
|
rowSelection={{}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkboxes = TestUtils.scryRenderedDOMComponentsWithTag(instance, 'input');
|
||||||
|
const checkboxAll = checkboxes[0];
|
||||||
|
|
||||||
|
checkboxAll.checked = true;
|
||||||
|
TestUtils.Simulate.change(checkboxAll);
|
||||||
|
|
||||||
|
expect(instance.store.getState()).toEqual({
|
||||||
|
selectedRowKeys: [0, 1],
|
||||||
|
selectionDirty: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
checkboxes[1].checked = false;
|
||||||
|
TestUtils.Simulate.change(checkboxes[1]);
|
||||||
|
|
||||||
|
expect(instance.store.getState()).toEqual({
|
||||||
|
selectedRowKeys: [1],
|
||||||
|
selectionDirty: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
checkboxes[1].checked = true;
|
||||||
|
TestUtils.Simulate.change(checkboxes[1]);
|
||||||
|
|
||||||
|
expect(instance.store.getState()).toEqual({
|
||||||
|
selectedRowKeys: [1, 0],
|
||||||
|
selectionDirty: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pass getCheckboxProps to checkbox', () => {
|
||||||
|
const columns = [{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
}];
|
||||||
|
|
||||||
|
const data = [{
|
||||||
|
id: 0,
|
||||||
|
name: 'Jack',
|
||||||
|
}, {
|
||||||
|
id: 1,
|
||||||
|
name: 'Lucy',
|
||||||
|
}];
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
getCheckboxProps: record => ({
|
||||||
|
disabled: record.name === 'Lucy',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const instance = TestUtils.renderIntoDocument(
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={data}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
rowKey="id"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkboxes = TestUtils.scryRenderedDOMComponentsWithTag(instance, 'input');
|
||||||
|
|
||||||
|
expect(checkboxes[1].disabled).toBe(false);
|
||||||
|
expect(checkboxes[2].disabled).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user