From 376326a11566e031e431defe0eda0203104cc363 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 23 Oct 2024 15:19:39 +0800 Subject: [PATCH 01/16] =?UTF-8?q?fix:=20=E5=BC=80=E5=90=AF=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E8=A1=8C=E5=88=97=E5=A4=B4=E6=97=B6,=20?= =?UTF-8?q?=E4=B8=8D=E5=BA=94=E8=AF=A5=E6=B8=B2=E6=9F=93=E6=80=BB=E8=AE=A1?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=92=8C=E7=BB=84=E5=86=85=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=20icon=20closes=20#2898=20#2893?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/spreadsheet/custom-grid-spec.ts | 52 +++++++++++++++++- packages/s2-core/src/cell/header-cell.ts | 5 ++ .../src/facet/layout/build-gird-hierarchy.ts | 18 +++--- .../facet/layout/build-row-tree-hierarchy.ts | 2 +- .../playground/components/CustomGrid.tsx | 4 -- s2-site/docs/common/totals.zh.md | 4 +- .../advanced/custom/custom-header.zh.md | 12 +++- s2-site/docs/manual/basic/sort/group.zh.md | 5 +- s2-site/docs/manual/basic/totals.zh.md | 55 ++----------------- .../examples/analysis/totals/demo/custom.ts | 3 +- 10 files changed, 91 insertions(+), 69 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts index d28cd7025a..e528671c23 100644 --- a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts @@ -254,6 +254,19 @@ describe('SpreadSheet Custom Grid Tests', () => { s2.facet.getRowNodes().every((node) => node.isCollapsed), ).toBeTruthy(); }); + + // https://github.com/antvis/S2/issues/2898 + test('should not render sort action icon for custom row header', async () => { + s2.setOptions({ + showDefaultHeaderActionIcon: true, + }); + + await s2.render(false); + + s2.facet.getRowCells().forEach((cell) => { + expect(cell.getActionIcons()).toHaveLength(0); + }); + }); }); describe('Custom Col Grid Tests', () => { @@ -276,7 +289,7 @@ describe('SpreadSheet Custom Grid Tests', () => { }); afterEach(() => { - s2.destroy(); + // s2.destroy(); }); test('should enable valueInCols', () => { @@ -538,5 +551,42 @@ describe('SpreadSheet Custom Grid Tests', () => { s2.facet.getColNodes().some((node) => node.isCollapsed), ).toBeFalsy(); }); + + // https://github.com/antvis/S2/issues/2893 + test.each(['tree', 'grid'])( + 'should not render total node for %s mode', + async (hierarchyType) => { + s2.setOptions({ + hierarchyType, + totals: { + col: { + showGrandTotals: true, + reverseGrandTotalsLayout: true, + }, + row: { + showGrandTotals: true, + reverseGrandTotalsLayout: true, + }, + }, + }); + + await s2.render(false); + + expect(s2.facet.getColGrandTotalsNodes()).toHaveLength(0); + expect(s2.facet.getRowGrandTotalsNodes()).toHaveLength(0); + }, + ); + + test('should not render sort action icon for custom col header', async () => { + s2.setOptions({ + showDefaultHeaderActionIcon: true, + }); + + await s2.render(false); + + s2.facet.getColCells().forEach((cell) => { + expect(cell.getActionIcons()).toHaveLength(0); + }); + }); }); }); diff --git a/packages/s2-core/src/cell/header-cell.ts b/packages/s2-core/src/cell/header-cell.ts index 442175309f..3f55bf083f 100644 --- a/packages/s2-core/src/cell/header-cell.ts +++ b/packages/s2-core/src/cell/header-cell.ts @@ -335,6 +335,11 @@ export abstract class HeaderCell< } protected isSortCell() { + // https://github.com/antvis/S2/issues/2898 + if (this.meta.extra?.isCustomNode) { + return false; + } + // 数值置于列头, 排序 icon 绘制在列头叶子节点; 置于行头, 排序 icon 绘制在行头叶子节点 const isValueInCols = this.spreadsheet?.isValueInCols?.(); const isMaxLevel = this.meta.level === this.meta.hierarchy?.maxLevel; diff --git a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts index 87734069f8..fe4f090cb1 100644 --- a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts @@ -114,14 +114,16 @@ const buildNormalGridHierarchy = (params: GridHeaderParams) => { } } - // add totals if needed - addTotals({ - currentField, - lastField: fields[index - 1], - isFirstField: index === 0, - fieldValues, - spreadsheet, - }); + // https://github.com/antvis/S2/issues/2893 + if (!spreadsheet.isCustomHeaderFields()) { + addTotals({ + currentField, + lastField: fields[index - 1], + isFirstField: index === 0, + fieldValues, + spreadsheet, + }); + } const displayFieldValues = filterOutDetail(fieldValues as string[]); diff --git a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts index 8595623d7a..c7f11255a8 100644 --- a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts @@ -61,7 +61,7 @@ export const buildRowTreeHierarchy = (params: TreeHeaderParams) => { fieldValues = fieldValues.slice(0, drillItemsNum); } - if (level === 0) { + if (level === 0 && !spreadsheet.isCustomHeaderFields()) { addTotals(spreadsheet, currentField, fieldValues); } diff --git a/packages/s2-react/playground/components/CustomGrid.tsx b/packages/s2-react/playground/components/CustomGrid.tsx index 65efe662ff..38a2ef6d7f 100644 --- a/packages/s2-react/playground/components/CustomGrid.tsx +++ b/packages/s2-react/playground/components/CustomGrid.tsx @@ -78,7 +78,6 @@ export const pivotSheetCustomColGridDataCfg: S2DataConfig = { enum CustomType { Row = 'row', Col = 'col', - All = 'all', } type CustomGridProps = Partial; @@ -122,9 +121,6 @@ export const CustomGrid = React.forwardRef( > 自定义行头 自定义列头 - - TODO: 自定义行头和列头 - diff --git a/s2-site/docs/manual/basic/totals.zh.md b/s2-site/docs/manual/basic/totals.zh.md index 8f51a1ae2c..2cf22535b4 100644 --- a/s2-site/docs/manual/basic/totals.zh.md +++ b/s2-site/docs/manual/basic/totals.zh.md @@ -5,7 +5,7 @@ order: 5 ## 简介 -小计总计属于表的透视功能,可以给行头和列头分别配置小计总计。 +小计总计属于表的透视功能,可以给行头和列头分别配置汇总能力,展示小计总计,开启 [自定义行列头](/manual/advanced/custom/custom-header) 时,汇总能力无效。 ### 小计 @@ -81,53 +81,7 @@ order: 5 配置 [S2Options](/docs/api/general/S2Options#total) 的 `totals` 属性来实现是否展示行列小计总计以及显示位置,类型如下: -#### Totals - -功能描述: 行/列小计总计配置 - -| 参数 | 说明 | 类型 | 默认值 | 必选 | -| ---- | ------ | --------------------------------------------- | ------ | ---- | -| row | 列总计 | [Total](/docs/api/general/S2Options#total) | - | | -| col | 行总计 | [Total](/docs/api/general/S2Options#total) | - | | - -#### Total - -功能描述:小计总计算配置 - -| 参数 | 说明 | 类型 | 默认值 | 必选 | -| ------------------- | ------------------------ | ------------ | ------ | ---- | -| showGrandTotals | 是否显示总计 | `boolean` | false | | -| showSubTotals | 是否显示小计。配置为对象时,`always` 用于控制当子维度小于 2 个时是否始终展示小计,默认展示 | `boolean \| { always: boolean }` | false | | -| subTotalsDimensions | 小计的汇总维度 | `string[]` | [] | | -| reverseGrandTotalsLayout | 总计布局位置,默认下或右 | `boolean` | false | | -| reverseSubTotalsLayout | 小计布局位置,默认下或右 | `boolean` | false | | -| grandTotalsLabel | 总计别名 | `string` | `总计` | | -| subTotalsLabel | 小计别名 | `string` | `小计` | | -| calcGrandTotals | 计算总计 | `CalcTotals` | | | -| calcSubTotals | 计算小计 | `CalcTotals` | | | - -```ts -const s2Options = { - totals: { - row: { - showGrandTotals: true, - showSubTotals: true, - reverseGrandTotalsLayout: true, - reverseSubTotalsLayout: true, - subTotalsDimensions: ['province'], - grandTotalsGroupDimensions: ['city'], - subTotalsGroupDimensions: ['type', 'sub_type'], - }, - col: { - showGrandTotals: true, - showSubTotals: true, - reverseGrandTotalsLayout: true, - reverseSubTotalsLayout: true, - subTotalsDimensions: ['type'], - }, - }, -}; -``` + ### 2. 数据 @@ -269,11 +223,12 @@ const s2Options = { 注意:`data` 为明细数据,如需获取包含汇总的数据 -```ts +```ts | pure import { QueryDataType } from '@antv/s2'; const calcFunc = (query, data, spreadsheet) => { - const allData = spreadsheet.dataSet.getMultiData(query, { + const allData = spreadsheet.dataSet.getMultiData({ + query, queryType: QueryDataType.All, }); diff --git a/s2-site/examples/analysis/totals/demo/custom.ts b/s2-site/examples/analysis/totals/demo/custom.ts index 1d014e0dbc..86b8686d7f 100644 --- a/s2-site/examples/analysis/totals/demo/custom.ts +++ b/s2-site/examples/analysis/totals/demo/custom.ts @@ -39,7 +39,8 @@ fetch('https://render.alipay.com/p/yuyan/180020010001215413/s2/basic.json') }; const calcFunc: CalcTotals['calcFunc'] = (query, data, spreadsheet) => { - const allData = spreadsheet.dataSet.getMultiData(query, { + const allData = spreadsheet.dataSet.getCellMultiData({ + query, queryType: QueryDataType.All, }); From d5ceb098997142ce5d472611433ded56ac7a9a2d Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 23 Oct 2024 15:34:40 +0800 Subject: [PATCH 02/16] =?UTF-8?q?docs:=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- s2-site/docs/manual/basic/totals.zh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/s2-site/docs/manual/basic/totals.zh.md b/s2-site/docs/manual/basic/totals.zh.md index 2cf22535b4..ff8eb68636 100644 --- a/s2-site/docs/manual/basic/totals.zh.md +++ b/s2-site/docs/manual/basic/totals.zh.md @@ -227,7 +227,7 @@ const s2Options = { import { QueryDataType } from '@antv/s2'; const calcFunc = (query, data, spreadsheet) => { - const allData = spreadsheet.dataSet.getMultiData({ + const allData = spreadsheet.dataSet.getCellMultiData({ query, queryType: QueryDataType.All, }); From ed3848bf696c4dff1aa55e723149e6a2249ecd74 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Wed, 23 Oct 2024 17:07:24 +0800 Subject: [PATCH 03/16] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils/export/__snapshots__/copy-spec.ts.snap | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap b/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap index 94d5443b32..9386cf636d 100644 --- a/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap +++ b/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap @@ -363,14 +363,14 @@ exports[`Tree Table Core Data Process should copy all data in tree mode 1`] = ` `; exports[`Tree Table Core Data Process should copy all data in tree mode for custom row cell 1`] = ` -"家具 家具 家具 总计 -桌子 椅子 小计 -- - - - -- - - - -13 11 - - -2 8 - - -- - - - -- - - -" +"家具 家具 +桌子 椅子 +- - +- - +13 11 +2 8 +- - +- -" `; exports[`Tree Table Core Data Process should copy all data in tree mode with format 1`] = ` From 0df176c25f351e542765c6f87e9fe9e6db56ae5d Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Fri, 25 Oct 2024 10:49:06 +0800 Subject: [PATCH 04/16] =?UTF-8?q?fix:=20=E6=98=8E=E7=BB=86=E8=A1=A8?= =?UTF-8?q?=E9=9D=9E=E5=8F=B6=E5=AD=90=E8=8A=82=E7=82=B9=E4=B8=8D=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=20sort=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/spreadsheet/custom-table-col-spec.ts | 12 ++++++++++++ packages/s2-core/src/cell/header-cell.ts | 13 ++++++++----- packages/s2-core/src/cell/table-col-cell.ts | 11 ++++++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts index f1ebace5cc..44cc3eaa04 100644 --- a/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts @@ -308,4 +308,16 @@ describe('TableSheet Custom Tests', () => { expect(resizeAreaList.length).toEqual(8); }); + + test('should only render sort action icon in value cell for custom col header', async () => { + s2.setOptions({ + showDefaultHeaderActionIcon: true, + }); + + await s2.render(false); + + s2.facet.getColCells().forEach((cell) => { + expect(cell.getActionIcons()).toHaveLength(1); + }); + }); }); diff --git a/packages/s2-core/src/cell/header-cell.ts b/packages/s2-core/src/cell/header-cell.ts index 3f55bf083f..4075157950 100644 --- a/packages/s2-core/src/cell/header-cell.ts +++ b/packages/s2-core/src/cell/header-cell.ts @@ -182,6 +182,14 @@ export abstract class HeaderCell< const { options, dataCfg } = this.spreadsheet; const isEmptyValues = isEmpty(dataCfg.fields.values); + /** + * TODO: 自定义行列头支持组内排序 + * https://github.com/antvis/S2/issues/2898 + */ + if (this.meta.extra?.isCustomNode) { + return false; + } + if (options.showDefaultHeaderActionIcon && !isEmptyValues) { const { sortParam } = this.getHeaderConfig(); const query = this.meta.query; @@ -335,11 +343,6 @@ export abstract class HeaderCell< } protected isSortCell() { - // https://github.com/antvis/S2/issues/2898 - if (this.meta.extra?.isCustomNode) { - return false; - } - // 数值置于列头, 排序 icon 绘制在列头叶子节点; 置于行头, 排序 icon 绘制在行头叶子节点 const isValueInCols = this.spreadsheet?.isValueInCols?.(); const isMaxLevel = this.meta.level === this.meta.hierarchy?.maxLevel; diff --git a/packages/s2-core/src/cell/table-col-cell.ts b/packages/s2-core/src/cell/table-col-cell.ts index a907be4bec..584c568110 100644 --- a/packages/s2-core/src/cell/table-col-cell.ts +++ b/packages/s2-core/src/cell/table-col-cell.ts @@ -116,7 +116,16 @@ export class TableColCell extends ColCell { } protected showSortIcon() { - return this.spreadsheet.options.showDefaultHeaderActionIcon; + const { extra } = this.meta; + const { options } = this.spreadsheet; + const { showDefaultHeaderActionIcon } = options; + + if (!extra?.isCustomNode) { + return showDefaultHeaderActionIcon; + } + + // 自定义列头时, 只在叶子节点展示 + return showDefaultHeaderActionIcon && this.meta.isLeaf; } protected getTextStyle() { From c95572d70665bbb7738c537b9179d1d7750aeba6 Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Fri, 25 Oct 2024 11:57:37 +0800 Subject: [PATCH 05/16] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spreadsheet/custom-table-col-spec.ts | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts index 44cc3eaa04..9c84fffe1b 100644 --- a/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/custom-table-col-spec.ts @@ -309,15 +309,28 @@ describe('TableSheet Custom Tests', () => { expect(resizeAreaList.length).toEqual(8); }); - test('should only render sort action icon in value cell for custom col header', async () => { - s2.setOptions({ - showDefaultHeaderActionIcon: true, - }); - - await s2.render(false); - - s2.facet.getColCells().forEach((cell) => { - expect(cell.getActionIcons()).toHaveLength(1); - }); - }); + test.each([ + { showDefaultHeaderActionIcon: false }, + { showDefaultHeaderActionIcon: true }, + ])( + 'should render correctly sort action icon in value cell for custom col header with %o', + async (options) => { + s2.setOptions(options); + + await s2.render(false); + + const fields = s2.facet + .getColCells() + .filter((cell) => { + return cell.getActionIcons().length >= 1; + }) + .map((cell) => cell.getMeta().field); + + expect(fields).toEqual( + options.showDefaultHeaderActionIcon + ? ['province', 'city', 'type', 'price', 'number'] + : [], + ); + }, + ); }); From fe4afaf71729df1c29e8196770fee4a2b77eac00 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Fri, 25 Oct 2024 17:39:38 +0800 Subject: [PATCH 06/16] =?UTF-8?q?fix:=20=E5=85=BC=E5=AE=B9=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=B8=83=E5=B1=80=E6=97=B6=20dataset=20?= =?UTF-8?q?=E5=B1=82=E5=AF=B9=E8=B1=A1=E7=B1=BB=E5=9E=8B=20field=20?= =?UTF-8?q?=E7=9A=84=E5=AE=9A=E4=BD=8D=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data-set/custom-tree-pivot-data-set.ts | 21 +++-- .../s2-core/src/data-set/pivot-data-set.ts | 79 +++++++++++-------- .../src/utils/dataset/pivot-data-set.ts | 20 +++-- packages/s2-core/src/utils/sort-action.ts | 6 +- 4 files changed, 71 insertions(+), 55 deletions(-) diff --git a/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts b/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts index 229989b430..3dd13ae9fd 100644 --- a/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts +++ b/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts @@ -5,6 +5,7 @@ import type { Meta, S2DataConfig } from '../common/interface'; import { getDataPath, getDataPathPrefix, + getIndexFields, transformDimensionsValues, } from '../utils/dataset/pivot-data-set'; import { CellData } from './cell-data'; @@ -15,22 +16,20 @@ export class CustomTreePivotDataSet extends PivotDataSet { getCellData(params: GetCellDataParams) { const { query = {} } = params || {}; const { columns, rows } = this.fields; - const rowDimensionValues = transformDimensionsValues( - query, - rows as string[], - ); - const colDimensionValues = transformDimensionsValues( - query, - columns as string[], - ); + + const indexRows = getIndexFields(rows); + const indexColumns = getIndexFields(columns); + + const rowDimensionValues = transformDimensionsValues(query, indexRows); + const colDimensionValues = transformDimensionsValues(query, indexColumns); const path = getDataPath({ rowDimensionValues, colDimensionValues, rowPivotMeta: this.rowPivotMeta, colPivotMeta: this.colPivotMeta, - rowFields: rows as string[], - colFields: columns as string[], - prefix: getDataPathPrefix(rows as string[], columns as string[]), + rowFields: indexRows, + colFields: indexColumns, + prefix: getDataPathPrefix(indexRows, indexColumns), }); const rawData = get(this.indexesData, path as PropertyPath); diff --git a/packages/s2-core/src/data-set/pivot-data-set.ts b/packages/s2-core/src/data-set/pivot-data-set.ts index 240e691945..5907f76b5f 100644 --- a/packages/s2-core/src/data-set/pivot-data-set.ts +++ b/packages/s2-core/src/data-set/pivot-data-set.ts @@ -54,6 +54,7 @@ import { getDataPathPrefix, getExistValues, getFlattenDimensionValues, + getIndexFields, getSatisfiedPivotMetaValues, isMultiValue, transformDimensionsValues, @@ -104,22 +105,21 @@ export class PivotDataSet extends BaseDataSet { this.rowPivotMeta = new Map(); this.colPivotMeta = new Map(); this.dimensionValuesCache = new Map(); - this.transformIndexesData(this.originData, rows as string[]); + this.transformIndexesData(this.originData, rows); this.handleDimensionValuesSort(); } public transformIndexesData( data: RawData[], - rows: string[], + rows?: CustomHeaderFields, ): TransformResult { const { columns, values, valueInCols } = this.fields; - let result!: TransformResult; DebuggerUtil.getInstance().debugCallback(DEBUG_TRANSFORM_DATA, () => { result = transformIndexesData({ - rows: rows as string[], - columns: columns as string[], + rows: getIndexFields(rows), + columns: getIndexFields(columns), values: values!, valueInCols: valueInCols!, data, @@ -349,14 +349,14 @@ export class PivotDataSet extends BaseDataSet { if (rows.includes(field)) { return { - dimensions: rows as string[], + dimensions: getIndexFields(rows), pivotMeta: this.rowPivotMeta, }; } if (columns.includes(field)) { return { - dimensions: columns as string[], + dimensions: getIndexFields(columns), pivotMeta: this.colPivotMeta, }; } @@ -444,7 +444,7 @@ export class PivotDataSet extends BaseDataSet { const { query = {}, rowNode, isTotals = false, totalStatus } = params || {}; const { rows: originRows, columns } = this.fields; - let rows = originRows as string[]; + let rows = originRows; const drillDownIdPathMap = this.spreadsheet?.store.get('drillDownIdPathMap'); @@ -458,23 +458,23 @@ export class PivotDataSet extends BaseDataSet { // 如果是下钻结点,行维度在 originRows 中并不存在 if (rowNode && isDrillDown) { - rows = - Node.getFieldPath(rowNode, isDrillDown) ?? (originRows as string[]); + rows = Node.getFieldPath(rowNode, isDrillDown) ?? originRows; } - const rowDimensionValues = transformDimensionsValues(query, rows); - const colDimensionValues = transformDimensionsValues( - query, - columns as string[], - ); + const indexRows = getIndexFields(rows); + const indexColumns = getIndexFields(columns); + + const rowDimensionValues = transformDimensionsValues(query, indexRows); + const colDimensionValues = transformDimensionsValues(query, indexColumns); + const path = getDataPath({ rowDimensionValues, colDimensionValues, rowPivotMeta: this.rowPivotMeta, colPivotMeta: this.colPivotMeta, - rowFields: rows as string[], - colFields: columns as string[], - prefix: getDataPathPrefix(rows as string[], columns as string[]), + rowFields: indexRows, + colFields: indexColumns, + prefix: getDataPathPrefix(indexRows, indexColumns), }); const rawData = get(this.indexesData, path as PropertyPath); @@ -491,21 +491,27 @@ export class PivotDataSet extends BaseDataSet { public getTotalStatus = (query: Query): TotalStatus => { const { columns, rows } = this.fields; - const isTotals = (dimensions: string[], isSubTotal?: boolean) => { + const isTotals = ( + dimensions?: CustomHeaderFields, + isSubTotal?: boolean, + ) => { if (isSubTotal) { - const firstDimension = find(dimensions, (item) => !has(query, item)); + const firstDimension = find( + dimensions, + (item) => !has(query, item as string), + ); return !!(firstDimension && firstDimension !== first(dimensions)); } - return every(dimensions, (item) => !has(query, item)); + return every(dimensions, (item) => !has(query, item as string)); }; return { - isRowGrandTotal: isTotals(filterExtraDimension(rows as string[])), - isRowSubTotal: isTotals(rows as string[], true), - isColGrandTotal: isTotals(filterExtraDimension(columns as string[])), - isColSubTotal: isTotals(columns as string[], true), + isRowGrandTotal: isTotals(filterExtraDimension(rows)), + isRowSubTotal: isTotals(rows, true), + isColGrandTotal: isTotals(filterExtraDimension(columns)), + isColSubTotal: isTotals(columns, true), }; }; @@ -536,18 +542,21 @@ export class PivotDataSet extends BaseDataSet { } const { rows, columns } = this.fields; - const totalRows: string[] = !isEmpty(drillDownFields) - ? (rows as string[]).concat(drillDownFields!) - : (rows as string[]); + const totalRows = !isEmpty(drillDownFields) + ? rows!.concat(drillDownFields!) + : rows!; + + const indexRows = getIndexFields(totalRows); + const indexColumns = getIndexFields(columns); const rowDimensionValues = transformDimensionsValues( query, - totalRows, + indexRows, MULTI_VALUE, ); const colDimensionValues = transformDimensionsValues( query, - columns as string[], + indexColumns, MULTI_VALUE, ); @@ -556,13 +565,13 @@ export class PivotDataSet extends BaseDataSet { colDimensionValues, rowPivotMeta: this.rowPivotMeta, colPivotMeta: this.colPivotMeta, - rowFields: totalRows, - colFields: columns as string[], + rowFields: indexRows, + colFields: indexColumns, sortedDimensionValues: this.sortedDimensionValues, queryType, }); - const prefix = getDataPathPrefix(totalRows, columns as string[]); + const prefix = getDataPathPrefix(indexRows, indexColumns); const all: RawData[] = []; for (const rowQuery of rowQueries) { @@ -572,8 +581,8 @@ export class PivotDataSet extends BaseDataSet { colDimensionValues: colQuery, rowPivotMeta: this.rowPivotMeta, colPivotMeta: this.colPivotMeta, - rowFields: totalRows, - colFields: columns as string[], + rowFields: indexRows, + colFields: indexColumns, prefix, }); diff --git a/packages/s2-core/src/utils/dataset/pivot-data-set.ts b/packages/s2-core/src/utils/dataset/pivot-data-set.ts index 015a9d2864..022eb8c7aa 100644 --- a/packages/s2-core/src/utils/dataset/pivot-data-set.ts +++ b/packages/s2-core/src/utils/dataset/pivot-data-set.ts @@ -8,10 +8,11 @@ import { isArray, isEmpty, isNull, + isString, last, set, } from 'lodash'; -import type { RawData } from '../../common'; +import type { CustomHeaderFields, RawData } from '../../common'; import { EMPTY_EXTRA_FIELD_PLACEHOLDER, EXTRA_FIELD, @@ -37,7 +38,7 @@ import type { import type { Node } from '../../facet/layout/node'; import { generateNillString } from '../layout/generate-id'; -export function filterExtraDimension(dimensions: string[] = []) { +export function filterExtraDimension(dimensions: CustomHeaderFields = []) { return dimensions.filter((d) => d !== EXTRA_FIELD); } @@ -89,7 +90,7 @@ export function getExistValues(data: RawData, values: string[]) { return result; } -export function transformDimensionsValuesWithExtraFields( +function transformDimensionsValuesWithExtraFields( record: RawData = {}, dimensions: string[] = [], values: string[] | null, @@ -293,13 +294,20 @@ export interface TransformResult { sortedDimensionValues: SortedDimensionValues; } +/** + * 获取用于数据 transform 中定位的 string 的字段,自定义布局中,自定义字段是 object 类型,这些类型不应该参与到数据处理的流程中 + */ +export function getIndexFields(fields: CustomHeaderFields = []) { + return fields.filter(isString); +} + /** * 转换原始数据为二维数组数据 */ export function transformIndexesData(params: Param): TransformResult { const { - rows, - columns, + rows = [], + columns = [], values, valueInCols, data = [], @@ -337,7 +345,7 @@ export function transformIndexesData(params: Param): TransformResult { ).push(dimensionPath); }; - const prefix = getDataPathPrefix(rows, columns as string[]); + const prefix = getDataPathPrefix(rows, columns); data.forEach((item: RawData) => { // 空数据没有意义,直接跳过 diff --git a/packages/s2-core/src/utils/sort-action.ts b/packages/s2-core/src/utils/sort-action.ts index 2d48cf58ef..01056bd039 100644 --- a/packages/s2-core/src/utils/sort-action.ts +++ b/packages/s2-core/src/utils/sort-action.ts @@ -320,17 +320,17 @@ export const getSortByMeasureValues = ( const isSortFieldInRow = includes(fields.rows, sortFieldId); // 排序字段所在一侧的全部字段 const sortFields = filterExtraDimension( - (isSortFieldInRow ? fields.rows : columns) as string[], + isSortFieldInRow ? fields.rows : columns, ); // 与排序交叉的另一侧全部字段 const oppositeFields = filterExtraDimension( - (isSortFieldInRow ? columns : fields.rows) as string[], + isSortFieldInRow ? columns : fields.rows, ); const fieldAfterSortField = sortFields[sortFields.indexOf(sortFieldId) + 1]; const queryKeys = keys(query); const missedOppositeFields = oppositeFields.filter( - (field) => !queryKeys.includes(field), + (field) => !queryKeys.includes(field as string), ); const totalDataList = dataList.filter((dataItem) => { From ced4d5c49e17fa70573512176cdbc6cc63a45e27 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Fri, 25 Oct 2024 17:46:20 +0800 Subject: [PATCH 07/16] fix: lint fix --- packages/s2-core/src/utils/sort-action.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/s2-core/src/utils/sort-action.ts b/packages/s2-core/src/utils/sort-action.ts index 01056bd039..171b00ceec 100644 --- a/packages/s2-core/src/utils/sort-action.ts +++ b/packages/s2-core/src/utils/sort-action.ts @@ -345,7 +345,7 @@ export const getSortByMeasureValues = ( return false; } - if (dataItemKeys.has(fieldAfterSortField)) { + if (dataItemKeys.has(fieldAfterSortField as string)) { /* * 若排序数据包含`排序字段`的后一个维度字段,则过滤 * 不需要比排序字段更 “明细” 的数据,只需取到 sortFieldId 当级的汇总 @@ -359,7 +359,7 @@ export const getSortByMeasureValues = ( * 如 query={ type: 'xx',EXTRA_FIELD=price },代表了最高可以取到 type 的小计汇总数据 */ const allMissed = missedOppositeFields.every( - (missedField) => !dataItemKeys.has(missedField), + (missedField) => !dataItemKeys.has(missedField as string), ); // 返回符合要求的汇总数据 From 99bbd4646195b7a0a1492082d90b9c5834ec08d0 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Fri, 25 Oct 2024 18:06:08 +0800 Subject: [PATCH 08/16] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20todo=20?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/src/cell/header-cell.ts | 8 -------- .../src/facet/layout/build-gird-hierarchy.ts | 17 +++++++---------- .../facet/layout/build-row-tree-hierarchy.ts | 2 +- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/packages/s2-core/src/cell/header-cell.ts b/packages/s2-core/src/cell/header-cell.ts index 4075157950..442175309f 100644 --- a/packages/s2-core/src/cell/header-cell.ts +++ b/packages/s2-core/src/cell/header-cell.ts @@ -182,14 +182,6 @@ export abstract class HeaderCell< const { options, dataCfg } = this.spreadsheet; const isEmptyValues = isEmpty(dataCfg.fields.values); - /** - * TODO: 自定义行列头支持组内排序 - * https://github.com/antvis/S2/issues/2898 - */ - if (this.meta.extra?.isCustomNode) { - return false; - } - if (options.showDefaultHeaderActionIcon && !isEmptyValues) { const { sortParam } = this.getHeaderConfig(); const query = this.meta.query; diff --git a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts index fe4f090cb1..53f32bf23c 100644 --- a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts @@ -114,16 +114,13 @@ const buildNormalGridHierarchy = (params: GridHeaderParams) => { } } - // https://github.com/antvis/S2/issues/2893 - if (!spreadsheet.isCustomHeaderFields()) { - addTotals({ - currentField, - lastField: fields[index - 1], - isFirstField: index === 0, - fieldValues, - spreadsheet, - }); - } + addTotals({ + currentField, + lastField: fields[index - 1], + isFirstField: index === 0, + fieldValues, + spreadsheet, + }); const displayFieldValues = filterOutDetail(fieldValues as string[]); diff --git a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts index c7f11255a8..8595623d7a 100644 --- a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts @@ -61,7 +61,7 @@ export const buildRowTreeHierarchy = (params: TreeHeaderParams) => { fieldValues = fieldValues.slice(0, drillItemsNum); } - if (level === 0 && !spreadsheet.isCustomHeaderFields()) { + if (level === 0) { addTotals(spreadsheet, currentField, fieldValues); } From 81ffb0d2a80e3dbcbcd7a483f13919564fcce380 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Mon, 28 Oct 2024 11:37:07 +0800 Subject: [PATCH 09/16] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E8=A1=8C=E5=88=97=E5=A4=B4=E5=AF=B9=E6=80=BB?= =?UTF-8?q?=E8=AE=A1=E5=B0=8F=E8=AE=A1=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data-set/custom-tree-pivot-data-set.ts | 35 ----------- .../s2-core/src/sheet-type/pivot-sheet.ts | 4 +- .../playground/components/CustomGrid.tsx | 58 ++++++++++++++++++- 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts b/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts index 3dd13ae9fd..9ce3a4b853 100644 --- a/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts +++ b/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts @@ -1,44 +1,9 @@ -import { get, type PropertyPath } from 'lodash'; import { EXTRA_FIELD } from '../common/constant'; import { i18n } from '../common/i18n'; import type { Meta, S2DataConfig } from '../common/interface'; -import { - getDataPath, - getDataPathPrefix, - getIndexFields, - transformDimensionsValues, -} from '../utils/dataset/pivot-data-set'; -import { CellData } from './cell-data'; -import type { GetCellDataParams } from './interface'; import { PivotDataSet } from './pivot-data-set'; export class CustomTreePivotDataSet extends PivotDataSet { - getCellData(params: GetCellDataParams) { - const { query = {} } = params || {}; - const { columns, rows } = this.fields; - - const indexRows = getIndexFields(rows); - const indexColumns = getIndexFields(columns); - - const rowDimensionValues = transformDimensionsValues(query, indexRows); - const colDimensionValues = transformDimensionsValues(query, indexColumns); - const path = getDataPath({ - rowDimensionValues, - colDimensionValues, - rowPivotMeta: this.rowPivotMeta, - colPivotMeta: this.colPivotMeta, - rowFields: indexRows, - colFields: indexColumns, - prefix: getDataPathPrefix(indexRows, indexColumns), - }); - - const rawData = get(this.indexesData, path as PropertyPath); - - if (rawData) { - return CellData.getCellData(rawData, query[EXTRA_FIELD]); - } - } - processDataCfg(dataCfg: S2DataConfig): S2DataConfig { /** * 自定义行头有如下几个特点 diff --git a/packages/s2-core/src/sheet-type/pivot-sheet.ts b/packages/s2-core/src/sheet-type/pivot-sheet.ts index a9ffcd66ef..804a5b94f3 100644 --- a/packages/s2-core/src/sheet-type/pivot-sheet.ts +++ b/packages/s2-core/src/sheet-type/pivot-sheet.ts @@ -144,10 +144,10 @@ export class PivotSheet extends SpreadSheet { const { rows, columns } = this.dataCfg.fields; const { hideValue } = this.options.style!.colCell!; const sortField = this.isValueInCols() ? last(rows) : last(columns); - const { query, value } = meta; + const { query, field, value, extra } = meta; const sortQuery = clone(query); - let sortValue = value; + let sortValue = extra?.isCustomNode ? field : value; // 数值置于列头且隐藏了指标列头的情况, 会默认取第一个指标做组内排序, 需要还原指标列的 query, 所以多指标时请不要这么用…… if (hideValue && this.isValueInCols()) { diff --git a/packages/s2-react/playground/components/CustomGrid.tsx b/packages/s2-react/playground/components/CustomGrid.tsx index 38a2ef6d7f..0f01c42a2d 100644 --- a/packages/s2-react/playground/components/CustomGrid.tsx +++ b/packages/s2-react/playground/components/CustomGrid.tsx @@ -1,5 +1,10 @@ /* eslint-disable no-console */ -import type { S2DataConfig, SpreadSheet, ThemeCfg } from '@antv/s2'; +import { + Aggregation, + type S2DataConfig, + type SpreadSheet, + type ThemeCfg, +} from '@antv/s2'; import { customColGridFields, customRowGridFields, @@ -154,6 +159,57 @@ export const CustomGrid = React.forwardRef( setSheetType(checked ? 'pivot' : 'table'); }} /> + + { + setOptions({ + totals: { + row: { + showGrandTotals: checked, + showSubTotals: checked, + reverseGrandTotalsLayout: true, + reverseSubTotalsLayout: true, + subTotalsDimensions: ['province'], + calcGrandTotals: { + aggregation: Aggregation.SUM, + }, + calcSubTotals: { + aggregation: Aggregation.SUM, + }, + }, + }, + }); + }} + /> + { + setOptions({ + totals: { + col: { + showGrandTotals: checked, + showSubTotals: checked, + reverseGrandTotalsLayout: true, + reverseSubTotalsLayout: true, + subTotalsDimensions: ['type'], + calcGrandTotals: { + aggregation: Aggregation.SUM, + }, + calcSubTotals: { + aggregation: Aggregation.SUM, + }, + }, + }, + }); + }} + /> Date: Mon, 28 Oct 2024 11:48:41 +0800 Subject: [PATCH 10/16] =?UTF-8?q?docs:=20=E6=96=87=E6=A1=A3=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- s2-site/docs/common/totals.zh.md | 4 ++-- s2-site/docs/manual/advanced/custom/custom-header.zh.md | 3 +-- s2-site/docs/manual/basic/sort/group.zh.md | 1 - s2-site/docs/manual/basic/totals.zh.md | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/s2-site/docs/common/totals.zh.md b/s2-site/docs/common/totals.zh.md index d5637937d5..8cd304edab 100644 --- a/s2-site/docs/common/totals.zh.md +++ b/s2-site/docs/common/totals.zh.md @@ -9,8 +9,8 @@ order: 3 | 参数 | 说明 | 类型 | 必选 | 默认值 | | ---- | ------ | --------------------------------------------- | ---- | ------ | -| row | 列总计(在 [自定义行列头时](/manual/advanced/custom/custom-header) 无效) | [Total](#total) | - | | -| col | 行总计(在 [自定义行列头时](/manual/advanced/custom/custom-header) 无效) | [Total](#total) | - | | +| row | 行总计配置(在 [自定义行头时](/manual/advanced/custom/custom-header#11-自定义行头) 无效) | [Total](#total) | - | | +| col | 列总计配置(在 [自定义列头时](/manual/advanced/custom/custom-header#12-自定义列头) 无效) | [Total](#total) | - | | ## Total diff --git a/s2-site/docs/manual/advanced/custom/custom-header.zh.md b/s2-site/docs/manual/advanced/custom/custom-header.zh.md index 01640b26bc..db1316024a 100644 --- a/s2-site/docs/manual/advanced/custom/custom-header.zh.md +++ b/s2-site/docs/manual/advanced/custom/custom-header.zh.md @@ -13,8 +13,7 @@ tag: New :::warning{title="注意"} 1. 默认的排序 icon `showDefaultHeaderActionIcon` 无效。 -2. 不支持 [组内排序](/manual/basic/sort/group), 相关 API 无效。 -3. 不支持 [小计总计](/manual/basic/totals), 且 [自定义汇总](/manual/basic/totals#2-%E6%95%B0%E6%8D%AE) 中无法获取到明细数据。 +2. 不支持自定义所在 header 的[小计总计](/manual/basic/totals)。 ::: diff --git a/s2-site/docs/manual/basic/sort/group.zh.md b/s2-site/docs/manual/basic/sort/group.zh.md index af62313b12..75ce2df535 100644 --- a/s2-site/docs/manual/basic/sort/group.zh.md +++ b/s2-site/docs/manual/basic/sort/group.zh.md @@ -11,7 +11,6 @@ tag: Updated :::warning{title="注意"} 1. `行头/列头` 只存在单一状态,当前状态会**覆盖前一状态**,如上图所示,当对 `笔` 进行排序时,`纸张` 的排序状态消失,`行头 + 列头` 可同时存在自身状态。 -2. 在开启 [自定义行列头时](/manual/advanced/custom/custom-header) 组内排序无效。 ::: diff --git a/s2-site/docs/manual/basic/totals.zh.md b/s2-site/docs/manual/basic/totals.zh.md index ff8eb68636..c8d3e47f81 100644 --- a/s2-site/docs/manual/basic/totals.zh.md +++ b/s2-site/docs/manual/basic/totals.zh.md @@ -5,7 +5,7 @@ order: 5 ## 简介 -小计总计属于表的透视功能,可以给行头和列头分别配置汇总能力,展示小计总计,开启 [自定义行列头](/manual/advanced/custom/custom-header) 时,汇总能力无效。 +小计总计属于表的透视功能,可以给行头和列头分别配置汇总能力,展示小计总计,开启 [自定义行列头](/manual/advanced/custom/custom-header) 时,对应行列头的汇总能力无效。 ### 小计 From f29aea27087fdeb509dbcf2a07007bbf66cded8e Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Mon, 28 Oct 2024 15:42:29 +0800 Subject: [PATCH 11/16] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20CustomTr?= =?UTF-8?q?eeDataSet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/spreadsheet/custom-tree-spec.ts | 332 ------------------ ...t-spec.ts => custom-grid-data-set-spec.ts} | 20 +- .../data-set/custom-grid-pivot-data-set.ts | 8 +- .../data-set/custom-tree-pivot-data-set.ts | 28 -- packages/s2-core/src/data-set/index.ts | 9 +- .../sheets/strategy-sheet/custom-data-set.ts | 4 +- packages/s2-react/src/hooks/useSpreadSheet.ts | 2 +- packages/s2-vue/src/hooks/useSheetUpdate.ts | 2 +- .../demo/custom-strategy-sheet-dataset.ts | 4 +- 9 files changed, 24 insertions(+), 385 deletions(-) delete mode 100644 packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts rename packages/s2-core/__tests__/unit/data-set/{custom-tree-data-set-spec.ts => custom-grid-data-set-spec.ts} (85%) delete mode 100644 packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts diff --git a/packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts deleted file mode 100644 index 2c93df3010..0000000000 --- a/packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts +++ /dev/null @@ -1,332 +0,0 @@ -import type { S2DataConfig, S2Options } from '@/common/interface'; -import { PivotSheet, SpreadSheet } from '@/sheet-type'; -import { getContainer } from 'tests/util/helpers'; -import { CustomTreePivotDataSet } from '../../src'; -import type { HeaderCell } from '../../src/cell/header-cell'; -import { customRowGridSimpleFields } from '../data/custom-grid-simple-fields'; -import { customTreeNodes } from '../data/custom-tree-nodes'; -import { CustomGridData } from '../data/data-custom-grid'; -import { - expectHighlightActiveNodes, - getSelectedCount, - getSelectedSum, - getTestTooltipData, -} from '../util/interaction'; - -const s2Options: S2Options = { - width: 600, - height: 400, - hierarchyType: 'tree', - style: { - rowCell: { - width: 400, - }, - }, -}; - -describe('SpreadSheet Custom Tree Tests', () => { - let s2: SpreadSheet; - - const getCornerCellLabels = () => { - return s2.facet.getCornerCells().map((cell) => cell.getActualText()); - }; - - const mapRowNodes = (spreadsheet: SpreadSheet) => - spreadsheet.facet.getRowLeafNodes().map((node) => { - const iconName = (node.belongsCell as HeaderCell).getTreeIcon()?.config - .name; - - return { - id: node.id, - value: node.value, - isCollapsed: node.isCollapsed, - iconName, - }; - }); - - const customRowDataCfg: S2DataConfig = { - data: CustomGridData, - meta: [ - { - field: 'type', - name: '类型', - }, - { - field: 'sub_type', - name: '子类型', - }, - { - field: 'a-1', - name: '层级1', - }, - { - field: 'a-2', - name: '层级2', - }, - ], - fields: customRowGridSimpleFields, - }; - - beforeEach(async () => { - s2 = new PivotSheet(getContainer(), customRowDataCfg, s2Options); - await s2.render(); - }); - - afterEach(() => { - s2.destroy(); - }); - - test('should disable valueInCols', () => { - expect(s2.dataCfg.fields.valueInCols).toBeFalsy(); - expect(s2.dataSet.fields.valueInCols).toBeFalsy(); - }); - - test('should use custom tree pivot dataSet', () => { - expect(s2.dataSet).toBeInstanceOf(CustomTreePivotDataSet); - }); - - test('should get correctly dataset fields', () => { - expect(s2.dataSet.fields).toMatchSnapshot(); - }); - - test('should render custom layout row nodes', () => { - const rowNodes = s2.facet.getRowNodes().map((node) => { - return { - value: node.value, - width: node.width, - height: node.height, - description: node.extra?.['description'], - }; - }); - - expect(rowNodes).toMatchSnapshot(); - }); - - test('should calc correctly row index of leaf nodes', () => { - const rowLeafNodes = s2.facet.getRowLeafNodes().map((node) => { - return { - value: node.value, - rowIndex: node.rowIndex, - }; - }); - - expect(rowLeafNodes).toMatchSnapshot(); - }); - - test('should select custom row header cell', () => { - // a-1 - const rowNode = s2.facet.getRowNodes()[0]; - - // 选中 a-1 - s2.interaction.changeCell({ - cell: rowNode.belongsCell!, - }); - - // 选中单元格本身 - expect(s2.interaction.getActiveCells()).toHaveLength(1); - // 高亮子节点 - expectHighlightActiveNodes(s2, ['root[&]a-1']); - - // 取消选中 a-1 - s2.interaction.changeCell({ - cell: rowNode.belongsCell!, - }); - expect(s2.interaction.getActiveCells()).toBeEmpty(); - }); - - test.each([ - { field: 'a-1', count: 2, sum: null }, - { field: 'a-1-1', count: 2, sum: null }, - { field: 'measure-1', count: 2, sum: 24 }, - { field: 'measure-2', count: 2, sum: 10 }, - { field: 'a-1-2', count: 2, sum: null }, - { field: 'a-1-2', count: 2, sum: null }, - ])( - 'should get selected cell summary infos for %o', - ({ field, count, sum }) => { - const rowNode = s2.facet - .getRowNodes() - .find((node) => node.field === field)!; - - // 选中 - s2.interaction.changeCell({ - cell: rowNode.belongsCell!, - }); - - const tooltipData = getTestTooltipData(s2, rowNode.belongsCell!); - - // 选中个数 - expect(getSelectedCount(tooltipData.summaries)).toEqual(count); - // 汇总数据总和 - expect(getSelectedSum(tooltipData.summaries)).toEqual(sum); - }, - ); - - test('should render custom corner text by default title', async () => { - s2.setDataCfg({ - meta: [], - }); - - await s2.render(true); - - const cornerCellLabels = getCornerCellLabels(); - - expect(cornerCellLabels).toEqual([ - '自定义节点 a-1/自定义节点 a-2/数值', - 'type', - ]); - }); - - test('should render custom corner text by meta formatter', async () => { - s2.setDataCfg({ - meta: [ - { - field: 'a-1', - name: '文本1', - }, - { - field: 'a-2', - name: '文本2', - }, - ], - }); - - await s2.render(true); - const cornerCellLabels = getCornerCellLabels(); - - expect(cornerCellLabels).toEqual(['文本1/文本2/数值', 'type']); - }); - - test('should render custom corner text by cornerText options', async () => { - s2.setOptions({ - cornerText: '测试', - }); - - await s2.render(); - - const cornerCellLabels = getCornerCellLabels(); - - expect(cornerCellLabels).toEqual(['测试', '类型']); - }); - - test('should render custom tree row node width', async () => { - s2.setOptions({ - style: { - rowCell: { - width: 50, - }, - }, - }); - - await s2.render(); - - const { rowNodes, rowsHierarchy } = s2.facet.getLayoutResult(); - - expect(rowsHierarchy.width).toEqual(50); - expect(rowNodes.every((node) => node.width === 50)).toBeTruthy(); - }); - - test('should collapse node by collapsed', async () => { - s2.setDataCfg({ - fields: { - rows: customTreeNodes.map((node) => { - return { - ...node, - collapsed: true, - }; - }), - }, - }); - await s2.render(true); - - expect(mapRowNodes(s2)).toMatchSnapshot(); - }); - - test('should collapse node by collapsed fields id', async () => { - s2.setOptions({ - style: { - rowCell: { - collapseFields: { - 'root[&]自定义节点 a-1': true, - 'root[&]自定义节点 a-2': false, - }, - }, - }, - }); - await s2.render(true); - - expect(mapRowNodes(s2)).toMatchSnapshot(); - }); - - test('should collapse node by collapsed field', async () => { - s2.setOptions({ - style: { - rowCell: { - collapseFields: { 'a-1-1': true, 'a-1-2': false }, - }, - }, - }); - await s2.render(true); - - expect(mapRowNodes(s2)).toMatchSnapshot(); - }); - - test('should collapse node by user collapseFields first', async () => { - const collapsedField = 'custom-node-1'; - - s2.setDataCfg({ - fields: { - rows: customTreeNodes.map((node) => { - return { - ...node, - collapsed: node.field !== collapsedField, - }; - }), - }, - }); - - s2.setOptions({ - style: { - rowCell: { - collapseFields: { - [collapsedField]: true, - }, - }, - }, - }); - await s2.render(true); - - expect(mapRowNodes(s2)).toMatchSnapshot(); - }); - - // https://github.com/antvis/S2/issues/2455 - test('should only collapse first node by node id', async () => { - const collapsedField = 'custom-node-1'; - - s2.setDataCfg({ - fields: { - rows: customTreeNodes.map((node) => { - return { - ...node, - // 让两个节点名一样 - title: '自定义节点A', - collapsed: false, - }; - }), - }, - }); - - s2.setOptions({ - style: { - rowCell: { - collapseFields: { - [collapsedField]: true, - }, - }, - }, - }); - await s2.render(true); - - expect(mapRowNodes(s2)).toMatchSnapshot(); - }); -}); diff --git a/packages/s2-core/__tests__/unit/data-set/custom-tree-data-set-spec.ts b/packages/s2-core/__tests__/unit/data-set/custom-grid-data-set-spec.ts similarity index 85% rename from packages/s2-core/__tests__/unit/data-set/custom-tree-data-set-spec.ts rename to packages/s2-core/__tests__/unit/data-set/custom-grid-data-set-spec.ts index 4c7a8110f3..fe6cdf9f83 100644 --- a/packages/s2-core/__tests__/unit/data-set/custom-tree-data-set-spec.ts +++ b/packages/s2-core/__tests__/unit/data-set/custom-grid-data-set-spec.ts @@ -3,11 +3,12 @@ */ import { EXTRA_FIELD, ORIGIN_FIELD } from '@/common/constant'; import type { S2DataConfig } from '@/common/interface'; -import { CustomTreePivotDataSet } from '@/data-set/custom-tree-pivot-data-set'; +import { CustomGridPivotDataSet } from '@/data-set/custom-grid-pivot-data-set'; import { PivotSheet } from '@/sheet-type'; import { get } from 'lodash'; import { customTreeNodes } from 'tests/data/custom-tree-nodes'; import { CustomTreeData } from 'tests/data/data-custom-tree'; +import { Store } from '../../../src'; jest.mock('@/sheet-type'); @@ -15,7 +16,7 @@ jest.mock('@/interaction/root'); const MockPivotSheet = PivotSheet as unknown as jest.Mock; -describe('Custom Tree Dataset Test', () => { +describe('Custom Grid Dataset Test', () => { const values = [ 'measure-a', 'measure-b', @@ -35,14 +36,21 @@ describe('Custom Tree Dataset Test', () => { }, }; - const mockSheet = new MockPivotSheet(); - const dataSet = new CustomTreePivotDataSet(mockSheet); + let dataSet: CustomGridPivotDataSet; - dataSet.setDataCfg(dataCfg); + beforeEach(() => { + const mockSheet = new MockPivotSheet(); + + mockSheet.isCustomRowFields = () => true; + mockSheet.store = new Store(); + + dataSet = new CustomGridPivotDataSet(mockSheet); + dataSet.setDataCfg(dataCfg); + }); describe('test base dataset structure', () => { test('should get correct field data', () => { - expect(dataSet.fields.rows).toEqual([EXTRA_FIELD]); + expect(dataSet.fields.rows).toEqual([...customTreeNodes, EXTRA_FIELD]); expect(dataSet.fields.columns).toEqual(['type', 'sub_type']); expect(dataSet.fields.values).toEqual(values); }); diff --git a/packages/s2-core/src/data-set/custom-grid-pivot-data-set.ts b/packages/s2-core/src/data-set/custom-grid-pivot-data-set.ts index 2ae06d0b1b..7fcd12a3f5 100644 --- a/packages/s2-core/src/data-set/custom-grid-pivot-data-set.ts +++ b/packages/s2-core/src/data-set/custom-grid-pivot-data-set.ts @@ -1,14 +1,12 @@ import { EXTRA_FIELD, i18n } from '../common'; import type { S2DataConfig } from '../common/interface'; -import { CustomTreePivotDataSet } from './custom-tree-pivot-data-set'; +import { PivotDataSet } from './pivot-data-set'; -export class CustomGridPivotDataSet extends CustomTreePivotDataSet { +export class CustomGridPivotDataSet extends PivotDataSet { processDataCfg(dataCfg: S2DataConfig): S2DataConfig { const valueInCols = !this.spreadsheet.isCustomRowFields(); const originalRows = dataCfg.fields.rows || []; - const rows = valueInCols - ? [...originalRows] - : [...originalRows, EXTRA_FIELD]; + const rows = valueInCols ? originalRows : [...originalRows, EXTRA_FIELD]; const meta = this.processMeta(dataCfg.meta!, i18n('数值')); return { diff --git a/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts b/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts deleted file mode 100644 index 9ce3a4b853..0000000000 --- a/packages/s2-core/src/data-set/custom-tree-pivot-data-set.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { EXTRA_FIELD } from '../common/constant'; -import { i18n } from '../common/i18n'; -import type { Meta, S2DataConfig } from '../common/interface'; -import { PivotDataSet } from './pivot-data-set'; - -export class CustomTreePivotDataSet extends PivotDataSet { - processDataCfg(dataCfg: S2DataConfig): S2DataConfig { - /** - * 自定义行头有如下几个特点 - * 1、rows配置必须是空,需要额外添加 $$extra$$ 定位数据(标记指标的id) - * 2、要有配置 fields.rowCustomTree(行头结构) - * 3、values 不需要参与计算,默认就在行头结构中 - */ - - const updatedDataCfg = super.processDataCfg(dataCfg); - const newMeta: Meta[] = this.processMeta(dataCfg.meta, i18n('指标')); - - return { - ...updatedDataCfg, - meta: newMeta, - fields: { - ...updatedDataCfg.fields, - rows: [EXTRA_FIELD], - valueInCols: false, - }, - }; - } -} diff --git a/packages/s2-core/src/data-set/index.ts b/packages/s2-core/src/data-set/index.ts index f0ffc95172..1e498c7cd4 100644 --- a/packages/s2-core/src/data-set/index.ts +++ b/packages/s2-core/src/data-set/index.ts @@ -1,17 +1,10 @@ import { BaseDataSet } from './base-data-set'; import { CustomGridPivotDataSet } from './custom-grid-pivot-data-set'; -import { CustomTreePivotDataSet } from './custom-tree-pivot-data-set'; import { PivotDataSet } from './pivot-data-set'; import { TableDataSet } from './table-data-set'; export { CellData } from './cell-data'; -export { - BaseDataSet, - CustomGridPivotDataSet, - CustomTreePivotDataSet, - PivotDataSet, - TableDataSet, -}; +export { BaseDataSet, CustomGridPivotDataSet, PivotDataSet, TableDataSet }; export * from './interface'; diff --git a/packages/s2-react/src/components/sheets/strategy-sheet/custom-data-set.ts b/packages/s2-react/src/components/sheets/strategy-sheet/custom-data-set.ts index 169d4f0b12..f612ebda66 100644 --- a/packages/s2-react/src/components/sheets/strategy-sheet/custom-data-set.ts +++ b/packages/s2-react/src/components/sheets/strategy-sheet/custom-data-set.ts @@ -1,5 +1,5 @@ import { - CustomTreePivotDataSet, + CustomGridPivotDataSet, EMPTY_EXTRA_FIELD_PLACEHOLDER, EXTRA_FIELD, i18n, @@ -9,7 +9,7 @@ import { } from '@antv/s2'; import { isEmpty, isObject, keys, size } from 'lodash'; -export class StrategySheetDataSet extends CustomTreePivotDataSet { +export class StrategySheetDataSet extends CustomGridPivotDataSet { getExistValuesByDataItem(data: RawData) { const result = keys(data).filter((key) => isObject(data[key])); diff --git a/packages/s2-react/src/hooks/useSpreadSheet.ts b/packages/s2-react/src/hooks/useSpreadSheet.ts index 86c8a16d1e..275f347a9e 100644 --- a/packages/s2-react/src/hooks/useSpreadSheet.ts +++ b/packages/s2-react/src/hooks/useSpreadSheet.ts @@ -124,7 +124,7 @@ export function useSpreadSheet(props: SheetComponentProps) { if (!Object.is(prevOptions, options)) { if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) { - // 自定义树目录需要重新构建 CustomTreePivotDataSet + // 自定义树目录需要重新构建 CustomGridPivotDataSet rebuildDataSet = true; reloadData = true; s2Ref.current?.setDataCfg(dataCfg); diff --git a/packages/s2-vue/src/hooks/useSheetUpdate.ts b/packages/s2-vue/src/hooks/useSheetUpdate.ts index 4eca76ad10..cce38fce32 100644 --- a/packages/s2-vue/src/hooks/useSheetUpdate.ts +++ b/packages/s2-vue/src/hooks/useSheetUpdate.ts @@ -25,7 +25,7 @@ export const useSheetUpdate = ( updateFlag.rerender = true; if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) { - // 自定义树目录需要重新构建 CustomTreePivotDataSet + // 自定义树目录需要重新构建 CustomGridPivotDataSet updateFlag.reloadData = true; updateFlag.rebuildDataset = true; } diff --git a/s2-site/examples/custom/custom-dataset/demo/custom-strategy-sheet-dataset.ts b/s2-site/examples/custom/custom-dataset/demo/custom-strategy-sheet-dataset.ts index bb3db61674..4e0480edb1 100644 --- a/s2-site/examples/custom/custom-dataset/demo/custom-strategy-sheet-dataset.ts +++ b/s2-site/examples/custom/custom-dataset/demo/custom-strategy-sheet-dataset.ts @@ -1,7 +1,7 @@ /* eslint-disable no-console */ /* eslint-disable max-classes-per-file */ import { - CustomTreePivotDataSet, + CustomGridPivotDataSet, DataCell, EMPTY_EXTRA_FIELD_PLACEHOLDER, EXTRA_COLUMN_FIELD, @@ -18,7 +18,7 @@ import { } from '@antv/s2'; import { isEmpty, isObject, keys, size } from 'lodash'; -class CustomDataSet extends CustomTreePivotDataSet { +class CustomDataSet extends CustomGridPivotDataSet { // 自定义单个数据查询逻辑 getCellData(params: GetCellDataParams) { console.log('getCellData:', params); From b654d0a719c0aee82f0cf07544c752d93fac8a54 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Mon, 28 Oct 2024 15:44:33 +0800 Subject: [PATCH 12/16] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/spreadsheet/custom-grid-spec.ts | 48 ++++++++++++------- .../playground/components/CustomGrid.tsx | 2 +- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts index e528671c23..32b03998e3 100644 --- a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts @@ -5,7 +5,11 @@ import { pick } from 'lodash'; import { CustomGridData } from 'tests/data/data-custom-grid'; import { waitForRender } from 'tests/util'; import { getContainer } from 'tests/util/helpers'; -import { KEY_GROUP_COL_RESIZE_AREA } from '../../src/common/constant'; +import { + KEY_GROUP_COL_RESIZE_AREA, + VALUE_FIELD, +} from '../../src/common/constant'; +import { Aggregation } from '../../src/common/interface/basic'; import { CustomGridPivotDataSet } from '../../src/data-set/custom-grid-pivot-data-set'; import { customColGridSimpleFields, @@ -254,19 +258,6 @@ describe('SpreadSheet Custom Grid Tests', () => { s2.facet.getRowNodes().every((node) => node.isCollapsed), ).toBeTruthy(); }); - - // https://github.com/antvis/S2/issues/2898 - test('should not render sort action icon for custom row header', async () => { - s2.setOptions({ - showDefaultHeaderActionIcon: true, - }); - - await s2.render(false); - - s2.facet.getRowCells().forEach((cell) => { - expect(cell.getActionIcons()).toHaveLength(0); - }); - }); }); describe('Custom Col Grid Tests', () => { @@ -554,26 +545,47 @@ describe('SpreadSheet Custom Grid Tests', () => { // https://github.com/antvis/S2/issues/2893 test.each(['tree', 'grid'])( - 'should not render total node for %s mode', + 'should render correct total node for %s mode', async (hierarchyType) => { s2.setOptions({ hierarchyType, totals: { - col: { + row: { showGrandTotals: true, + showSubTotals: true, reverseGrandTotalsLayout: true, + reverseSubTotalsLayout: true, + subTotalsDimensions: ['type'], + calcGrandTotals: { + aggregation: Aggregation.SUM, + }, + calcSubTotals: { + aggregation: Aggregation.SUM, + }, }, - row: { + col: { showGrandTotals: true, + showSubTotals: true, reverseGrandTotalsLayout: true, + reverseSubTotalsLayout: true, + subTotalsDimensions: ['type'], + calcGrandTotals: { + aggregation: Aggregation.SUM, + }, + calcSubTotals: { + aggregation: Aggregation.SUM, + }, }, }, }); await s2.render(false); + expect(s2.facet.getRowGrandTotalsNodes()).toHaveLength(1); expect(s2.facet.getColGrandTotalsNodes()).toHaveLength(0); - expect(s2.facet.getRowGrandTotalsNodes()).toHaveLength(0); + + expect(s2.facet.getCellMeta(0, 0).data[VALUE_FIELD]).toEqual(24); + expect(s2.facet.getCellMeta(0, 1).data[VALUE_FIELD]).toEqual(10); }, ); diff --git a/packages/s2-react/playground/components/CustomGrid.tsx b/packages/s2-react/playground/components/CustomGrid.tsx index 0f01c42a2d..5951800dac 100644 --- a/packages/s2-react/playground/components/CustomGrid.tsx +++ b/packages/s2-react/playground/components/CustomGrid.tsx @@ -173,7 +173,7 @@ export const CustomGrid = React.forwardRef( showSubTotals: checked, reverseGrandTotalsLayout: true, reverseSubTotalsLayout: true, - subTotalsDimensions: ['province'], + subTotalsDimensions: ['type'], calcGrandTotals: { aggregation: Aggregation.SUM, }, From 7db85233caab4bfb5442fe0d1feb76d8996abaa7 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Mon, 28 Oct 2024 16:05:42 +0800 Subject: [PATCH 13/16] =?UTF-8?q?test:=20=E5=8D=95=E6=B5=8B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__snapshots__/custom-tree-spec.ts.snap | 259 ------------------ .../__tests__/spreadsheet/custom-grid-spec.ts | 12 - 2 files changed, 271 deletions(-) delete mode 100644 packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap diff --git a/packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap b/packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap deleted file mode 100644 index bf1d0f68fd..0000000000 --- a/packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap +++ /dev/null @@ -1,259 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SpreadSheet Custom Tree Tests should calc correctly row index of leaf nodes 1`] = ` -Array [ - Object { - "rowIndex": 0, - "value": "自定义节点 a-1", - }, - Object { - "rowIndex": 1, - "value": "自定义节点 a-1-1", - }, - Object { - "rowIndex": 2, - "value": "指标1", - }, - Object { - "rowIndex": 3, - "value": "指标2", - }, - Object { - "rowIndex": 4, - "value": "自定义节点 a-1-2", - }, - Object { - "rowIndex": 5, - "value": "自定义节点 a-2", - }, -] -`; - -exports[`SpreadSheet Custom Tree Tests should collapse node by collapsed 1`] = ` -Array [ - Object { - "iconName": "Plus", - "id": "root[&]custom-node-1", - "isCollapsed": true, - "value": "自定义节点A", - }, - Object { - "iconName": "Plus", - "id": "root[&]measure-e", - "isCollapsed": true, - "value": "指标E", - }, -] -`; - -exports[`SpreadSheet Custom Tree Tests should collapse node by collapsed field 1`] = ` -Array [ - Object { - "iconName": "Minus", - "id": "root[&]a-1", - "isCollapsed": false, - "value": "自定义节点 a-1", - }, - Object { - "iconName": "Plus", - "id": "root[&]a-1[&]a-1-1", - "isCollapsed": true, - "value": "自定义节点 a-1-1", - }, - Object { - "iconName": undefined, - "id": "root[&]a-1[&]a-1-2", - "isCollapsed": false, - "value": "自定义节点 a-1-2", - }, - Object { - "iconName": undefined, - "id": "root[&]a-2", - "isCollapsed": false, - "value": "自定义节点 a-2", - }, -] -`; - -exports[`SpreadSheet Custom Tree Tests should collapse node by collapsed fields id 1`] = ` -Array [ - Object { - "iconName": "Minus", - "id": "root[&]a-1", - "isCollapsed": false, - "value": "自定义节点 a-1", - }, - Object { - "iconName": "Minus", - "id": "root[&]a-1[&]a-1-1", - "isCollapsed": false, - "value": "自定义节点 a-1-1", - }, - Object { - "iconName": undefined, - "id": "root[&]a-1[&]a-1-1[&]measure-1", - "isCollapsed": false, - "value": "指标1", - }, - Object { - "iconName": undefined, - "id": "root[&]a-1[&]a-1-1[&]measure-2", - "isCollapsed": false, - "value": "指标2", - }, - Object { - "iconName": undefined, - "id": "root[&]a-1[&]a-1-2", - "isCollapsed": false, - "value": "自定义节点 a-1-2", - }, - Object { - "iconName": undefined, - "id": "root[&]a-2", - "isCollapsed": false, - "value": "自定义节点 a-2", - }, -] -`; - -exports[`SpreadSheet Custom Tree Tests should collapse node by user collapseFields first 1`] = ` -Array [ - Object { - "iconName": "Plus", - "id": "root[&]custom-node-1", - "isCollapsed": true, - "value": "自定义节点A", - }, - Object { - "iconName": "Plus", - "id": "root[&]measure-e", - "isCollapsed": true, - "value": "指标E", - }, -] -`; - -exports[`SpreadSheet Custom Tree Tests should get correctly dataset fields 1`] = ` -Object { - "columns": Array [ - "type", - "sub_type", - ], - "rows": Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": Array [], - "description": "指标1描述", - "field": "measure-1", - "title": "指标1", - }, - Object { - "children": Array [], - "description": "指标2描述", - "field": "measure-2", - "title": "指标2", - }, - ], - "description": "a-1-1 描述", - "field": "a-1-1", - "title": "自定义节点 a-1-1", - }, - Object { - "children": Array [], - "description": "a-1-2 描述", - "field": "a-1-2", - "title": "自定义节点 a-1-2", - }, - ], - "description": "a-1 描述", - "field": "a-1", - "title": "自定义节点 a-1", - }, - Object { - "children": Array [], - "description": "a-2 描述", - "field": "a-2", - "title": "自定义节点 a-2", - }, - "$$extra$$", - ], - "valueInCols": false, - "values": Array [ - "measure-1", - "measure-2", - ], -} -`; - -exports[`SpreadSheet Custom Tree Tests should only collapse first node by node id 1`] = ` -Array [ - Object { - "iconName": "Plus", - "id": "root[&]custom-node-1", - "isCollapsed": true, - "value": "自定义节点A", - }, - Object { - "iconName": "Minus", - "id": "root[&]measure-e", - "isCollapsed": false, - "value": "自定义节点A", - }, - Object { - "iconName": undefined, - "id": "root[&]measure-e[&]custom-node-3", - "isCollapsed": false, - "value": "自定义节点C", - }, - Object { - "iconName": "Plus", - "id": "root[&]measure-e[&]custom-node-4", - "isCollapsed": true, - "value": "自定义节点D", - }, -] -`; - -exports[`SpreadSheet Custom Tree Tests should render custom layout row nodes 1`] = ` -Array [ - Object { - "description": "a-1 描述", - "height": 30, - "value": "自定义节点 a-1", - "width": 400, - }, - Object { - "description": "a-1-1 描述", - "height": 30, - "value": "自定义节点 a-1-1", - "width": 400, - }, - Object { - "description": "指标1描述", - "height": 30, - "value": "指标1", - "width": 400, - }, - Object { - "description": "指标2描述", - "height": 30, - "value": "指标2", - "width": 400, - }, - Object { - "description": "a-1-2 描述", - "height": 30, - "value": "自定义节点 a-1-2", - "width": 400, - }, - Object { - "description": "a-2 描述", - "height": 30, - "value": "自定义节点 a-2", - "width": 400, - }, -] -`; diff --git a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts index 32b03998e3..20f40b58fe 100644 --- a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts @@ -588,17 +588,5 @@ describe('SpreadSheet Custom Grid Tests', () => { expect(s2.facet.getCellMeta(0, 1).data[VALUE_FIELD]).toEqual(10); }, ); - - test('should not render sort action icon for custom col header', async () => { - s2.setOptions({ - showDefaultHeaderActionIcon: true, - }); - - await s2.render(false); - - s2.facet.getColCells().forEach((cell) => { - expect(cell.getActionIcons()).toHaveLength(0); - }); - }); }); }); From ada22e0ffc9ab8d08b220469435b4ee087249a15 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Mon, 28 Oct 2024 16:14:27 +0800 Subject: [PATCH 14/16] =?UTF-8?q?test:=20=E5=8D=95=E6=B5=8B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/spreadsheet/custom-grid-spec.ts | 2 +- .../utils/export/__snapshots__/copy-spec.ts.snap | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts index 20f40b58fe..6b2f946411 100644 --- a/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/custom-grid-spec.ts @@ -280,7 +280,7 @@ describe('SpreadSheet Custom Grid Tests', () => { }); afterEach(() => { - // s2.destroy(); + s2.destroy(); }); test('should enable valueInCols', () => { diff --git a/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap b/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap index 9386cf636d..94d5443b32 100644 --- a/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap +++ b/packages/s2-core/__tests__/unit/utils/export/__snapshots__/copy-spec.ts.snap @@ -363,14 +363,14 @@ exports[`Tree Table Core Data Process should copy all data in tree mode 1`] = ` `; exports[`Tree Table Core Data Process should copy all data in tree mode for custom row cell 1`] = ` -"家具 家具 -桌子 椅子 -- - -- - -13 11 -2 8 -- - -- -" +"家具 家具 家具 总计 +桌子 椅子 小计 +- - - - +- - - - +13 11 - - +2 8 - - +- - - - +- - - -" `; exports[`Tree Table Core Data Process should copy all data in tree mode with format 1`] = ` From eff7eec0c875602cad235ec046ea38e7cb436d2b Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Mon, 28 Oct 2024 17:26:58 +0800 Subject: [PATCH 15/16] =?UTF-8?q?refactor:=20=E5=8E=BB=E9=99=A4=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-react/src/hooks/useSpreadSheet.ts | 1 - packages/s2-vue/src/hooks/useSheetUpdate.ts | 1 - s2-site/docs/manual/advanced/custom/custom-header.zh.md | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/s2-react/src/hooks/useSpreadSheet.ts b/packages/s2-react/src/hooks/useSpreadSheet.ts index 275f347a9e..2b37860b41 100644 --- a/packages/s2-react/src/hooks/useSpreadSheet.ts +++ b/packages/s2-react/src/hooks/useSpreadSheet.ts @@ -124,7 +124,6 @@ export function useSpreadSheet(props: SheetComponentProps) { if (!Object.is(prevOptions, options)) { if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) { - // 自定义树目录需要重新构建 CustomGridPivotDataSet rebuildDataSet = true; reloadData = true; s2Ref.current?.setDataCfg(dataCfg); diff --git a/packages/s2-vue/src/hooks/useSheetUpdate.ts b/packages/s2-vue/src/hooks/useSheetUpdate.ts index cce38fce32..8cad721928 100644 --- a/packages/s2-vue/src/hooks/useSheetUpdate.ts +++ b/packages/s2-vue/src/hooks/useSheetUpdate.ts @@ -25,7 +25,6 @@ export const useSheetUpdate = ( updateFlag.rerender = true; if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) { - // 自定义树目录需要重新构建 CustomGridPivotDataSet updateFlag.reloadData = true; updateFlag.rebuildDataset = true; } diff --git a/s2-site/docs/manual/advanced/custom/custom-header.zh.md b/s2-site/docs/manual/advanced/custom/custom-header.zh.md index db1316024a..19a580315d 100644 --- a/s2-site/docs/manual/advanced/custom/custom-header.zh.md +++ b/s2-site/docs/manual/advanced/custom/custom-header.zh.md @@ -13,7 +13,7 @@ tag: New :::warning{title="注意"} 1. 默认的排序 icon `showDefaultHeaderActionIcon` 无效。 -2. 不支持自定义所在 header 的[小计总计](/manual/basic/totals)。 +2. 自定义行头时,不支持配置行头[小计总计](/manual/basic/totals);自定义列头时,不支持配置列头[小计总计](/manual/basic/totals)。 ::: From b59484f4027ee2fff737a8de4b432d83ec324c01 Mon Sep 17 00:00:00 2001 From: wjgogogo <906626481@qq.com> Date: Mon, 28 Oct 2024 17:34:18 +0800 Subject: [PATCH 16/16] =?UTF-8?q?refactor:=20=E8=BF=98=E5=8E=9F=20custom-t?= =?UTF-8?q?ree=20=E5=8D=95=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__snapshots__/custom-tree-spec.ts.snap | 259 ++++++++++++++ .../__tests__/spreadsheet/custom-tree-spec.ts | 327 ++++++++++++++++++ 2 files changed, 586 insertions(+) create mode 100644 packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap create mode 100644 packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts diff --git a/packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap b/packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap new file mode 100644 index 0000000000..bf1d0f68fd --- /dev/null +++ b/packages/s2-core/__tests__/spreadsheet/__snapshots__/custom-tree-spec.ts.snap @@ -0,0 +1,259 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SpreadSheet Custom Tree Tests should calc correctly row index of leaf nodes 1`] = ` +Array [ + Object { + "rowIndex": 0, + "value": "自定义节点 a-1", + }, + Object { + "rowIndex": 1, + "value": "自定义节点 a-1-1", + }, + Object { + "rowIndex": 2, + "value": "指标1", + }, + Object { + "rowIndex": 3, + "value": "指标2", + }, + Object { + "rowIndex": 4, + "value": "自定义节点 a-1-2", + }, + Object { + "rowIndex": 5, + "value": "自定义节点 a-2", + }, +] +`; + +exports[`SpreadSheet Custom Tree Tests should collapse node by collapsed 1`] = ` +Array [ + Object { + "iconName": "Plus", + "id": "root[&]custom-node-1", + "isCollapsed": true, + "value": "自定义节点A", + }, + Object { + "iconName": "Plus", + "id": "root[&]measure-e", + "isCollapsed": true, + "value": "指标E", + }, +] +`; + +exports[`SpreadSheet Custom Tree Tests should collapse node by collapsed field 1`] = ` +Array [ + Object { + "iconName": "Minus", + "id": "root[&]a-1", + "isCollapsed": false, + "value": "自定义节点 a-1", + }, + Object { + "iconName": "Plus", + "id": "root[&]a-1[&]a-1-1", + "isCollapsed": true, + "value": "自定义节点 a-1-1", + }, + Object { + "iconName": undefined, + "id": "root[&]a-1[&]a-1-2", + "isCollapsed": false, + "value": "自定义节点 a-1-2", + }, + Object { + "iconName": undefined, + "id": "root[&]a-2", + "isCollapsed": false, + "value": "自定义节点 a-2", + }, +] +`; + +exports[`SpreadSheet Custom Tree Tests should collapse node by collapsed fields id 1`] = ` +Array [ + Object { + "iconName": "Minus", + "id": "root[&]a-1", + "isCollapsed": false, + "value": "自定义节点 a-1", + }, + Object { + "iconName": "Minus", + "id": "root[&]a-1[&]a-1-1", + "isCollapsed": false, + "value": "自定义节点 a-1-1", + }, + Object { + "iconName": undefined, + "id": "root[&]a-1[&]a-1-1[&]measure-1", + "isCollapsed": false, + "value": "指标1", + }, + Object { + "iconName": undefined, + "id": "root[&]a-1[&]a-1-1[&]measure-2", + "isCollapsed": false, + "value": "指标2", + }, + Object { + "iconName": undefined, + "id": "root[&]a-1[&]a-1-2", + "isCollapsed": false, + "value": "自定义节点 a-1-2", + }, + Object { + "iconName": undefined, + "id": "root[&]a-2", + "isCollapsed": false, + "value": "自定义节点 a-2", + }, +] +`; + +exports[`SpreadSheet Custom Tree Tests should collapse node by user collapseFields first 1`] = ` +Array [ + Object { + "iconName": "Plus", + "id": "root[&]custom-node-1", + "isCollapsed": true, + "value": "自定义节点A", + }, + Object { + "iconName": "Plus", + "id": "root[&]measure-e", + "isCollapsed": true, + "value": "指标E", + }, +] +`; + +exports[`SpreadSheet Custom Tree Tests should get correctly dataset fields 1`] = ` +Object { + "columns": Array [ + "type", + "sub_type", + ], + "rows": Array [ + Object { + "children": Array [ + Object { + "children": Array [ + Object { + "children": Array [], + "description": "指标1描述", + "field": "measure-1", + "title": "指标1", + }, + Object { + "children": Array [], + "description": "指标2描述", + "field": "measure-2", + "title": "指标2", + }, + ], + "description": "a-1-1 描述", + "field": "a-1-1", + "title": "自定义节点 a-1-1", + }, + Object { + "children": Array [], + "description": "a-1-2 描述", + "field": "a-1-2", + "title": "自定义节点 a-1-2", + }, + ], + "description": "a-1 描述", + "field": "a-1", + "title": "自定义节点 a-1", + }, + Object { + "children": Array [], + "description": "a-2 描述", + "field": "a-2", + "title": "自定义节点 a-2", + }, + "$$extra$$", + ], + "valueInCols": false, + "values": Array [ + "measure-1", + "measure-2", + ], +} +`; + +exports[`SpreadSheet Custom Tree Tests should only collapse first node by node id 1`] = ` +Array [ + Object { + "iconName": "Plus", + "id": "root[&]custom-node-1", + "isCollapsed": true, + "value": "自定义节点A", + }, + Object { + "iconName": "Minus", + "id": "root[&]measure-e", + "isCollapsed": false, + "value": "自定义节点A", + }, + Object { + "iconName": undefined, + "id": "root[&]measure-e[&]custom-node-3", + "isCollapsed": false, + "value": "自定义节点C", + }, + Object { + "iconName": "Plus", + "id": "root[&]measure-e[&]custom-node-4", + "isCollapsed": true, + "value": "自定义节点D", + }, +] +`; + +exports[`SpreadSheet Custom Tree Tests should render custom layout row nodes 1`] = ` +Array [ + Object { + "description": "a-1 描述", + "height": 30, + "value": "自定义节点 a-1", + "width": 400, + }, + Object { + "description": "a-1-1 描述", + "height": 30, + "value": "自定义节点 a-1-1", + "width": 400, + }, + Object { + "description": "指标1描述", + "height": 30, + "value": "指标1", + "width": 400, + }, + Object { + "description": "指标2描述", + "height": 30, + "value": "指标2", + "width": 400, + }, + Object { + "description": "a-1-2 描述", + "height": 30, + "value": "自定义节点 a-1-2", + "width": 400, + }, + Object { + "description": "a-2 描述", + "height": 30, + "value": "自定义节点 a-2", + "width": 400, + }, +] +`; diff --git a/packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts b/packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts new file mode 100644 index 0000000000..f2a256984d --- /dev/null +++ b/packages/s2-core/__tests__/spreadsheet/custom-tree-spec.ts @@ -0,0 +1,327 @@ +import type { S2DataConfig, S2Options } from '@/common/interface'; +import { PivotSheet, SpreadSheet } from '@/sheet-type'; +import { getContainer } from 'tests/util/helpers'; +import type { HeaderCell } from '../../src/cell/header-cell'; +import { customRowGridSimpleFields } from '../data/custom-grid-simple-fields'; +import { customTreeNodes } from '../data/custom-tree-nodes'; +import { CustomGridData } from '../data/data-custom-grid'; +import { + expectHighlightActiveNodes, + getSelectedCount, + getSelectedSum, + getTestTooltipData, +} from '../util/interaction'; + +const s2Options: S2Options = { + width: 600, + height: 400, + hierarchyType: 'tree', + style: { + rowCell: { + width: 400, + }, + }, +}; + +describe('SpreadSheet Custom Tree Tests', () => { + let s2: SpreadSheet; + + const getCornerCellLabels = () => { + return s2.facet.getCornerCells().map((cell) => cell.getActualText()); + }; + + const mapRowNodes = (spreadsheet: SpreadSheet) => + spreadsheet.facet.getRowLeafNodes().map((node) => { + const iconName = (node.belongsCell as HeaderCell).getTreeIcon()?.config + .name; + + return { + id: node.id, + value: node.value, + isCollapsed: node.isCollapsed, + iconName, + }; + }); + + const customRowDataCfg: S2DataConfig = { + data: CustomGridData, + meta: [ + { + field: 'type', + name: '类型', + }, + { + field: 'sub_type', + name: '子类型', + }, + { + field: 'a-1', + name: '层级1', + }, + { + field: 'a-2', + name: '层级2', + }, + ], + fields: customRowGridSimpleFields, + }; + + beforeEach(async () => { + s2 = new PivotSheet(getContainer(), customRowDataCfg, s2Options); + await s2.render(); + }); + + afterEach(() => { + s2.destroy(); + }); + + test('should disable valueInCols', () => { + expect(s2.dataCfg.fields.valueInCols).toBeFalsy(); + expect(s2.dataSet.fields.valueInCols).toBeFalsy(); + }); + + test('should get correctly dataset fields', () => { + expect(s2.dataSet.fields).toMatchSnapshot(); + }); + + test('should render custom layout row nodes', () => { + const rowNodes = s2.facet.getRowNodes().map((node) => { + return { + value: node.value, + width: node.width, + height: node.height, + description: node.extra?.['description'], + }; + }); + + expect(rowNodes).toMatchSnapshot(); + }); + + test('should calc correctly row index of leaf nodes', () => { + const rowLeafNodes = s2.facet.getRowLeafNodes().map((node) => { + return { + value: node.value, + rowIndex: node.rowIndex, + }; + }); + + expect(rowLeafNodes).toMatchSnapshot(); + }); + + test('should select custom row header cell', () => { + // a-1 + const rowNode = s2.facet.getRowNodes()[0]; + + // 选中 a-1 + s2.interaction.changeCell({ + cell: rowNode.belongsCell!, + }); + + // 选中单元格本身 + expect(s2.interaction.getActiveCells()).toHaveLength(1); + // 高亮子节点 + expectHighlightActiveNodes(s2, ['root[&]a-1']); + + // 取消选中 a-1 + s2.interaction.changeCell({ + cell: rowNode.belongsCell!, + }); + expect(s2.interaction.getActiveCells()).toBeEmpty(); + }); + + test.each([ + { field: 'a-1', count: 2, sum: null }, + { field: 'a-1-1', count: 2, sum: null }, + { field: 'measure-1', count: 2, sum: 24 }, + { field: 'measure-2', count: 2, sum: 10 }, + { field: 'a-1-2', count: 2, sum: null }, + { field: 'a-1-2', count: 2, sum: null }, + ])( + 'should get selected cell summary infos for %o', + ({ field, count, sum }) => { + const rowNode = s2.facet + .getRowNodes() + .find((node) => node.field === field)!; + + // 选中 + s2.interaction.changeCell({ + cell: rowNode.belongsCell!, + }); + + const tooltipData = getTestTooltipData(s2, rowNode.belongsCell!); + + // 选中个数 + expect(getSelectedCount(tooltipData.summaries)).toEqual(count); + // 汇总数据总和 + expect(getSelectedSum(tooltipData.summaries)).toEqual(sum); + }, + ); + + test('should render custom corner text by default title', async () => { + s2.setDataCfg({ + meta: [], + }); + + await s2.render(true); + + const cornerCellLabels = getCornerCellLabels(); + + expect(cornerCellLabels).toEqual([ + '自定义节点 a-1/自定义节点 a-2/数值', + 'type', + ]); + }); + + test('should render custom corner text by meta formatter', async () => { + s2.setDataCfg({ + meta: [ + { + field: 'a-1', + name: '文本1', + }, + { + field: 'a-2', + name: '文本2', + }, + ], + }); + + await s2.render(true); + const cornerCellLabels = getCornerCellLabels(); + + expect(cornerCellLabels).toEqual(['文本1/文本2/数值', 'type']); + }); + + test('should render custom corner text by cornerText options', async () => { + s2.setOptions({ + cornerText: '测试', + }); + + await s2.render(); + + const cornerCellLabels = getCornerCellLabels(); + + expect(cornerCellLabels).toEqual(['测试', '类型']); + }); + + test('should render custom tree row node width', async () => { + s2.setOptions({ + style: { + rowCell: { + width: 50, + }, + }, + }); + + await s2.render(); + + const { rowNodes, rowsHierarchy } = s2.facet.getLayoutResult(); + + expect(rowsHierarchy.width).toEqual(50); + expect(rowNodes.every((node) => node.width === 50)).toBeTruthy(); + }); + + test('should collapse node by collapsed', async () => { + s2.setDataCfg({ + fields: { + rows: customTreeNodes.map((node) => { + return { + ...node, + collapsed: true, + }; + }), + }, + }); + await s2.render(true); + + expect(mapRowNodes(s2)).toMatchSnapshot(); + }); + + test('should collapse node by collapsed fields id', async () => { + s2.setOptions({ + style: { + rowCell: { + collapseFields: { + 'root[&]自定义节点 a-1': true, + 'root[&]自定义节点 a-2': false, + }, + }, + }, + }); + await s2.render(true); + + expect(mapRowNodes(s2)).toMatchSnapshot(); + }); + + test('should collapse node by collapsed field', async () => { + s2.setOptions({ + style: { + rowCell: { + collapseFields: { 'a-1-1': true, 'a-1-2': false }, + }, + }, + }); + await s2.render(true); + + expect(mapRowNodes(s2)).toMatchSnapshot(); + }); + + test('should collapse node by user collapseFields first', async () => { + const collapsedField = 'custom-node-1'; + + s2.setDataCfg({ + fields: { + rows: customTreeNodes.map((node) => { + return { + ...node, + collapsed: node.field !== collapsedField, + }; + }), + }, + }); + + s2.setOptions({ + style: { + rowCell: { + collapseFields: { + [collapsedField]: true, + }, + }, + }, + }); + await s2.render(true); + + expect(mapRowNodes(s2)).toMatchSnapshot(); + }); + + // https://github.com/antvis/S2/issues/2455 + test('should only collapse first node by node id', async () => { + const collapsedField = 'custom-node-1'; + + s2.setDataCfg({ + fields: { + rows: customTreeNodes.map((node) => { + return { + ...node, + // 让两个节点名一样 + title: '自定义节点A', + collapsed: false, + }; + }), + }, + }); + + s2.setOptions({ + style: { + rowCell: { + collapseFields: { + [collapsedField]: true, + }, + }, + }, + }); + await s2.render(true); + + expect(mapRowNodes(s2)).toMatchSnapshot(); + }); +});