@opendata-ai/openchart-engine 8.0.0-rc.9 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +175 -12
- package/dist/index.js +917 -168
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/__test-fixtures__/specs.ts +0 -182
- package/src/__tests__/__snapshots__/compile-snapshot.test.ts.snap +0 -2401
- package/src/__tests__/annotation-behavior.test.ts +0 -852
- package/src/__tests__/axes.test.ts +0 -1579
- package/src/__tests__/compile-animation.test.ts +0 -161
- package/src/__tests__/compile-chart.test.ts +0 -1303
- package/src/__tests__/compile-footnotes.test.ts +0 -277
- package/src/__tests__/compile-layer.test.ts +0 -969
- package/src/__tests__/compile-snapshot.test.ts +0 -156
- package/src/__tests__/compound-labels.test.ts +0 -147
- package/src/__tests__/dimensions.test.ts +0 -918
- package/src/__tests__/encoding-sugar.test.ts +0 -390
- package/src/__tests__/facet.test.ts +0 -215
- package/src/__tests__/helpers/invariants.ts +0 -299
- package/src/__tests__/known-bugs.test.ts +0 -566
- package/src/__tests__/layout-fuzz.test.ts +0 -358
- package/src/__tests__/layout-invariants.test.ts +0 -535
- package/src/__tests__/legend.test.ts +0 -721
- package/src/__tests__/mark-keys.test.ts +0 -294
- package/src/__tests__/ranking-recipes.test.ts +0 -260
- package/src/__tests__/rotation.test.ts +0 -83
- package/src/__tests__/scales.test.ts +0 -323
- package/src/__tests__/series-search.test.ts +0 -92
- package/src/__tests__/spec-sugar.test.ts +0 -801
- package/src/__tests__/you-draw-it.test.ts +0 -160
- package/src/annotations/__tests__/compute.test.ts +0 -1672
- package/src/annotations/__tests__/geometry.test.ts +0 -66
- package/src/annotations/__tests__/placement-fuzz.test.ts +0 -256
- package/src/annotations/__tests__/placement-invariants.test.ts +0 -345
- package/src/annotations/__tests__/placement.test.ts +0 -316
- package/src/annotations/__tests__/resolve-text.test.ts +0 -788
- package/src/annotations/__tests__/rich-text.test.ts +0 -91
- package/src/annotations/__tests__/thinning.test.ts +0 -285
- package/src/annotations/collisions.ts +0 -326
- package/src/annotations/compute.ts +0 -333
- package/src/annotations/constants.ts +0 -162
- package/src/annotations/geometry.ts +0 -376
- package/src/annotations/move.ts +0 -135
- package/src/annotations/placement.ts +0 -636
- package/src/annotations/position.ts +0 -131
- package/src/annotations/resolve-range.ts +0 -117
- package/src/annotations/resolve-refline.ts +0 -157
- package/src/annotations/resolve-text.ts +0 -545
- package/src/annotations/rich-text.ts +0 -65
- package/src/annotations/thinning.ts +0 -190
- package/src/barlist/__tests__/compile-barlist.test.ts +0 -200
- package/src/barlist/compile-barlist.ts +0 -391
- package/src/barlist/types.ts +0 -28
- package/src/charts/__tests__/post-process.test.ts +0 -237
- package/src/charts/__tests__/registry.test.ts +0 -116
- package/src/charts/__tests__/utils.test.ts +0 -195
- package/src/charts/_shared/__tests__/density-filter.test.ts +0 -32
- package/src/charts/_shared/density-filter.ts +0 -26
- package/src/charts/_shared/format-label-value.ts +0 -16
- package/src/charts/bar/__tests__/compute.test.ts +0 -934
- package/src/charts/bar/__tests__/gradient-orient.test.ts +0 -127
- package/src/charts/bar/__tests__/labels.test.ts +0 -292
- package/src/charts/bar/compute.ts +0 -539
- package/src/charts/bar/index.ts +0 -60
- package/src/charts/bar/labels.ts +0 -311
- package/src/charts/beeswarm/__tests__/compute.test.ts +0 -280
- package/src/charts/beeswarm/__tests__/dodge.test.ts +0 -148
- package/src/charts/beeswarm/compute.ts +0 -203
- package/src/charts/beeswarm/dodge.ts +0 -155
- package/src/charts/beeswarm/index.ts +0 -32
- package/src/charts/builtin.ts +0 -75
- package/src/charts/calendar/__tests__/compute.test.ts +0 -417
- package/src/charts/calendar/compute.ts +0 -319
- package/src/charts/calendar/index.ts +0 -32
- package/src/charts/column/__tests__/compute.test.ts +0 -540
- package/src/charts/column/__tests__/labels.test.ts +0 -179
- package/src/charts/column/compute.ts +0 -493
- package/src/charts/column/index.ts +0 -53
- package/src/charts/column/labels.ts +0 -229
- package/src/charts/dot/__tests__/compute.test.ts +0 -380
- package/src/charts/dot/__tests__/labels.test.ts +0 -98
- package/src/charts/dot/compute.ts +0 -285
- package/src/charts/dot/index.ts +0 -62
- package/src/charts/dot/labels.ts +0 -121
- package/src/charts/line/__tests__/compute.test.ts +0 -1194
- package/src/charts/line/__tests__/curves.test.ts +0 -51
- package/src/charts/line/__tests__/labels.test.ts +0 -94
- package/src/charts/line/__tests__/mark-options.test.ts +0 -175
- package/src/charts/line/area.ts +0 -484
- package/src/charts/line/compute.ts +0 -356
- package/src/charts/line/curves.ts +0 -85
- package/src/charts/line/index.ts +0 -176
- package/src/charts/line/labels.ts +0 -177
- package/src/charts/parliament/__tests__/compute.test.ts +0 -411
- package/src/charts/parliament/compute.ts +0 -313
- package/src/charts/parliament/index.ts +0 -32
- package/src/charts/pie/__tests__/compute.test.ts +0 -384
- package/src/charts/pie/__tests__/labels.test.ts +0 -132
- package/src/charts/pie/compute.ts +0 -324
- package/src/charts/pie/index.ts +0 -49
- package/src/charts/pie/labels.ts +0 -143
- package/src/charts/post-process.ts +0 -229
- package/src/charts/range/__tests__/compute.test.ts +0 -486
- package/src/charts/range/compute.ts +0 -273
- package/src/charts/range/index.ts +0 -82
- package/src/charts/range/labels.ts +0 -169
- package/src/charts/rect/__tests__/compute.test.ts +0 -120
- package/src/charts/rect/index.ts +0 -127
- package/src/charts/registry.ts +0 -72
- package/src/charts/rule/index.ts +0 -139
- package/src/charts/scatter/__tests__/compute.test.ts +0 -488
- package/src/charts/scatter/__tests__/trendline.test.ts +0 -191
- package/src/charts/scatter/compute.ts +0 -192
- package/src/charts/scatter/index.ts +0 -46
- package/src/charts/scatter/trendline.ts +0 -100
- package/src/charts/text/__tests__/compute.test.ts +0 -307
- package/src/charts/text/index.ts +0 -141
- package/src/charts/tick/__tests__/compute.test.ts +0 -75
- package/src/charts/tick/index.ts +0 -99
- package/src/charts/utils.ts +0 -170
- package/src/charts/waffle/__tests__/compute.test.ts +0 -305
- package/src/charts/waffle/compute.ts +0 -197
- package/src/charts/waffle/index.ts +0 -31
- package/src/compile/__tests__/color-scale-range.test.ts +0 -158
- package/src/compile/__tests__/color-scale-reverse.test.ts +0 -89
- package/src/compile/__tests__/data-clip.test.ts +0 -127
- package/src/compile/__tests__/fill-patterns.test.ts +0 -154
- package/src/compile/__tests__/size-scale.test.ts +0 -108
- package/src/compile/__tests__/watermark-obstacle.test.ts +0 -93
- package/src/compile/color-scale-range.ts +0 -101
- package/src/compile/data-clip.ts +0 -48
- package/src/compile/fill-patterns.ts +0 -74
- package/src/compile/layer.ts +0 -630
- package/src/compile/size-scale.ts +0 -141
- package/src/compile/spec-sugar.ts +0 -758
- package/src/compile/watermark-obstacle.ts +0 -54
- package/src/compile/you-draw-it.ts +0 -123
- package/src/compile.ts +0 -1503
- package/src/compiler/__tests__/a11y-warnings.test.ts +0 -148
- package/src/compiler/__tests__/animation.test.ts +0 -177
- package/src/compiler/__tests__/compile.test.ts +0 -87
- package/src/compiler/__tests__/fixtures/vl-parity/axis-null.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/color-value.json +0 -20
- package/src/compiler/__tests__/fixtures/vl-parity/count-aggregate.json +0 -22
- package/src/compiler/__tests__/fixtures/vl-parity/data-values.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/layer-axis-null.json +0 -14
- package/src/compiler/__tests__/fixtures/vl-parity/layer-count.json +0 -34
- package/src/compiler/__tests__/fixtures/vl-parity/layer-data-values.json +0 -22
- package/src/compiler/__tests__/fixtures/vl-parity/layer-legend-null.json +0 -41
- package/src/compiler/__tests__/fixtures/vl-parity/layer-scheme.json +0 -38
- package/src/compiler/__tests__/fixtures/vl-parity/layer-sort.json +0 -28
- package/src/compiler/__tests__/fixtures/vl-parity/layer-theta.json +0 -28
- package/src/compiler/__tests__/fixtures/vl-parity/layer-title.json +0 -30
- package/src/compiler/__tests__/fixtures/vl-parity/layer-value-def.json +0 -29
- package/src/compiler/__tests__/fixtures/vl-parity/legend-null.json +0 -32
- package/src/compiler/__tests__/fixtures/vl-parity/scheme.json +0 -25
- package/src/compiler/__tests__/fixtures/vl-parity/sort-by-value.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/theta-arc.json +0 -19
- package/src/compiler/__tests__/fixtures/vl-parity/title-subtitle.json +0 -21
- package/src/compiler/__tests__/fixtures/vl-parity/width-height.json +0 -23
- package/src/compiler/__tests__/keys.test.ts +0 -83
- package/src/compiler/__tests__/normalize-highlight.test.ts +0 -103
- package/src/compiler/__tests__/normalize.test.ts +0 -399
- package/src/compiler/__tests__/sparkline-defaults.test.ts +0 -153
- package/src/compiler/__tests__/validate-did-you-mean.test.ts +0 -170
- package/src/compiler/__tests__/validate-highlight.test.ts +0 -112
- package/src/compiler/__tests__/validate-near-miss.test.ts +0 -251
- package/src/compiler/__tests__/validate.test.ts +0 -629
- package/src/compiler/__tests__/vl-parity.test.ts +0 -68
- package/src/compiler/a11y-warnings.ts +0 -110
- package/src/compiler/animation.ts +0 -142
- package/src/compiler/index.ts +0 -47
- package/src/compiler/keys.ts +0 -55
- package/src/compiler/normalize.ts +0 -734
- package/src/compiler/sparkline-defaults.ts +0 -138
- package/src/compiler/types.ts +0 -241
- package/src/compiler/validate.ts +0 -2041
- package/src/endpoint-labels/__tests__/compute.test.ts +0 -587
- package/src/endpoint-labels/compute.ts +0 -621
- package/src/endpoint-labels/constants.ts +0 -54
- package/src/endpoint-labels/format.ts +0 -60
- package/src/endpoint-labels/predict.ts +0 -139
- package/src/format/__tests__/field-format.test.ts +0 -164
- package/src/format/field-format.ts +0 -27
- package/src/geo/__tests__/compile-map.test.ts +0 -1128
- package/src/geo/compile-map.ts +0 -1376
- package/src/geo/join.ts +0 -115
- package/src/geo/projections.ts +0 -65
- package/src/geo/types.ts +0 -26
- package/src/graphs/__tests__/community.test.ts +0 -228
- package/src/graphs/__tests__/compile-graph.test.ts +0 -396
- package/src/graphs/__tests__/encoding.test.ts +0 -415
- package/src/graphs/community.ts +0 -92
- package/src/graphs/compile-graph.ts +0 -329
- package/src/graphs/encoding.ts +0 -356
- package/src/graphs/types.ts +0 -106
- package/src/index.ts +0 -177
- package/src/layout/__tests__/metrics.test.ts +0 -97
- package/src/layout/__tests__/scales-highlight.test.ts +0 -192
- package/src/layout/axes/rotation.ts +0 -152
- package/src/layout/axes/thinning.ts +0 -98
- package/src/layout/axes/ticks.ts +0 -409
- package/src/layout/axes.ts +0 -757
- package/src/layout/dimensions.ts +0 -912
- package/src/layout/facet.ts +0 -91
- package/src/layout/gridlines.ts +0 -84
- package/src/layout/metrics.ts +0 -132
- package/src/layout/plan.ts +0 -631
- package/src/layout/scales.ts +0 -1053
- package/src/layout/shared.ts +0 -44
- package/src/legend/__tests__/continuous.test.ts +0 -318
- package/src/legend/__tests__/redundancy.test.ts +0 -108
- package/src/legend/__tests__/size.test.ts +0 -348
- package/src/legend/__tests__/suppression.test.ts +0 -382
- package/src/legend/compute.ts +0 -748
- package/src/legend/continuous.ts +0 -327
- package/src/legend/size.ts +0 -407
- package/src/legend/suppression.ts +0 -226
- package/src/legend/wrap.ts +0 -117
- package/src/sankey/__tests__/compile-sankey.test.ts +0 -458
- package/src/sankey/__tests__/layout.test.ts +0 -165
- package/src/sankey/__tests__/node-label-wrap.test.ts +0 -114
- package/src/sankey/__tests__/node-sort.test.ts +0 -45
- package/src/sankey/compile-sankey.ts +0 -706
- package/src/sankey/layout.ts +0 -184
- package/src/sankey/types.ts +0 -41
- package/src/tables/__tests__/bar-column.test.ts +0 -147
- package/src/tables/__tests__/category-colors.test.ts +0 -230
- package/src/tables/__tests__/compile-table.test.ts +0 -236
- package/src/tables/__tests__/format-cells.test.ts +0 -143
- package/src/tables/__tests__/heatmap.test.ts +0 -148
- package/src/tables/__tests__/pagination.test.ts +0 -78
- package/src/tables/__tests__/search.test.ts +0 -94
- package/src/tables/__tests__/sort.test.ts +0 -107
- package/src/tables/__tests__/sparkline.test.ts +0 -122
- package/src/tables/bar-column.ts +0 -94
- package/src/tables/category-colors.ts +0 -78
- package/src/tables/compile-table.ts +0 -427
- package/src/tables/format-cells.ts +0 -115
- package/src/tables/heatmap.ts +0 -141
- package/src/tables/pagination.ts +0 -46
- package/src/tables/search.ts +0 -66
- package/src/tables/sort.ts +0 -69
- package/src/tables/sparkline.ts +0 -113
- package/src/tables/utils.ts +0 -16
- package/src/tilemap/__tests__/compile-tilemap.test.ts +0 -576
- package/src/tilemap/compile-tilemap.ts +0 -755
- package/src/tilemap/layout.ts +0 -172
- package/src/tilemap/types.ts +0 -33
- package/src/tooltips/__tests__/compute.test.ts +0 -582
- package/src/tooltips/compute.ts +0 -700
- package/src/transforms/__tests__/aggregate.test.ts +0 -159
- package/src/transforms/__tests__/bin.test.ts +0 -88
- package/src/transforms/__tests__/calculate.test.ts +0 -146
- package/src/transforms/__tests__/conditional.test.ts +0 -127
- package/src/transforms/__tests__/filter-relative.test.ts +0 -202
- package/src/transforms/__tests__/filter.test.ts +0 -59
- package/src/transforms/__tests__/fold.test.ts +0 -79
- package/src/transforms/__tests__/index.test.ts +0 -93
- package/src/transforms/__tests__/predicates.test.ts +0 -193
- package/src/transforms/__tests__/timeunit.test.ts +0 -129
- package/src/transforms/__tests__/window.test.ts +0 -286
- package/src/transforms/aggregate.ts +0 -131
- package/src/transforms/bin.ts +0 -87
- package/src/transforms/calculate.ts +0 -60
- package/src/transforms/conditional.ts +0 -46
- package/src/transforms/filter.ts +0 -122
- package/src/transforms/fold.ts +0 -49
- package/src/transforms/index.ts +0 -60
- package/src/transforms/predicates.ts +0 -123
- package/src/transforms/timeunit.ts +0 -88
- package/src/transforms/window.ts +0 -185
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { computeSankeyLayout } from '../layout';
|
|
3
|
-
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
// Shared fixtures
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
|
|
8
|
-
/** Linear chain: A -> B -> C (3 columns) */
|
|
9
|
-
const linearData = [
|
|
10
|
-
{ source: 'A', target: 'B', value: 10 },
|
|
11
|
-
{ source: 'B', target: 'C', value: 10 },
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
/** Branching: A -> B, A -> C */
|
|
15
|
-
const branchData = [
|
|
16
|
-
{ source: 'A', target: 'B', value: 10 },
|
|
17
|
-
{ source: 'A', target: 'C', value: 20 },
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
const area = { x: 0, y: 0, width: 600, height: 400 };
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Tests
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
describe('computeSankeyLayout', () => {
|
|
27
|
-
it('linear chain produces correct column count (depths 0, 1, 2)', () => {
|
|
28
|
-
const { nodes } = computeSankeyLayout(
|
|
29
|
-
linearData,
|
|
30
|
-
'source',
|
|
31
|
-
'target',
|
|
32
|
-
'value',
|
|
33
|
-
area,
|
|
34
|
-
12,
|
|
35
|
-
16,
|
|
36
|
-
'justify',
|
|
37
|
-
6,
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const depths = new Set(nodes.map((n) => n.depth));
|
|
41
|
-
expect(depths.size).toBe(3);
|
|
42
|
-
expect(depths).toContain(0);
|
|
43
|
-
expect(depths).toContain(1);
|
|
44
|
-
expect(depths).toContain(2);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('nodeWidth is respected', () => {
|
|
48
|
-
const nodeWidth = 20;
|
|
49
|
-
const { nodes } = computeSankeyLayout(
|
|
50
|
-
linearData,
|
|
51
|
-
'source',
|
|
52
|
-
'target',
|
|
53
|
-
'value',
|
|
54
|
-
area,
|
|
55
|
-
nodeWidth,
|
|
56
|
-
16,
|
|
57
|
-
'justify',
|
|
58
|
-
6,
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
for (const node of nodes) {
|
|
62
|
-
const computedWidth = (node.x1 ?? 0) - (node.x0 ?? 0);
|
|
63
|
-
expect(computedWidth).toBeCloseTo(nodeWidth, 0);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('nodePadding ensures vertical gap between nodes in the same column', () => {
|
|
68
|
-
const padding = 24;
|
|
69
|
-
// Use left alignment for a deterministic test (justify may spread nodes across columns).
|
|
70
|
-
const { nodes: leftNodes } = computeSankeyLayout(
|
|
71
|
-
branchData,
|
|
72
|
-
'source',
|
|
73
|
-
'target',
|
|
74
|
-
'value',
|
|
75
|
-
area,
|
|
76
|
-
12,
|
|
77
|
-
padding,
|
|
78
|
-
'left',
|
|
79
|
-
6,
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const col1 = leftNodes.filter((n) => n.depth === 1).sort((a, b) => (a.y0 ?? 0) - (b.y0 ?? 0));
|
|
83
|
-
|
|
84
|
-
if (col1.length >= 2) {
|
|
85
|
-
for (let i = 0; i < col1.length - 1; i++) {
|
|
86
|
-
const gap = (col1[i + 1].y0 ?? 0) - (col1[i].y1 ?? 0);
|
|
87
|
-
expect(gap).toBeGreaterThanOrEqual(padding - 1); // Allow 1px rounding
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('different nodeAlign values produce different layouts', () => {
|
|
93
|
-
const justifyResult = computeSankeyLayout(
|
|
94
|
-
linearData,
|
|
95
|
-
'source',
|
|
96
|
-
'target',
|
|
97
|
-
'value',
|
|
98
|
-
area,
|
|
99
|
-
12,
|
|
100
|
-
16,
|
|
101
|
-
'justify',
|
|
102
|
-
6,
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
const leftResult = computeSankeyLayout(
|
|
106
|
-
linearData,
|
|
107
|
-
'source',
|
|
108
|
-
'target',
|
|
109
|
-
'value',
|
|
110
|
-
area,
|
|
111
|
-
12,
|
|
112
|
-
16,
|
|
113
|
-
'left',
|
|
114
|
-
6,
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
// For a linear chain with justify vs left, node positions may differ.
|
|
118
|
-
// At minimum, both should produce valid layouts with the right node count.
|
|
119
|
-
expect(justifyResult.nodes).toHaveLength(3);
|
|
120
|
-
expect(leftResult.nodes).toHaveLength(3);
|
|
121
|
-
|
|
122
|
-
// The x positions should be the same for a linear chain (both align
|
|
123
|
-
// left-to-right), but verify the layouts are structurally sound.
|
|
124
|
-
for (const node of justifyResult.nodes) {
|
|
125
|
-
expect(node.x0).toBeDefined();
|
|
126
|
-
expect(node.x1).toBeDefined();
|
|
127
|
-
expect(node.y0).toBeDefined();
|
|
128
|
-
expect(node.y1).toBeDefined();
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('returns correct link count', () => {
|
|
133
|
-
const { links } = computeSankeyLayout(
|
|
134
|
-
linearData,
|
|
135
|
-
'source',
|
|
136
|
-
'target',
|
|
137
|
-
'value',
|
|
138
|
-
area,
|
|
139
|
-
12,
|
|
140
|
-
16,
|
|
141
|
-
'justify',
|
|
142
|
-
6,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
expect(links).toHaveLength(2);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('link values match input data', () => {
|
|
149
|
-
const { links } = computeSankeyLayout(
|
|
150
|
-
linearData,
|
|
151
|
-
'source',
|
|
152
|
-
'target',
|
|
153
|
-
'value',
|
|
154
|
-
area,
|
|
155
|
-
12,
|
|
156
|
-
16,
|
|
157
|
-
'justify',
|
|
158
|
-
6,
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
for (const link of links) {
|
|
162
|
-
expect(link.value).toBe(10);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
});
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Characterization test for sankey node-label wrapping at narrow widths.
|
|
3
|
-
*
|
|
4
|
-
* Part of refactor/v7-cohesion step 1. The sankey engine computes a label's
|
|
5
|
-
* `maxWidth` based on available horizontal space from the label anchor to the
|
|
6
|
-
* container edge (see computeNodeLabel in compile-sankey.ts around line 137).
|
|
7
|
-
* When the container is narrow and node labels are long, the computed
|
|
8
|
-
* `maxWidth` falls below the estimated text width — the adapter then wraps the
|
|
9
|
-
* label into multiple lines via its own wrapText routine.
|
|
10
|
-
*
|
|
11
|
-
* Step 3 of the v7 plan extracts `wrapText` to core while keeping the sankey
|
|
12
|
-
* path heuristic-only. This test guards the engine's responsibility: produce a
|
|
13
|
-
* label `maxWidth` that forces wrapping on long-label narrow-width sankeys.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { estimateTextWidth, wrapText } from '@opendata-ai/openchart-core';
|
|
17
|
-
import { describe, expect, it } from 'vitest';
|
|
18
|
-
import { compileSankey } from '../../compile';
|
|
19
|
-
|
|
20
|
-
describe('sankey node-label wrapping', () => {
|
|
21
|
-
it('produces at least one node whose label maxWidth forces wrapping at narrow widths', () => {
|
|
22
|
-
const spec = {
|
|
23
|
-
type: 'sankey' as const,
|
|
24
|
-
data: [
|
|
25
|
-
{
|
|
26
|
-
from: 'Renewable Energy Sources International',
|
|
27
|
-
to: 'Grid Transmission Network',
|
|
28
|
-
amount: 100,
|
|
29
|
-
},
|
|
30
|
-
{ from: 'Grid Transmission Network', to: 'Residential Consumption Households', amount: 60 },
|
|
31
|
-
{ from: 'Grid Transmission Network', to: 'Commercial Industrial Facilities', amount: 40 },
|
|
32
|
-
],
|
|
33
|
-
encoding: {
|
|
34
|
-
source: { field: 'from', type: 'nominal' as const },
|
|
35
|
-
target: { field: 'to', type: 'nominal' as const },
|
|
36
|
-
value: { field: 'amount', type: 'quantitative' as const },
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// 300px wide total is cramped for the labels above across 3 columns,
|
|
41
|
-
// forcing per-label maxWidth to be well below the longest text width.
|
|
42
|
-
const layout = compileSankey(spec, { width: 300, height: 300 });
|
|
43
|
-
|
|
44
|
-
// Every node should have a finite maxWidth set (a proxy for "engine
|
|
45
|
-
// participated in wrap geometry") when overflow-compression kicked in.
|
|
46
|
-
const withMaxWidth = layout.nodes.filter(
|
|
47
|
-
(n) => typeof n.label.maxWidth === 'number' && (n.label.maxWidth as number) >= 0,
|
|
48
|
-
);
|
|
49
|
-
expect(withMaxWidth.length).toBe(layout.nodes.length);
|
|
50
|
-
|
|
51
|
-
// At least one node label requires wrapping: its natural text width
|
|
52
|
-
// exceeds the engine-assigned maxWidth at the same font size/weight.
|
|
53
|
-
const wrapsRequired = withMaxWidth.filter((n) => {
|
|
54
|
-
const tw = estimateTextWidth(
|
|
55
|
-
n.label.text,
|
|
56
|
-
n.label.style.fontSize,
|
|
57
|
-
n.label.style.fontWeight ?? 400,
|
|
58
|
-
);
|
|
59
|
-
return tw > (n.label.maxWidth as number);
|
|
60
|
-
});
|
|
61
|
-
expect(wrapsRequired.length).toBeGreaterThanOrEqual(1);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('does not force wrapping at generous widths for the same long-label spec', () => {
|
|
65
|
-
// Control: the same spec at a comfortable width does not require wrapping.
|
|
66
|
-
const spec = {
|
|
67
|
-
type: 'sankey' as const,
|
|
68
|
-
data: [
|
|
69
|
-
{
|
|
70
|
-
from: 'Renewable Energy Sources International',
|
|
71
|
-
to: 'Grid Transmission Network',
|
|
72
|
-
amount: 100,
|
|
73
|
-
},
|
|
74
|
-
{ from: 'Grid Transmission Network', to: 'Residential Consumption Households', amount: 60 },
|
|
75
|
-
],
|
|
76
|
-
encoding: {
|
|
77
|
-
source: { field: 'from', type: 'nominal' as const },
|
|
78
|
-
target: { field: 'to', type: 'nominal' as const },
|
|
79
|
-
value: { field: 'amount', type: 'quantitative' as const },
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const layout = compileSankey(spec, { width: 1600, height: 300 });
|
|
84
|
-
|
|
85
|
-
const wrapsRequired = layout.nodes.filter((n) => {
|
|
86
|
-
if (typeof n.label.maxWidth !== 'number') return false;
|
|
87
|
-
const tw = estimateTextWidth(
|
|
88
|
-
n.label.text,
|
|
89
|
-
n.label.style.fontSize,
|
|
90
|
-
n.label.style.fontWeight ?? 400,
|
|
91
|
-
);
|
|
92
|
-
return tw > (n.label.maxWidth as number);
|
|
93
|
-
});
|
|
94
|
-
expect(wrapsRequired.length).toBe(0);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
// Pinned behavior (refactor/v7-cohesion code review item 1):
|
|
99
|
-
// The shared wrapText in core/src/layout/text-wrap.ts splits on `\n` before
|
|
100
|
-
// word-wrapping. The previous sankey-local wrapText did not. Pin the new
|
|
101
|
-
// behavior so anyone relying on `\n` in node labels sees the multi-line break.
|
|
102
|
-
// ---------------------------------------------------------------------------
|
|
103
|
-
it('honors explicit `\\n` in node labels by producing multi-line wrap output', () => {
|
|
104
|
-
const lines = wrapText('First line\nSecond line', 12, 400, 1000);
|
|
105
|
-
|
|
106
|
-
expect(lines).toEqual(['First line', 'Second line']);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('preserves blank lines between consecutive `\\n` characters', () => {
|
|
110
|
-
const lines = wrapText('A\n\nB', 12, 400, 1000);
|
|
111
|
-
|
|
112
|
-
expect(lines).toEqual(['A', '', 'B']);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Characterization test for sankey explicit `nodeSort`.
|
|
3
|
-
*
|
|
4
|
-
* Part of refactor/v7-cohesion step 1. Pins the `nodeSort` handling in
|
|
5
|
-
* `packages/engine/src/sankey/layout.ts` around line 120, where an explicit
|
|
6
|
-
* ordered-ID array is translated into a d3-sankey `nodeSort` comparator so
|
|
7
|
-
* nodes stack top-to-bottom within each column following the user's order
|
|
8
|
-
* rather than d3-sankey's default (data-insertion / value-based) ordering.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, expect, it } from 'vitest';
|
|
12
|
-
import { computeSankeyLayout } from '../layout';
|
|
13
|
-
|
|
14
|
-
describe('sankey explicit nodeSort', () => {
|
|
15
|
-
it('orders nodes within a column according to the nodeSort array', () => {
|
|
16
|
-
// A, B, C all live in column 0 and flow into D in column 1.
|
|
17
|
-
// Data-insertion order is A, B, C (the default d3-sankey order varies
|
|
18
|
-
// with value anyway). Explicit nodeSort forces C, A, B top-to-bottom.
|
|
19
|
-
const data = [
|
|
20
|
-
{ source: 'A', target: 'D', value: 10 },
|
|
21
|
-
{ source: 'B', target: 'D', value: 20 },
|
|
22
|
-
{ source: 'C', target: 'D', value: 15 },
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
const { nodes } = computeSankeyLayout(
|
|
26
|
-
data,
|
|
27
|
-
'source',
|
|
28
|
-
'target',
|
|
29
|
-
'value',
|
|
30
|
-
{ x: 0, y: 0, width: 600, height: 400 },
|
|
31
|
-
12,
|
|
32
|
-
16,
|
|
33
|
-
'justify',
|
|
34
|
-
6,
|
|
35
|
-
['C', 'A', 'B'],
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// Collect column-0 nodes, sort them by vertical position, and read back IDs.
|
|
39
|
-
const col0 = nodes.filter((n) => n.depth === 0);
|
|
40
|
-
expect(col0.map((n) => n.id).sort()).toEqual(['A', 'B', 'C']);
|
|
41
|
-
|
|
42
|
-
const ordered = [...col0].sort((a, b) => (a.y0 ?? 0) - (b.y0 ?? 0)).map((n) => n.id);
|
|
43
|
-
expect(ordered).toEqual(['C', 'A', 'B']);
|
|
44
|
-
});
|
|
45
|
-
});
|