Browse Source

feat: Table sorter support sortOrder parameter

shijia.me 1 year ago
parent
commit
fd15c4f37e

+ 130 - 1
content/show/table/index-en-US.md

@@ -1189,6 +1189,135 @@ function App() {
 render(App);
 ```
 
+When sorter is a function type, the sortOrder status can be obtained through the third parameter of the function. The function type is `(a?: RecordType, b?: RecordType, sortOrder?: 'ascend' | 'descend') => number`. Supported by version v2.47.
+
+```jsx live=true noInline=true dir="column"
+import React from 'react';
+import { Table, Avatar } from '@douyinfe/semi-ui';
+import * as dateFns from 'date-fns';
+
+function App() {
+    const columns = [
+        {
+            title: 'Title',
+            dataIndex: 'name',
+            width: 400,
+            render: (text, record, index) => {
+                return (
+                    <div>
+                        <Avatar size="small" shape="square" src={figmaIconUrl} style={{ marginRight: 12 }}></Avatar>
+                        {text}
+                    </div>
+                );
+            }
+        },
+        {
+            title: 'Size',
+            dataIndex: 'size',
+            sorter: (r1, r2, order) => {
+                const a = r1.size;
+                const b = r2.size;
+                if (typeof a === "number" && typeof b === "number") {
+                    return a - b;
+                } else if (typeof a === "undefined") {
+                    return order === "ascend" ? 1 : -1;
+                } else if (typeof b === "undefined") {
+                    return order === "ascend" ? -1 : 1;
+                } else {
+                    return 0;
+                }
+            },
+            render: text => text ? `${text} KB` : 'Unknown',
+        },
+        {
+            title: 'Owner',
+            dataIndex: 'owner',
+            render: (text, record, index) => {
+                return (
+                    <div>
+                        <Avatar size="small" color={record.avatarBg} style={{ marginRight: 4 }}>
+                            {typeof text === 'string' && text.slice(0, 1)}
+                        </Avatar>
+                        {text}
+                    </div>
+                );
+            },
+        },
+        {
+            title: 'Update',
+            dataIndex: 'updateTime',
+            render: value => {
+                return dateFns.format(new Date(value), 'yyyy-MM-dd');
+            },
+        },
+    ];
+
+    const figmaIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/figma-icon.png';
+    const docIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png';
+
+    const dataSource = [
+        {
+            key: '1',
+            name: 'Semi Design draft.fig',
+            nameIconSrc: figmaIconUrl,
+            size: 3,
+            owner: 'Jiang',
+            updateTime: '2020-02-02 05:13',
+            avatarBg: 'grey',
+        },
+        {
+            key: '2',
+            name: 'Semi D2C draft',
+            nameIconSrc: docIconUrl,
+            size: undefined,
+            owner: 'Hao',
+            updateTime: '2020-01-17 05:31',
+            avatarBg: 'red',
+        },
+        {
+            key: '3',
+            name: 'Semi D2C doc 3',
+            nameIconSrc: docIconUrl,
+            size: 1,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '4',
+            name: 'Semi Design doc 4',
+            nameIconSrc: docIconUrl,
+            size: 5,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '5',
+            name: 'Semi D2C doc 5',
+            nameIconSrc: docIconUrl,
+            size: undefined,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '6',
+            name: 'Semi Design doc 6',
+            nameIconSrc: docIconUrl,
+            size: 2,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+    ];
+
+    return <Table columns={columns} dataSource={dataSource} />;
+}
+
+render(App);
+```
+
 ### Custom Filter Item Rendering
 
 Since the **1.1.0** version, it is supported to pass in `renderFilterDropdownItem` to customize the rendering method of each filter item.
@@ -5015,7 +5144,7 @@ import { Table } from '@douyinfe/semi-ui';
 | resize | Whether to enable resize mode, this property will take effect only after Table resizable is enabled | boolean |  | **2.42.0** |
 | sortChildrenRecord | Whether to sort child data locally | boolean |  | **0.29.0** |
 | sortOrder | The controlled property of the sorting, the sorting of this control column can be set to 'ascend'\|'descended '\|false | boolean | false |
-| sorter | Sorting function, local sorting uses a function (refer to the compreFunction of Array.sort), requiring a server-side sorting can be set to true | boolean\|(r1: RecordType, r2: RecordType) => number | true |
+| sorter | Sorting function, local sorting uses a function (refer to the compareFunction of Array.sort), requiring a server-side sorting can be set to true | boolean\|(r1: RecordType, r2: RecordType, sortOrder: 'ascend' \| 'descend') => number | true |
 | title | Column header displays text. When a function is passed in, title will use the return value of the function; when other types are passed in, they will be aggregated with sorter and filter. It needs to be used with useFullRender to obtain parameters such as filter in the function type | string \| ReactNode\|({ filter: ReactNode, sorter: ReactNode, selection: ReactNode }) => ReactNode. |  | Function type requires **0.34.0** |
 | useFullRender | Whether to completely customize the rendering, see [Full Custom Rendering](#Fully-custom-rendering) for usage details, enabling this feature will cause a certain performance loss | boolean | false | **0.34.0** |
 | width | Column width | string \| number |  |

+ 132 - 1
content/show/table/index.md

@@ -1195,6 +1195,137 @@ function App() {
 render(App);
 ```
 
+sorter 为函数类型时,可以通过函数的第三个参数获取 sortOrder 状态。函数类型为 `(a?: RecordType, b?: RecordType, sortOrder?: 'ascend' | 'descend') => number`。v2.47 版本支持。
+
+```jsx live=true noInline=true dir="column"
+import React from 'react';
+import { Table, Avatar } from '@douyinfe/semi-ui';
+import * as dateFns from 'date-fns';
+
+function App() {
+    const columns = [
+        {
+            title: '标题',
+            dataIndex: 'name',
+            width: 400,
+            render: (text, record, index) => {
+                return (
+                    <div>
+                        <Avatar size="small" shape="square" src={figmaIconUrl} style={{ marginRight: 12 }}></Avatar>
+                        {text}
+                    </div>
+                );
+            }
+        },
+        {
+            title: '大小',
+            dataIndex: 'size',
+            sorter: (r1, r2, order) => {
+                const a = r1.size;
+                const b = r2.size;
+                if (typeof a === "number" && typeof b === "number") {
+                    return a - b; // 数字正常比较大小
+                } else if (typeof a === "undefined") {
+                    return order === "ascend" ? 1 : -1; // undefined 在后面
+                } else if (typeof b === "undefined") {
+                    return order === "ascend" ? -1 : 1; // undefined 在后面
+                } else {
+                    return 0; // 保持原来的顺序
+                }
+            },
+            render: text => text ? `${text} KB` : '未知',
+        },
+        {
+            title: '所有者',
+            dataIndex: 'owner',
+            render: (text, record, index) => {
+                return (
+                    <div>
+                        <Avatar size="small" color={record.avatarBg} style={{ marginRight: 4 }}>
+                            {typeof text === 'string' && text.slice(0, 1)}
+                        </Avatar>
+                        {text}
+                    </div>
+                );
+            },
+        },
+        {
+            title: '更新日期',
+            dataIndex: 'updateTime',
+            render: value => {
+                return dateFns.format(new Date(value), 'yyyy-MM-dd');
+            },
+        },
+    ];
+
+    const figmaIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/figma-icon.png';
+    const docIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png';
+
+    const dataSource = [
+        {
+            key: '1',
+            name: 'Semi Design 设计稿.fig',
+            nameIconSrc: figmaIconUrl,
+            size: 3,
+            owner: '姜鹏志',
+            updateTime: '2020-02-02 05:13',
+            avatarBg: 'grey',
+        },
+        {
+            key: '2',
+            name: 'Semi Design 分享演示文稿',
+            nameIconSrc: docIconUrl,
+            size: undefined,
+            owner: '郝宣',
+            updateTime: '2020-01-17 05:31',
+            avatarBg: 'red',
+        },
+        {
+            key: '3',
+            name: '设计文档3',
+            nameIconSrc: docIconUrl,
+            size: 1,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '4',
+            name: '设计文档4',
+            nameIconSrc: docIconUrl,
+            size: 5,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '5',
+            name: '设计文档5',
+            nameIconSrc: docIconUrl,
+            size: undefined,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '6',
+            name: '设计文档6',
+            nameIconSrc: docIconUrl,
+            size: 2,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+    ];
+
+    return <Table columns={columns} dataSource={dataSource} />;
+}
+
+render(App);
+```
+
+
+
 ### 自定义筛选项渲染
 
 自 **1.1.0** 版本后,支持往 column 中传入 `renderFilterDropdownItem` 自定义每个筛选项的渲染方式。
@@ -5029,7 +5160,7 @@ import { Table } from '@douyinfe/semi-ui';
 | resize | 是否开启 resize 模式,只有 Table resizable 开启后此属性才会生效 | boolean |  | **2.42.0** |
 | sortChildrenRecord | 是否对子级数据进行本地排序 | boolean |  | **0.29.0** |
 | sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 'ascend'\|'descend'\|false | boolean\| string | false |
-| sorter | 排序函数,本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 true | boolean\|(r1: RecordType, r2: RecordType) => number | true |
+| sorter | 排序函数,本地排序使用一个函数(参考 Array.sort 的 compareFunction),需要服务端排序可设为 true | boolean\|(r1: RecordType, r2: RecordType, sortOrder: 'ascend' \| 'descend') => number | true |
 | title | 列头显示文字。传入 function 时,title 将使用函数的返回值;传入其他类型,将会和 sorter、filter 进行聚合。需要搭配 useFullRender 获取函数类型中的 filter 等参数 | ReactNode\|({ filter: ReactNode, sorter: ReactNode, selection: ReactNode }) => ReactNode |  | Function 类型需要**0.34.0** |
 | useFullRender | 是否完全自定义渲染,用法详见[完全自定义渲染](#完全自定义渲染), 开启此功能会造成一定的性能损耗 | boolean | false | **0.34.0** |
 | width | 列宽度 | string \| number |  |

+ 16 - 1
cypress/e2e/table.spec.js

@@ -190,5 +190,20 @@ describe('table', () => {
         cy.visit('http://localhost:6006/iframe.html?id=table--resizable-table&viewMode=story');
         cy.get('.test-1').should('have.length', 1);
         cy.get('.test-2').should('have.length', 1);
-    })
+    });
+
+    it('test sorter sortOrder parameter', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=table--sorter-sort-order&viewMode=story', {
+            onBeforeLoad(win) {
+                cy.stub(win.console, 'log').as('consoleLog'); // 测试时用到控制台的前置步骤
+            },
+        });
+
+        cy.get('.semi-table-column-sorter').eq(0).click();
+        cy.get('.semi-table-tbody .semi-table-row').eq(4).should('contain.text', '未知');
+        cy.get('.semi-table-tbody .semi-table-row').eq(5).should('contain.text', '未知');
+        cy.get('.semi-table-column-sorter').eq(0).click();
+        cy.get('.semi-table-tbody .semi-table-row').eq(4).should('contain.text', '未知');
+        cy.get('.semi-table-tbody .semi-table-row').eq(5).should('contain.text', '未知');
+    });
 });

+ 1 - 1
packages/semi-foundation/table/foundation.ts

@@ -1202,7 +1202,7 @@ export interface BaseSorterInfo<RecordType> {
     sorter?: BaseSorter<RecordType>
 }
 export type BaseSortOrder = boolean | ArrayElement<typeof strings.SORT_DIRECTIONS>;
-export type BaseSorter<RecordType> = boolean | ((a?: RecordType, b?: RecordType) => number);
+export type BaseSorter<RecordType> = boolean | ((a?: RecordType, b?: RecordType, sortOrder?: 'ascend' | 'descend') => number);
 export interface BaseChangeInfoFilter<RecordType> {
     dataIndex?: string;
     value?: any;

+ 4 - 4
packages/semi-foundation/utils/array.ts

@@ -21,24 +21,24 @@ export function pullAll(arrayA: any[], arrayB: any[]) {
     }
     return arrayA;
 }
-type CompareFn<T> = (a: T, b: T) => number;
+type CompareFn<T> = (a: T, b: T, sortOrder: 'ascend' | 'descend') => number;
 /**
  * Adapt the descending order
  * @param {Function} fn
  * @param {String} order
  * @returns
  */
-export function withOrderSort<T = any>(fn: CompareFn<T>, order = 'ascend'): CompareFn<T> {
+export function withOrderSort<T = any>(fn: CompareFn<T>, order: 'ascend' | 'descend' = 'ascend'): (a: T, b: T) => number {
     switch (order) {
         case 'descend':
             return (
                 (a: any, b: any) => {
-                    const result = Number(fn(a, b));
+                    const result = Number(fn(a, b, order));
                     return result !== 0 ? -result : result;
                 }
             );
         case 'ascend':
         default:
-            return fn;
+            return (a: any, b: any) => fn(a, b, order);
     }
 }

+ 2 - 1
packages/semi-ui/table/_story/table.stories.jsx

@@ -101,7 +101,8 @@ export {
     FixedColumnAlign,
     FixOnChange,
     ColumnResize,
-    FixedResizableRowSelection
+    FixedResizableRowSelection,
+    SorterSortOrder
 } from './v2';
 export { default as FixSelectAll325 } from './Demos/rowSelection';
 

+ 126 - 0
packages/semi-ui/table/_story/v2/SorterSortOrder/index.tsx

@@ -0,0 +1,126 @@
+import React from 'react';
+import { Table, Avatar } from '@douyinfe/semi-ui';
+import * as dateFns from 'date-fns';
+
+/**
+ * test with cypress
+ */
+export default function App() {
+    const columns = [
+        {
+            title: '标题',
+            dataIndex: 'name',
+            width: 400,
+            render: (text, record, index) => {
+                return (
+                    <div>
+                        <Avatar size="small" shape="square" src={figmaIconUrl} style={{ marginRight: 12 }}></Avatar>
+                        {text}
+                    </div>
+                );
+            }
+        },
+        {
+            title: '大小',
+            dataIndex: 'size',
+            sorter: (r1, r2, order) => {
+                console.log('order', order);
+                const a = r1.size;
+                const b = r2.size;
+                if (typeof a === "number" && typeof b === "number") {
+                    return a - b; // 数字大的在前面
+                } else if (typeof a === "undefined") {
+                    return order === "ascend" ? 1 : -1; // undefined 在后面
+                } else if (typeof b === "undefined") {
+                    return order === "ascend" ? -1 : 1; // undefined 在后面
+                } else {
+                    return 0; // 保持原来的顺序
+                }
+            },
+            render: text => text ? `${text} KB` : '未知',
+        },
+        {
+            title: '所有者',
+            dataIndex: 'owner',
+            render: (text, record, index) => {
+                return (
+                    <div>
+                        <Avatar size="small" color={record.avatarBg} style={{ marginRight: 4 }}>
+                            {typeof text === 'string' && text.slice(0, 1)}
+                        </Avatar>
+                        {text}
+                    </div>
+                );
+            },
+        },
+        {
+            title: '更新日期',
+            dataIndex: 'updateTime',
+            render: value => {
+                return dateFns.format(new Date(value), 'yyyy-MM-dd');
+            },
+        },
+    ];
+
+    const figmaIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/figma-icon.png';
+    const docIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/docs-icon.png';
+
+    const dataSource = [
+        {
+            key: '1',
+            name: 'Semi Design 设计稿.fig',
+            nameIconSrc: figmaIconUrl,
+            size: 3,
+            owner: '姜鹏志',
+            updateTime: '2020-02-02 05:13',
+            avatarBg: 'grey',
+        },
+        {
+            key: '2',
+            name: 'Semi Design 分享演示文稿',
+            nameIconSrc: docIconUrl,
+            size: undefined,
+            owner: '郝宣',
+            updateTime: '2020-01-17 05:31',
+            avatarBg: 'red',
+        },
+        {
+            key: '3',
+            name: '设计文档3',
+            nameIconSrc: docIconUrl,
+            size: 1,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '4',
+            name: '设计文档4',
+            nameIconSrc: docIconUrl,
+            size: 5,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '5',
+            name: '设计文档5',
+            nameIconSrc: docIconUrl,
+            size: undefined,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+        {
+            key: '6',
+            name: '设计文档6',
+            nameIconSrc: docIconUrl,
+            size: 2,
+            owner: 'Zoey Edwards',
+            updateTime: '2020-01-26 11:01',
+            avatarBg: 'light-blue',
+        },
+    ];
+
+    return <Table columns={columns} dataSource={dataSource} />;
+}

+ 1 - 0
packages/semi-ui/table/_story/v2/index.js

@@ -22,3 +22,4 @@ export { default as FixedColumnAlign } from './FixedColumnAlign';
 export { default as FixOnChange } from './FixOnChange';
 export { default as ColumnResize } from './ColumnResize';
 export { default as FixedResizableRowSelection } from './FixedResizableRowSelection';
+export { default as SorterSortOrder } from './SorterSortOrder';