feat: more obviously tips for next sort (#21631)

This commit is contained in:
zefeng 2020-02-29 22:01:13 +08:00 committed by GitHub
parent c8c824c138
commit e262d14c2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 122 additions and 25 deletions

View File

@ -22,6 +22,9 @@ export default {
sortTitle: 'Sort', sortTitle: 'Sort',
expand: 'Expand row', expand: 'Expand row',
collapse: 'Collapse row', collapse: 'Collapse row',
triggerDesc: 'Click sort by descend',
triggerAsc: 'Click sort by ascend',
cancelSort: 'Click to cancel sort',
}, },
Modal: { Modal: {
okText: 'OK', okText: 'OK',

View File

@ -23,6 +23,9 @@ export default {
sortTitle: '排序', sortTitle: '排序',
expand: '展开行', expand: '展开行',
collapse: '关闭行', collapse: '关闭行',
triggerDesc: '点击降序',
triggerAsc: '点击升序',
cancelSort: '取消排序',
}, },
Modal: { Modal: {
okText: '确定', okText: '确定',

View File

@ -84,6 +84,7 @@ export interface TableProps<RecordType>
scrollToFirstRowOnChange?: boolean; scrollToFirstRowOnChange?: boolean;
}; };
sortDirections?: SortOrder[]; sortDirections?: SortOrder[];
showSorterTooltip?: boolean;
} }
function Table<RecordType extends object = any>(props: TableProps<RecordType>) { function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
@ -110,6 +111,7 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
scroll, scroll,
sortDirections, sortDirections,
locale, locale,
showSorterTooltip = true,
} = props; } = props;
const size = React.useContext(SizeContext); const size = React.useContext(SizeContext);
const { locale: contextLocale = defaultLocale, renderEmpty, direction } = React.useContext( const { locale: contextLocale = defaultLocale, renderEmpty, direction } = React.useContext(
@ -208,12 +210,13 @@ function Table<RecordType extends object = any>(props: TableProps<RecordType>) {
false, false,
); );
}; };
const [transformSorterColumns, sortStates, sorterTitleProps, getSorters] = useSorter<RecordType>({ const [transformSorterColumns, sortStates, sorterTitleProps, getSorters] = useSorter<RecordType>({
prefixCls, prefixCls,
columns: columns || [], columns: columns || [],
onSorterChange, onSorterChange,
sortDirections: sortDirections || ['ascend', 'descend'], sortDirections: sortDirections || ['ascend', 'descend'],
tableLocale,
showSorterTooltip,
}); });
const sortedData = React.useMemo(() => getSortData(rawData, sortStates, childrenColumnName), [ const sortedData = React.useMemo(() => getSortData(rawData, sortStates, childrenColumnName), [
rawData, rawData,

View File

@ -150,6 +150,45 @@ describe('Table.sorter', () => {
expect(sorter3.columnKey).toBe('name'); expect(sorter3.columnKey).toBe('name');
}); });
it('hover header show sorter tooltip', () => {
// tooltip has delay
jest.useFakeTimers();
const wrapper = mount(createTable({}));
// default show sorter tooltip
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('.ant-tooltip-open').length).toBeTruthy();
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
// set table props showSorterTooltip is false
wrapper.setProps({ showSorterTooltip: false });
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('.ant-tooltip-open').length).toBeFalsy();
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
// set table props showSorterTooltip is false, column showSorterTooltip is true
wrapper.setProps({
showSorterTooltip: false,
columns: [{ ...column, showSorterTooltip: true }],
});
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('.ant-tooltip-open').length).toBeTruthy();
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
// set table props showSorterTooltip is true, column showSorterTooltip is false
wrapper.setProps({
showSorterTooltip: true,
columns: [{ ...column, showSorterTooltip: false }],
});
wrapper.find('.ant-table-column-sorters').simulate('mouseenter');
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('.ant-tooltip-open').length).toBeFalsy();
wrapper.find('.ant-table-column-sorters').simulate('mouseout');
});
it('works with grouping columns in controlled mode', () => { it('works with grouping columns in controlled mode', () => {
const columns = [ const columns = [
{ {

View File

@ -10,9 +10,14 @@ import {
CompareFn, CompareFn,
ColumnTitleProps, ColumnTitleProps,
SorterResult, SorterResult,
TableLocale,
} from '../interface'; } from '../interface';
import Tooltip from '../../tooltip';
import { getColumnKey, getColumnPos, renderColumnTitle } from '../util'; import { getColumnKey, getColumnPos, renderColumnTitle } from '../util';
const ASCEND = 'ascend';
const DESCEND = 'descend';
function getMultiplePriority<RecordType>(column: ColumnType<RecordType>): number | false { function getMultiplePriority<RecordType>(column: ColumnType<RecordType>): number | false {
if (typeof column.sorter === 'object' && typeof column.sorter.multiple === 'number') { if (typeof column.sorter === 'object' && typeof column.sorter.multiple === 'number') {
return column.sorter.multiple; return column.sorter.multiple;
@ -89,6 +94,8 @@ function injectSorter<RecordType>(
sorterSates: SortState<RecordType>[], sorterSates: SortState<RecordType>[],
triggerSorter: (sorterSates: SortState<RecordType>) => void, triggerSorter: (sorterSates: SortState<RecordType>) => void,
defaultSortDirections: SortOrder[], defaultSortDirections: SortOrder[],
tableLocale?: TableLocale,
tableShowSorterTooltip?: boolean,
pos?: string, pos?: string,
): ColumnsType<RecordType> { ): ColumnsType<RecordType> {
return (columns || []).map((column, index) => { return (columns || []).map((column, index) => {
@ -97,53 +104,69 @@ function injectSorter<RecordType>(
if (newColumn.sorter) { if (newColumn.sorter) {
const sortDirections: SortOrder[] = newColumn.sortDirections || defaultSortDirections; const sortDirections: SortOrder[] = newColumn.sortDirections || defaultSortDirections;
const showSorterTooltip =
newColumn.showSorterTooltip === undefined
? tableShowSorterTooltip
: newColumn.showSorterTooltip;
const columnKey = getColumnKey(newColumn, columnPos); const columnKey = getColumnKey(newColumn, columnPos);
const sorterState = sorterSates.find(({ key }) => key === columnKey); const sorterState = sorterSates.find(({ key }) => key === columnKey);
const sorterOrder = sorterState ? sorterState.sortOrder : null; const sorterOrder = sorterState ? sorterState.sortOrder : null;
const nextSortOrder = nextSortDirection(sortDirections, sorterOrder);
const upNode: React.ReactNode = sortDirections.includes('ascend') && ( const upNode: React.ReactNode = sortDirections.includes(ASCEND) && (
<CaretUpOutlined <CaretUpOutlined
className={classNames(`${prefixCls}-column-sorter-up`, { className={classNames(`${prefixCls}-column-sorter-up`, {
active: sorterOrder === 'ascend', active: sorterOrder === ASCEND,
})} })}
/> />
); );
const downNode: React.ReactNode = sortDirections.includes('descend') && ( const downNode: React.ReactNode = sortDirections.includes(DESCEND) && (
<CaretDownOutlined <CaretDownOutlined
className={classNames(`${prefixCls}-column-sorter-down`, { className={classNames(`${prefixCls}-column-sorter-down`, {
active: sorterOrder === 'descend', active: sorterOrder === DESCEND,
})} })}
/> />
); );
const { cancelSort, triggerAsc, triggerDesc } = tableLocale || {};
let sortTip: string | undefined = cancelSort;
if (nextSortOrder === DESCEND) {
sortTip = triggerDesc;
} else if (nextSortOrder === ASCEND) {
sortTip = triggerAsc;
}
newColumn = { newColumn = {
...newColumn, ...newColumn,
className: classNames(newColumn.className, { [`${prefixCls}-column-sort`]: sorterOrder }), className: classNames(newColumn.className, { [`${prefixCls}-column-sort`]: sorterOrder }),
title: (renderProps: ColumnTitleProps<RecordType>) => ( title: (renderProps: ColumnTitleProps<RecordType>) => {
<div className={`${prefixCls}-column-sorters`}> const renderSortTitle = (
<span>{renderColumnTitle(column.title, renderProps)}</span> <div className={`${prefixCls}-column-sorters`}>
<span <span>{renderColumnTitle(column.title, renderProps)}</span>
className={classNames(`${prefixCls}-column-sorter`, { <span
[`${prefixCls}-column-sorter-full`]: upNode && downNode, className={classNames(`${prefixCls}-column-sorter`, {
})} [`${prefixCls}-column-sorter-full`]: upNode && downNode,
> })}
<span className={`${prefixCls}-column-sorter-inner`}> >
{upNode} <span className={`${prefixCls}-column-sorter-inner`}>
{downNode} {upNode}
{downNode}
</span>
</span> </span>
</span> </div>
</div> );
), return showSorterTooltip ? (
<Tooltip title={sortTip}>{renderSortTitle}</Tooltip>
) : (
renderSortTitle
);
},
onHeaderCell: col => { onHeaderCell: col => {
const cell: React.HTMLAttributes<HTMLElement> = const cell: React.HTMLAttributes<HTMLElement> =
(column.onHeaderCell && column.onHeaderCell(col)) || {}; (column.onHeaderCell && column.onHeaderCell(col)) || {};
const originOnClick = cell.onClick; const originOnClick = cell.onClick;
cell.onClick = (event: React.MouseEvent<HTMLElement>) => { cell.onClick = (event: React.MouseEvent<HTMLElement>) => {
triggerSorter({ triggerSorter({
column, column,
key: columnKey, key: columnKey,
sortOrder: nextSortDirection(sortDirections, sorterOrder), sortOrder: nextSortOrder,
multiplePriority: getMultiplePriority(column), multiplePriority: getMultiplePriority(column),
}); });
@ -168,6 +191,8 @@ function injectSorter<RecordType>(
sorterSates, sorterSates,
triggerSorter, triggerSorter,
defaultSortDirections, defaultSortDirections,
tableLocale,
tableShowSorterTooltip,
columnPos, columnPos,
), ),
}; };
@ -238,7 +263,7 @@ export function getSortData<RecordType>(
const compareResult = compareFn(record1, record2, sortOrder); const compareResult = compareFn(record1, record2, sortOrder);
if (compareResult !== 0) { if (compareResult !== 0) {
return sortOrder === 'ascend' ? compareResult : -compareResult; return sortOrder === ASCEND ? compareResult : -compareResult;
} }
} }
} }
@ -265,6 +290,8 @@ interface SorterConfig<RecordType> {
sortStates: SortState<RecordType>[], sortStates: SortState<RecordType>[],
) => void; ) => void;
sortDirections: SortOrder[]; sortDirections: SortOrder[];
tableLocale?: TableLocale;
showSorterTooltip?: boolean;
} }
export default function useFilterSorter<RecordType>({ export default function useFilterSorter<RecordType>({
@ -272,6 +299,8 @@ export default function useFilterSorter<RecordType>({
columns, columns,
onSorterChange, onSorterChange,
sortDirections, sortDirections,
tableLocale,
showSorterTooltip,
}: SorterConfig<RecordType>): [ }: SorterConfig<RecordType>): [
TransformColumns<RecordType>, TransformColumns<RecordType>,
SortState<RecordType>[], SortState<RecordType>[],
@ -363,7 +392,15 @@ export default function useFilterSorter<RecordType>({
} }
const transformColumns = (innerColumns: ColumnsType<RecordType>) => const transformColumns = (innerColumns: ColumnsType<RecordType>) =>
injectSorter(prefixCls, innerColumns, mergedSorterStates, triggerSorter, sortDirections); injectSorter(
prefixCls,
innerColumns,
mergedSorterStates,
triggerSorter,
sortDirections,
tableLocale,
showSorterTooltip,
);
const getSorters = () => { const getSorters = () => {
return generateSorterInfo(mergedSorterStates); return generateSorterInfo(mergedSorterStates);

View File

@ -82,6 +82,7 @@ const columns = [
| onRow | Set props on per row | Function(record, index) | - | | onRow | Set props on per row | Function(record, index) | - |
| getPopupContainer | the render container of dropdowns in table | (triggerNode) => HTMLElement | `() => TableHtmlElement` | | getPopupContainer | the render container of dropdowns in table | (triggerNode) => HTMLElement | `() => TableHtmlElement` |
| sortDirections | supported sort way, could be `'ascend'`, `'descend'` | Array | `['ascend', 'descend']` | | sortDirections | supported sort way, could be `'ascend'`, `'descend'` | Array | `['ascend', 'descend']` |
| showSorterTooltip | header show next sorter direction tooltip | boolean | `true` |
#### onRow usage #### onRow usage
@ -138,6 +139,7 @@ One of the Table `columns` prop for describing the table's columns, Column has t
| onFilter | Callback executed when the confirm filter button is clicked | Function | - | | onFilter | Callback executed when the confirm filter button is clicked | Function | - |
| onFilterDropdownVisibleChange | Callback executed when `filterDropdownVisible` is changed | function(visible) {} | - | | onFilterDropdownVisibleChange | Callback executed when `filterDropdownVisible` is changed | function(visible) {} | - |
| onHeaderCell | Set props on per header cell | Function(column) | - | | onHeaderCell | Set props on per header cell | Function(column) | - |
| showSorterTooltip | header show next sorter direction tooltip, override `showSorterTooltip` in table | boolean | `true` |
### ColumnGroup ### ColumnGroup

View File

@ -87,6 +87,7 @@ const columns = [
| onRow | 设置行属性 | Function(record, index) | - | | onRow | 设置行属性 | Function(record, index) | - |
| getPopupContainer | 设置表格内各类浮层的渲染节点,如筛选菜单 | (triggerNode) => HTMLElement | `() => TableHtmlElement` | | getPopupContainer | 设置表格内各类浮层的渲染节点,如筛选菜单 | (triggerNode) => HTMLElement | `() => TableHtmlElement` |
| sortDirections | 支持的排序方式,取值为 `'ascend'` `'descend'` | Array | `['ascend', 'descend']` | | sortDirections | 支持的排序方式,取值为 `'ascend'` `'descend'` | Array | `['ascend', 'descend']` |
| showSorterTooltip | 表头是否显示下一次排序的 tooltip 提示 | boolean | `true` |
#### onRow 用法 #### onRow 用法
@ -143,6 +144,7 @@ const columns = [
| onFilter | 本地模式下,确定筛选的运行函数 | Function | - | | onFilter | 本地模式下,确定筛选的运行函数 | Function | - |
| onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用 | function(visible) {} | - | | onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用 | function(visible) {} | - |
| onHeaderCell | 设置头部单元格属性 | Function(column) | - | | onHeaderCell | 设置头部单元格属性 | Function(column) | - |
| showSorterTooltip | 表头显示下一次排序的 tooltip 提示, 覆盖 table 中`showSorterTooltip` | boolean | `true` |
### ColumnGroup ### ColumnGroup

View File

@ -23,6 +23,9 @@ export interface TableLocale {
sortTitle?: string; sortTitle?: string;
expand?: string; expand?: string;
collapse?: string; collapse?: string;
triggerDesc?: string;
triggerAsc?: string;
cancelSort?: string;
} }
export type SortOrder = 'descend' | 'ascend' | null; export type SortOrder = 'descend' | 'ascend' | null;
@ -74,6 +77,7 @@ export interface ColumnType<RecordType> extends RcColumnType<RecordType> {
sortOrder?: SortOrder; sortOrder?: SortOrder;
defaultSortOrder?: SortOrder; defaultSortOrder?: SortOrder;
sortDirections?: SortOrder[]; sortDirections?: SortOrder[];
showSorterTooltip?: boolean;
// Filter // Filter
filters?: ColumnFilterItem[]; filters?: ColumnFilterItem[];

View File

@ -188,6 +188,7 @@
// ============================ Sorter ============================ // ============================ Sorter ============================
thead th.@{table-prefix-cls}-column-has-sorters { thead th.@{table-prefix-cls}-column-has-sorters {
padding: 0;
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
@ -210,6 +211,8 @@
&-column-sorters { &-column-sorters {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
width: 100%;
padding: @table-padding-vertical @table-padding-horizontal;
} }
&-column-sorter { &-column-sorter {

View File

@ -9,3 +9,4 @@ import '../../checkbox/style';
import '../../dropdown/style'; import '../../dropdown/style';
import '../../spin/style'; import '../../spin/style';
import '../../pagination/style'; import '../../pagination/style';
import '../../tooltip/style';