Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 新增treeLineShow配置和usePolyline,给树形表头扩展配置线性功能(优化版) #3022

Open
wants to merge 6 commits into
base: next
Choose a base branch
from
81 changes: 81 additions & 0 deletions packages/s2-core/__tests__/data/data-custom-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,84 @@ export const CustomTreeData = [
'measure-f': 66,
},
];

export const customTreeFields: S2DataConfig['fields'] = {
columns: ['type', 'sub_type'],
values: [
'measure-a',
'measure-b',
'measure-c',
'measure-d',
'measure-e',
'measure-f',
],
rows: [
{
field: 'custom-node-1',
title: '自定义节点A',
description: '自定义节点A描述',
collapsed: false,
children: [
{
field: 'measure-a',
title: '指标A',
description: '指标A描述',
children: [
{
field: 'measure-b',
title: '指标B',
description: '指标B描述',
children: [],
},
{
field: 'custom-node-2',
title: '自定义节点B',
description: '自定义节点B描述',
children: [],
},
{
field: 'measure-c',
title: '指标C',
description: '指标C描述',
children: [],
},
],
},
{
field: 'custom-node-5',
title: '自定义节点E',
description: '自定义节点E描述',
children: [],
},
],
},
{
field: 'measure-e',
title: '自定义节点E',
description: '指标E描述',
children: [
{
field: 'custom-node-3',
title: '自定义节点C',
description: '自定义节点C描述',
children: [],
},
{
field: 'custom-node-4',
title: '自定义节点D',
description: '自定义节点D描述',
children: [
{
field: 'measure-f',
title: '指标F',
description: '指标F描述',
children: [],
},
],
collapsed: false,
},
],
},
],
valueInCols: false,
};
169 changes: 168 additions & 1 deletion packages/s2-core/__tests__/spreadsheet/spread-sheet-tree-mode-spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { S2DataConfig, S2Options } from '@/common';
import { get } from 'lodash';
import { CustomTreeData, customTreeFields } from 'tests/data/data-custom-tree';
import * as mockDataConfig from 'tests/data/simple-data.json';
import { createPivotSheet, getContainer } from 'tests/util/helpers';
import { CornerNodeType, PivotSheet } from '../../src';
import { CornerNodeType, Node, PivotSheet } from '../../src';

const s2Options: S2Options = {
width: 200,
Expand Down Expand Up @@ -110,5 +112,170 @@ describe('SpreadSheet Tree Mode Tests', () => {
expect(seriesNumberCell.getTreeIcon()).toBeFalsy();
expect(rowCell.getTreeIcon()).toBeTruthy();
});

// https://github.com/antvis/S2/issues/2995
test('should render correctly tree polyline', async () => {
const customTreeDataCfg: S2DataConfig = {
data: CustomTreeData,
fields: customTreeFields,
};
// 正常渲染树形图和polyline
const customTreeOptions: S2Options = {
debug: true,
width: 600,
height: 480,
hierarchyType: 'tree',
style: {
rowCell: {
showTreeLine: true,
},
},
};
const s2 = new PivotSheet(
container,
customTreeDataCfg,
customTreeOptions,
);

await s2.render();
const dottedLinesLenght = s2.getDottedLinesLengh();

expect(dottedLinesLenght).toEqual(8);
// 渲染polyline时的坐标位置与节点位置一致
function getTreeIconCfg(node: Node) {
if (
get(node, 'belongsCell.treeIcon.cfg') &&
!get(node, 'belongsCell.treeIcon.cfg.destroyed')
) {
return get(node, 'belongsCell.treeIcon.cfg');
}

return {
x: 8 + node.level * 14,
y: node.y + node.height / 2 - 10 / 2,
width: 10,
height: 10,
};
}
const maxLevel = s2.facet.getRowNodes(0)?.[0]?.hierarchy?.maxLevel;
const colHeaderHeight = s2.facet.getColNodes(0)?.[0]?.hierarchy?.height;
const viewportHeight =
s2.facet.panelBBox.viewportHeight + colHeaderHeight;
const offsetHeight = s2.facet.getScrollOffset().scrollY;
const dottedLines = s2.getDottedLines();

for (let i = 0; i <= maxLevel; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

单测有点复杂了, 后面不太好维护, 可以直接给实际渲染的连接线的坐标等做一个快照测试 toMatchSnapshot()

const rowNodes = s2.facet.getRowNodes(i);

rowNodes.forEach((rowNode) => {
if (rowNode.children.length) {
const childs: Node[] = [];

rowNode.children.forEach((child) => {
const rowNodeTreeIconCfg = getTreeIconCfg(rowNode);
const childTreeIconCfg = getTreeIconCfg(child);

if (rowNodeTreeIconCfg && childTreeIconCfg) {
const x1 =
rowNode.x +
rowNodeTreeIconCfg.x +
rowNodeTreeIconCfg.width / 2;
let y1 =
colHeaderHeight +
rowNodeTreeIconCfg.y +
rowNodeTreeIconCfg.height;

if (childs?.length > 0) {
const preChild = childs?.pop()!;
const preChildTreeIconCfg = getTreeIconCfg(preChild);

y1 =
colHeaderHeight +
preChildTreeIconCfg.y +
preChildTreeIconCfg.height / 2;
}

childs.push(child);
const x2 = child.x + childTreeIconCfg.x;
const y2 =
colHeaderHeight +
childTreeIconCfg.y +
childTreeIconCfg.height / 2;
const points = [
[
x1,
Math.min(
Math.max(y1 - offsetHeight, colHeaderHeight),
viewportHeight,
),
],
[
x1,
Math.min(
Math.max(y2 - offsetHeight, colHeaderHeight),
viewportHeight,
),
],
];

if (
y2 - offsetHeight >= colHeaderHeight &&
y2 - offsetHeight <= viewportHeight
) {
points.push([x2, y2 - offsetHeight]);
}

const isExist = dottedLines.some((line) => {
const pointsLine = line?.attributes?.points;

return JSON.stringify(points) === JSON.stringify(pointsLine);
});

// eslint-disable-next-line jest/no-conditional-expect
expect(isExist).toEqual(true);
}
});
}
});
}

// 树形图全部收起时,不展示polyline
const customTreeOptions1: S2Options = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

展开和收起可以分两个 test 断言, 而不是写在一起

debug: true,
width: 600,
height: 480,
hierarchyType: 'tree',
style: {
rowCell: {
showTreeLine: true,
collapseAll: true,
},
},
};

s2.setOptions(customTreeOptions1);
await s2.render();
const dottedLines1 = s2.getDottedLinesLengh();

expect(dottedLines1).toEqual(0);
// showTreeLine为false时, 关闭polyline
const customTreeOptions2: S2Options = {
debug: true,
width: 600,
height: 480,
hierarchyType: 'tree',
style: {
rowCell: {
showTreeLine: false,
},
},
};

s2.setOptions(customTreeOptions2);
await s2.render();
const dottedLines2 = s2.getDottedLinesLengh();

expect(dottedLines2).toEqual(0);
});
});
});
4 changes: 4 additions & 0 deletions packages/s2-core/src/common/constant/axisPolyline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Polyline } from '@antv/g-lite';

// 存储已绘制的连接线,用于销毁
export const dottedTreeLines: Polyline[] = [];
1 change: 1 addition & 0 deletions packages/s2-core/src/common/constant/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './axisPolyline';
export * from './basic';
export * from './classnames';
export * from './condition';
Expand Down
4 changes: 4 additions & 0 deletions packages/s2-core/src/common/interface/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ export interface RowCellStyle extends BaseCellStyle, CellTextWordWrapStyle {
* @description 优先级 `collapseFields` > `expandDepth` > `collapseAll`
*/
expandDepth?: number | null;
/**
* 是否配置线性样式,默认不配置
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 是否配置线性样式,默认不配置
* 是否展示连接线,默认关闭

*/
showTreeLine: boolean;
}

export interface ColCellStyle extends BaseCellStyle, CellTextWordWrapStyle {
Expand Down
8 changes: 8 additions & 0 deletions packages/s2-core/src/common/interface/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ export interface SplitLine {
borderDash?: LineStyleProps['lineDash'];
}

export interface PolyLine {
stroke?: string;
lineDash?: number[];
lineWidth?: number;
}

export interface DefaultCellTheme extends GridAnalysisCellTheme {
/**
* 粗体文本样式 (如: 总计, 小计, 行列头非叶子节点文本)
Expand Down Expand Up @@ -354,6 +360,8 @@ export interface S2Theme extends CellThemes {

/** 空数据占位符 */
empty?: EmptyTheme;
/** 线性配置 */
polyline?: PolyLine;
}

export type ThemeName = keyof typeof PALETTE_MAP;
Expand Down
Loading
Loading