diff --git a/packages/s2-core/__tests__/spreadsheet/interaction-corner-click-spec.ts b/packages/s2-core/__tests__/spreadsheet/interaction-corner-click-spec.ts new file mode 100644 index 0000000000..6eb3aa1eda --- /dev/null +++ b/packages/s2-core/__tests__/spreadsheet/interaction-corner-click-spec.ts @@ -0,0 +1,126 @@ +import { + createMockCellInfo, + createPivotSheet, + getContainer, +} from 'tests/util/helpers'; +import { customRowGridSimpleFields } from '../data/custom-grid-simple-fields'; +import { CustomGridData } from '../data/data-custom-grid'; +import { S2Event } from '@/common/constant'; +import { CornerNodeType, type S2Options } from '@/common/interface'; +import type { GEvent, HierarchyType, S2DataConfig } from '@/index'; +import { PivotSheet, SpreadSheet } from '@/sheet-type'; + +const s2Options: S2Options = { + width: 600, + height: 400, +}; + +describe('Interaction Corner Cell Click Tests', () => { + let s2: SpreadSheet; + + afterEach(() => { + s2.destroy(); + }); + + test.each(['grid', 'tree'] as HierarchyType[])( + 'should not selected row cell when col corner cell clicked for %s mode', + async (hierarchyType) => { + jest.spyOn(SpreadSheet.prototype, 'getCell').mockImplementationOnce( + () => + createMockCellInfo('testId', { + cornerType: CornerNodeType.Col, + }).mockCell, + ); + + s2 = createPivotSheet({ + ...s2Options, + hierarchyType, + }); + await s2.render(); + + const reset = jest + .spyOn(s2.interaction, 'reset') + .mockImplementationOnce(() => {}); + + s2.emit(S2Event.CORNER_CELL_CLICK, {} as unknown as GEvent); + + expect(reset).toHaveBeenCalledTimes(1); + expect(s2.interaction.isSelectedState()).toBeFalsy(); + }, + ); + + test.each(['grid', 'tree'] as HierarchyType[])( + 'should selected row cell when row corner cell clicked for %s mode', + async (hierarchyType) => { + jest.spyOn(SpreadSheet.prototype, 'getCell').mockImplementationOnce( + () => + createMockCellInfo('city', { + cornerType: CornerNodeType.Row, + }).mockCell, + ); + + s2 = createPivotSheet({ + ...s2Options, + hierarchyType, + }); + await s2.render(); + + const reset = jest + .spyOn(s2.interaction, 'reset') + .mockImplementationOnce(() => {}); + + s2.emit(S2Event.CORNER_CELL_CLICK, {} as unknown as GEvent); + + expect(reset).not.toHaveBeenCalled(); + expect(s2.interaction.getActiveCells()).toHaveLength( + s2.isHierarchyTreeType() ? 3 : 2, + ); + expect(s2.interaction.isSelectedState()).toBeTruthy(); + }, + ); + + test.each(['grid', 'tree'] as HierarchyType[])( + 'should selected custom row cell when row corner cell clicked for %s mode', + async (hierarchyType) => { + jest.spyOn(SpreadSheet.prototype, 'getCell').mockImplementationOnce( + () => + createMockCellInfo('a-1-1', { + cornerType: CornerNodeType.Row, + }).mockCell, + ); + + const customRowDataCfg: S2DataConfig = { + data: CustomGridData, + fields: customRowGridSimpleFields, + }; + + s2 = new PivotSheet(getContainer(), customRowDataCfg, { + ...s2Options, + hierarchyType, + }); + await s2.render(); + + const reset = jest + .spyOn(s2.interaction, 'reset') + .mockImplementationOnce(() => {}); + + s2.emit(S2Event.CORNER_CELL_CLICK, {} as unknown as GEvent); + + const gridFields = ['a-1-1', 'a-1-2']; + const treeFields = [ + 'a-1', + 'a-1-1', + 'measure-1', + 'measure-2', + 'a-1-2', + 'a-2', + ]; + + expect(reset).not.toHaveBeenCalled(); + expect( + s2.interaction.getActiveCells().map((cell) => cell.getMeta().field), + ).toEqual(s2.isHierarchyTreeType() ? treeFields : gridFields); + expect(s2.interaction.isSelectedState()).toBeTruthy(); + }, + ); +}); diff --git a/packages/s2-core/__tests__/unit/interaction/base-interaction/click/corner-cell-click-spec.ts b/packages/s2-core/__tests__/unit/interaction/base-interaction/click/corner-cell-click-spec.ts index 5c7d6d7f7a..08168ba37f 100644 --- a/packages/s2-core/__tests__/unit/interaction/base-interaction/click/corner-cell-click-spec.ts +++ b/packages/s2-core/__tests__/unit/interaction/base-interaction/click/corner-cell-click-spec.ts @@ -4,11 +4,11 @@ import { sleep, } from 'tests/util/helpers'; import { CellType, InteractionStateName, type Node } from '../../../../../src'; -import type { GEvent } from '@/index'; -import type { S2Options } from '@/common/interface'; -import type { SpreadSheet } from '@/sheet-type'; import { InterceptType, S2Event } from '@/common/constant'; +import type { HierarchyType, S2Options } from '@/common/interface'; +import type { GEvent } from '@/index'; import { CornerCellClick } from '@/interaction'; +import type { SpreadSheet } from '@/sheet-type'; jest.mock('@/interaction/event-controller'); @@ -43,30 +43,40 @@ describe('Interaction Corner Cell Click Tests', () => { expect(cornerCellClick.bindEvents).toBeDefined(); }); - test('should select current column cells when row corner cell click', () => { - const selected = jest.fn(); + test.each(['grid', 'tree'] as HierarchyType[])( + 'should select current column cells when row corner cell click by %s mode', + async (hierarchyType) => { + s2.setOptions({ hierarchyType }); + await s2.render(); - s2.on(S2Event.GLOBAL_SELECTED, selected); + const selected = jest.fn(); - s2.emit(S2Event.CORNER_CELL_CLICK, {} as unknown as GEvent); + s2.on(S2Event.GLOBAL_SELECTED, selected); - expect(s2.interaction.hasIntercepts([InterceptType.HOVER])).toBeTruthy(); - expect(s2.showTooltipWithInfo).toHaveBeenCalledWith(expect.anything(), [], { - data: { summaries: [{ name: '', selectedData: [], value: null }] }, - }); - expect(s2.interaction.getState()).toEqual({ - cells: [ + s2.emit(S2Event.CORNER_CELL_CLICK, {} as unknown as GEvent); + + expect(s2.interaction.hasIntercepts([InterceptType.HOVER])).toBeTruthy(); + expect(s2.showTooltipWithInfo).toHaveBeenCalledWith( + expect.anything(), + [], { - colIndex: -1, - rowIndex: -1, - type: CellType.ROW_CELL, - id: mockCellInfo.mockCellMeta['id'], + data: { summaries: [{ name: '', selectedData: [], value: null }] }, }, - ], - stateName: InteractionStateName.SELECTED, - }); - expect(selected).toHaveBeenCalled(); - }); + ); + expect(s2.interaction.getState()).toEqual({ + cells: [ + { + colIndex: -1, + rowIndex: -1, + type: CellType.ROW_CELL, + id: mockCellInfo.mockCellMeta['id'], + }, + ], + stateName: InteractionStateName.SELECTED, + }); + expect(selected).toHaveBeenCalled(); + }, + ); test('should not select current column cells when column corner cell click', () => { s2.getCell = () => null; diff --git a/packages/s2-core/__tests__/util/helpers.ts b/packages/s2-core/__tests__/util/helpers.ts index 736ba8a3db..cbd9f5fe95 100644 --- a/packages/s2-core/__tests__/util/helpers.ts +++ b/packages/s2-core/__tests__/util/helpers.ts @@ -199,7 +199,7 @@ export const createFakeSpreadSheet = (config?: { export const createMockCellInfo = ( cellId: string, - { colIndex = 0, rowIndex = 0, colId = '0', level = 0 } = {}, + { colIndex = 0, rowIndex = 0, colId = '0', level = 0, cornerType = '' } = {}, ) => { const mockCellViewMeta: Partial = { id: cellId, @@ -209,6 +209,7 @@ export const createMockCellInfo = ( colId, level, type: undefined, + cornerType, x: 0, y: 0, spreadsheet: { @@ -237,6 +238,7 @@ export const createMockCellInfo = ( 'field', 'colId', 'field', + 'cornerType', ]); const mockCell = { ...mockCellViewMeta, diff --git a/packages/s2-core/src/interaction/base-interaction/click/corner-cell-click.ts b/packages/s2-core/src/interaction/base-interaction/click/corner-cell-click.ts index 552f81a02f..78e62a32e5 100644 --- a/packages/s2-core/src/interaction/base-interaction/click/corner-cell-click.ts +++ b/packages/s2-core/src/interaction/base-interaction/click/corner-cell-click.ts @@ -1,6 +1,11 @@ import type { FederatedPointerEvent as CanvasEvent } from '@antv/g'; import { isEmpty } from 'lodash'; -import { CellType, type CellMeta, type Data } from '../../../common'; +import { + CellType, + type CellMeta, + type Data, + CornerNodeType, +} from '../../../common'; import { InteractionStateName, InterceptType, @@ -19,7 +24,7 @@ export class CornerCellClick extends BaseEvent implements BaseEventImplement { private bindCornerCellClick() { this.spreadsheet.on(S2Event.CORNER_CELL_CLICK, (event) => { - const { interaction, facet } = this.spreadsheet; + const { interaction } = this.spreadsheet; const cornerCell = this.spreadsheet.getCell(event.target); if (!cornerCell) { @@ -28,11 +33,17 @@ export class CornerCellClick extends BaseEvent implements BaseEventImplement { // 获取当前角头所对应那一列的行头单元格节点 const cornerCellMeta = cornerCell.getMeta(); - const rowNodes = cornerCellMeta?.field - ? facet.getRowNodesByField(cornerCellMeta?.field) - : []; + + // TODO: 列角头的交互待拓展 + if (cornerCellMeta?.cornerType === CornerNodeType.Col) { + interaction.reset(); + + return; + } + + const rowNodes = this.getSelectedRowNodes(cornerCellMeta?.field!); const sample = rowNodes[0]?.belongsCell; - const cells = this.getRowCells(rowNodes); + const cells = this.getRowCellMetas(rowNodes); if (sample && interaction.isSelectedCell(sample)) { interaction.reset(); @@ -63,7 +74,25 @@ export class CornerCellClick extends BaseEvent implements BaseEventImplement { }); } - private getRowCells(nodes: Node[]): CellMeta[] { + private getSelectedRowNodes(field: string) { + const { facet } = this.spreadsheet; + + // 树状模式只有一列 + if (this.spreadsheet.isHierarchyTreeType()) { + return facet.getRowNodes(); + } + + if (!this.spreadsheet.isCustomRowFields()) { + return facet.getRowNodesByField(field); + } + + // 自定义行头 field 都是独立的, 需要根据 level 区查找. + const sampleNode = facet.getRowNodesByField(field)[0]; + + return facet.getRowNodes(sampleNode?.level); + } + + private getRowCellMetas(nodes: Node[]): CellMeta[] { return nodes.map((node) => { return { id: node.id,