feat: Support Table.EXPAND_COLUMN & Table.SELECTION_COLUMN (#33026)

* feat: Table support Table.EXPAND_COLUMN & Table.SELECTION_COLUMN

* docs: Update docs
This commit is contained in:
二货机器人 2021-11-26 17:50:41 +08:00 committed by GitHub
parent c162045162
commit 2b152b0c22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 591 additions and 31 deletions

View File

@ -29,6 +29,7 @@ import {
} from './interface';
import useSelection, {
SELECTION_ALL,
SELECTION_COLUMN,
SELECTION_INVERT,
SELECTION_NONE,
} from './hooks/useSelection';
@ -375,7 +376,6 @@ function InternalTable<RecordType extends object = any>(
expandType,
childrenColumnName,
locale: tableLocale,
expandIconColumnIndex: mergedExpandable.expandIconColumnIndex,
getPopupContainer,
});
@ -524,6 +524,8 @@ type InternalTableType = typeof ForwardTable;
interface TableInterface extends InternalTableType {
defaultProps?: Partial<TableProps<any>>;
SELECTION_COLUMN: typeof SELECTION_COLUMN;
EXPAND_COLUMN: typeof RcTable.EXPAND_COLUMN;
SELECTION_ALL: 'SELECT_ALL';
SELECTION_INVERT: 'SELECT_INVERT';
SELECTION_NONE: 'SELECT_NONE';
@ -538,6 +540,8 @@ Table.defaultProps = {
rowKey: 'key',
};
Table.SELECTION_COLUMN = SELECTION_COLUMN;
Table.EXPAND_COLUMN = RcTable.EXPAND_COLUMN;
Table.SELECTION_ALL = SELECTION_ALL;
Table.SELECTION_INVERT = SELECTION_INVERT;
Table.SELECTION_NONE = SELECTION_NONE;

View File

@ -0,0 +1,76 @@
import React from 'react';
import { mount } from 'enzyme';
import Table from '..';
import { resetWarned } from '../../_util/devWarning';
describe('Table.order', () => {
window.requestAnimationFrame = callback => window.setTimeout(callback, 16);
window.cancelAnimationFrame = window.clearTimeout;
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
afterEach(() => {
errorSpy.mockReset();
});
afterAll(() => {
errorSpy.mockRestore();
});
const columns = [
{
title: 'Name',
dataIndex: 'name',
},
];
const data = [
{ key: 0, name: 'Jack' },
{ key: 1, name: 'Lucy' },
{ key: 2, name: 'Tom' },
{ key: 3, name: 'Jerry' },
];
function createTable(props = {}) {
return <Table columns={columns} dataSource={data} {...props} />;
}
it('warning if duplicated SELECTION_COLUMN', () => {
resetWarned();
mount(
createTable({
columns: [Table.SELECTION_COLUMN, Table.SELECTION_COLUMN],
rowSelection: {},
expandable: {
expandedRowRender: () => null,
},
}),
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Table] Multiple `SELECTION_COLUMN` exist in `columns`.',
);
});
it('auto fixed', () => {
const wrapper = mount(
createTable({
columns: [
{
dataIndex: 'name',
fixed: true,
},
Table.SELECTION_COLUMN,
{
dataIndex: 'key',
},
],
rowSelection: {},
}),
);
expect(wrapper.find('tr').last().find('td')).toHaveLength(3);
expect(wrapper.find('tr').last().find('td.ant-table-cell-fix-left')).toHaveLength(2);
wrapper.unmount();
});
});

View File

@ -12196,6 +12196,370 @@ exports[`renders ./components/table/demo/nested-table.md correctly 1`] = `
</div>
`;
exports[`renders ./components/table/demo/order-column.md correctly 1`] = `
<div
class="ant-table-wrapper"
>
<div
class="ant-spin-nested-loading"
>
<div
class="ant-spin-container"
>
<div
class="ant-table"
>
<div
class="ant-table-container"
>
<div
class="ant-table-content"
>
<table
style="table-layout:auto"
>
<colgroup>
<col />
<col
class="ant-table-expand-icon-col"
/>
<col />
<col
class="ant-table-selection-col"
/>
</colgroup>
<thead
class="ant-table-thead"
>
<tr>
<th
class="ant-table-cell"
>
Name
</th>
<th
class="ant-table-cell ant-table-row-expand-icon-cell"
/>
<th
class="ant-table-cell"
>
Age
</th>
<th
class="ant-table-cell ant-table-selection-column"
>
<div
class="ant-table-selection"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</div>
</th>
<th
class="ant-table-cell"
>
Address
</th>
</tr>
</thead>
<tbody
class="ant-table-tbody"
>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="1"
>
<td
class="ant-table-cell"
>
John Brown
</td>
<td
class="ant-table-cell ant-table-row-expand-icon-cell"
>
<button
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
New York No. 1 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="2"
>
<td
class="ant-table-cell"
>
Jim Green
</td>
<td
class="ant-table-cell ant-table-row-expand-icon-cell"
>
<button
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
</td>
<td
class="ant-table-cell"
>
42
</td>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
London No. 1 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="3"
>
<td
class="ant-table-cell"
>
Not Expandable
</td>
<td
class="ant-table-cell ant-table-row-expand-icon-cell"
>
<button
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
</td>
<td
class="ant-table-cell"
>
29
</td>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
Jiangsu No. 1 Lake Park
</td>
</tr>
<tr
class="ant-table-row ant-table-row-level-0"
data-row-key="4"
>
<td
class="ant-table-cell"
>
Joe Black
</td>
<td
class="ant-table-cell ant-table-row-expand-icon-cell"
>
<button
aria-label="Expand row"
class="ant-table-row-expand-icon ant-table-row-expand-icon-collapsed"
type="button"
/>
</td>
<td
class="ant-table-cell"
>
32
</td>
<td
class="ant-table-cell ant-table-selection-column"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
</td>
<td
class="ant-table-cell"
>
Sidney No. 1 Lake Park
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<ul
class="ant-pagination ant-table-pagination ant-table-pagination-right"
unselectable="unselectable"
>
<li
aria-disabled="true"
class="ant-pagination-prev ant-pagination-disabled"
title="Previous Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="left"
class="anticon anticon-left"
role="img"
>
<svg
aria-hidden="true"
data-icon="left"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</span>
</button>
</li>
<li
class="ant-pagination-item ant-pagination-item-1 ant-pagination-item-active"
tabindex="0"
title="1"
>
<a
rel="nofollow"
>
1
</a>
</li>
<li
aria-disabled="true"
class="ant-pagination-next ant-pagination-disabled"
title="Next Page"
>
<button
class="ant-pagination-item-link"
disabled=""
tabindex="-1"
type="button"
>
<span
aria-label="right"
class="anticon anticon-right"
role="img"
>
<svg
aria-hidden="true"
data-icon="right"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"
/>
</svg>
</span>
</button>
</li>
</ul>
</div>
</div>
</div>
`;
exports[`renders ./components/table/demo/pagination.md correctly 1`] = `
<div>
<div>

View File

@ -0,0 +1,70 @@
---
order: 14.1
version: 4.18.0
title:
en-US: Order Specific Column
zh-CN: 特殊列排序
---
## zh-CN
你可以通过 `Table.EXPAND_COLUMN``Table.SELECT_COLUMN` 来控制选择和展开列的顺序。
## en-US
You can control the order of the expand and select columns by using `Table.EXPAND_COLUMN` and `Table.SELECT_COLUMN`.
```jsx
import { Table } from 'antd';
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
Table.EXPAND_COLUMN,
{ title: 'Age', dataIndex: 'age', key: 'age' },
Table.SELECTION_COLUMN,
{ title: 'Address', dataIndex: 'address', key: 'address' },
];
const data = [
{
key: 1,
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
},
{
key: 2,
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
},
{
key: 3,
name: 'Not Expandable',
age: 29,
address: 'Jiangsu No. 1 Lake Park',
description: 'This not expandable',
},
{
key: 4,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.',
},
];
ReactDOM.render(
<Table
columns={columns}
rowSelection={{}}
expandable={{
expandedRowRender: record => <p style={{ margin: 0 }}>{record.description}</p>,
}}
dataSource={data}
/>,
mountNode,
);
```

View File

@ -17,6 +17,7 @@ import {
TableRowSelection,
Key,
ColumnsType,
ColumnType,
GetRowKey,
TableLocale,
SelectionItem,
@ -26,14 +27,12 @@ import {
} from '../interface';
// TODO: warning if use ajax!!!
export const SELECTION_COLUMN = {} as const;
export const SELECTION_ALL = 'SELECT_ALL' as const;
export const SELECTION_INVERT = 'SELECT_INVERT' as const;
export const SELECTION_NONE = 'SELECT_NONE' as const;
function getFixedType<RecordType>(column: ColumnsType<RecordType>[number]): FixedType | undefined {
return column && column.fixed;
}
interface UseSelectionConfig<RecordType> {
prefixCls: string;
pageData: RecordType[];
@ -42,7 +41,6 @@ interface UseSelectionConfig<RecordType> {
getRecordByKey: (key: Key) => RecordType;
expandType: ExpandType;
childrenColumnName: string;
expandIconColumnIndex?: number;
locale: TableLocale;
getPopupContainer?: GetPopupContainer;
}
@ -105,7 +103,6 @@ export default function useSelection<RecordType>(
expandType,
childrenColumnName,
locale: tableLocale,
expandIconColumnIndex,
getPopupContainer,
} = config;
@ -347,11 +344,21 @@ export default function useSelection<RecordType>(
// ======================= Columns ========================
const transformColumns = useCallback(
(columns: ColumnsType<RecordType>): ColumnsType<RecordType> => {
// >>>>>>>>>>> Skip if not exists `rowSelection`
if (!rowSelection) {
return columns;
if (process.env.NODE_ENV !== 'production') {
devWarning(
!columns.includes(SELECTION_COLUMN),
'Table',
'`rowSelection` is not config but `SELECTION_COLUMN` exists in the `columns`.',
);
}
return columns.filter(col => col !== SELECTION_COLUMN);
}
// Support selection
// >>>>>>>>>>> Support selection
let cloneColumns = [...columns];
const keySet = new Set(derivedSelectedKeySet);
// Record key only need check with enabled
@ -619,8 +626,62 @@ export default function useSelection<RecordType>(
return node;
};
// Columns
// Insert selection column if not exist
if (!cloneColumns.includes(SELECTION_COLUMN)) {
// Always after expand icon
if (
cloneColumns.findIndex(
(col: any) => col[INTERNAL_COL_DEFINE]?.columnType === 'EXPAND_COLUMN',
) === 0
) {
const [expandColumn, ...restColumns] = cloneColumns;
cloneColumns = [expandColumn, SELECTION_COLUMN, ...restColumns];
} else {
// Normal insert at first column
cloneColumns = [SELECTION_COLUMN, ...cloneColumns];
}
}
// Deduplicate selection column
const selectionColumnIndex = cloneColumns.indexOf(SELECTION_COLUMN);
if (
process.env.NODE_ENV !== 'production' &&
cloneColumns.filter(col => col === SELECTION_COLUMN).length > 1
) {
devWarning(false, 'Table', 'Multiple `SELECTION_COLUMN` exist in `columns`.');
}
cloneColumns = cloneColumns.filter(
(column, index) => column !== SELECTION_COLUMN || index === selectionColumnIndex,
);
// Fixed column logic
const prevCol: ColumnType<RecordType> & Record<string, any> =
cloneColumns[selectionColumnIndex - 1];
const nextCol: ColumnType<RecordType> & Record<string, any> =
cloneColumns[selectionColumnIndex + 1];
let mergedFixed: FixedType | undefined = fixed;
if (mergedFixed === undefined) {
if (nextCol?.fixed !== undefined) {
mergedFixed = nextCol.fixed;
} else if (prevCol?.fixed !== undefined) {
mergedFixed = prevCol.fixed;
}
}
if (
mergedFixed &&
prevCol &&
prevCol[INTERNAL_COL_DEFINE]?.columnType === 'EXPAND_COLUMN' &&
prevCol.fixed === undefined
) {
prevCol.fixed = mergedFixed;
}
// Replace with real selection column
const selectionColumn = {
fixed: mergedFixed,
width: selectionColWidth,
className: `${prefixCls}-selection-column`,
title: rowSelection.columnTitle || title,
@ -630,15 +691,7 @@ export default function useSelection<RecordType>(
},
};
if (expandType === 'row' && columns.length && !expandIconColumnIndex) {
const [expandColumn, ...restColumns] = columns;
const selectionFixed = fixed || getFixedType(restColumns[0]);
if (selectionFixed) {
expandColumn.fixed = selectionFixed;
}
return [expandColumn, { ...selectionColumn, fixed: selectionFixed }, ...restColumns];
}
return [{ ...selectionColumn, fixed: fixed || getFixedType(columns[0]) }, ...columns];
return cloneColumns.map(col => (col === SELECTION_COLUMN ? selectionColumn : col));
},
[
getRowKey,

View File

@ -178,18 +178,14 @@ Properties for expandable.
| expandedRowKeys | Current expanded row keys | string\[] | - | |
| expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - | |
| expandIcon | Customize row expand Icon. Ref [example](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - | |
| expandIconColumnIndex | Customize expand icon column index. Not render when `-1` | number | - | |
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false | |
| fixed | Whether the expansion icon is fixed. Optional true `left` `right` | boolean \| string | false | 4.16.0 |
| indentSize | Indent size in pixels of tree data | number | 15 | |
| rowExpandable | Enable row can be expandable | (record) => boolean | - | |
| showExpandColumn | Show expand column | boolean | true | 4.18.0 |
| onExpand | Callback executed when the row expand icon is clicked | function(expanded, record) | - | |
| onExpandedRowsChange | Callback executed when the expanded rows change | function(expandedRows) | - | |
- `fixed`
- When set to true or `left` and `expandIconColumnIndex` is not set or is 0, enable fixed
- When set to true or `right` and `expandIconColumnIndex` is set to the number of table columns, enable fixed
### rowSelection
Properties for row selection.

View File

@ -185,18 +185,14 @@ const columns = [
| expandedRowKeys | 展开的行,控制属性 | string\[] | - | |
| expandedRowRender | 额外的展开行 | function(record, index, indent, expanded): ReactNode | - | |
| expandIcon | 自定义展开图标,参考[示例](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - | |
| expandIconColumnIndex | 自定义展开按钮的列顺序,`-1` 时不展示 | number | - | |
| expandRowByClick | 通过点击行来展开子行 | boolean | false | |
| fixed | 控制展开图标是否固定,可选 true `left` `right` | boolean \| string | false | 4.16.0 |
| indentSize | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | 15 | |
| rowExpandable | 设置是否允许行展开 | (record) => boolean | - | |
| showExpandColumn | 设置是否展示行展开列 | boolean | true | 4.18.0 |
| onExpand | 点击展开图标时触发 | function(expanded, record) | - | |
| onExpandedRowsChange | 展开的行变化时触发 | function(expandedRows) | - | |
- `fixed`
- 当设置为 true 或 `left``expandIconColumnIndex` 未设置或为 0 时,开启固定
- 当设置为 true 或 `right``expandIconColumnIndex` 设置为表格列数时,开启固定
### rowSelection
选择功能的配置。

View File

@ -4,6 +4,7 @@ import {
ColumnType as RcColumnType,
RenderedCell as RcRenderedCell,
ExpandableConfig,
FixedType,
} from 'rc-table/lib/interface';
import { TooltipProps } from '../tooltip';
import { CheckboxProps } from '../checkbox';
@ -159,7 +160,7 @@ export interface TableRowSelection<T> {
onSelectNone?: () => void;
selections?: INTERNAL_SELECTION_ITEM[] | boolean;
hideSelectAll?: boolean;
fixed?: boolean;
fixed?: FixedType;
columnWidth?: string | number;
columnTitle?: string | React.ReactNode;
checkStrictly?: boolean;

View File

@ -143,7 +143,7 @@
"rc-slider": "~9.7.4",
"rc-steps": "~4.1.0",
"rc-switch": "~3.2.0",
"rc-table": "~7.19.0",
"rc-table": "~7.20.0",
"rc-tabs": "~11.10.0",
"rc-textarea": "~0.3.0",
"rc-tooltip": "~5.1.1",