@opendata-ai/openchart-engine 8.0.0-rc.9 → 8.0.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 +149 -12
- package/dist/index.js +804 -155
- 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,396 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { compileGraph } from '../compile-graph';
|
|
3
|
-
|
|
4
|
-
// ---------------------------------------------------------------------------
|
|
5
|
-
// Shared fixtures
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
|
|
8
|
-
function makeBasicGraphSpec() {
|
|
9
|
-
return {
|
|
10
|
-
type: 'graph' as const,
|
|
11
|
-
nodes: [
|
|
12
|
-
{ id: 'a', group: 'X', value: 10, name: 'Alice' },
|
|
13
|
-
{ id: 'b', group: 'X', value: 20, name: 'Bob' },
|
|
14
|
-
{ id: 'c', group: 'Y', value: 30, name: 'Carol' },
|
|
15
|
-
{ id: 'd', group: 'Y', value: 40, name: 'Dave' },
|
|
16
|
-
],
|
|
17
|
-
edges: [
|
|
18
|
-
{ source: 'a', target: 'b', weight: 1 },
|
|
19
|
-
{ source: 'b', target: 'c', weight: 2 },
|
|
20
|
-
{ source: 'c', target: 'd', weight: 3 },
|
|
21
|
-
{ source: 'a', target: 'c', weight: 1 },
|
|
22
|
-
],
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function makeEncodedGraphSpec() {
|
|
27
|
-
return {
|
|
28
|
-
...makeBasicGraphSpec(),
|
|
29
|
-
encoding: {
|
|
30
|
-
nodeSize: { field: 'value', type: 'quantitative' as const },
|
|
31
|
-
nodeColor: { field: 'group', type: 'nominal' as const },
|
|
32
|
-
nodeLabel: { field: 'name' },
|
|
33
|
-
edgeWidth: { field: 'weight', type: 'quantitative' as const },
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function makeClusteredGraphSpec() {
|
|
39
|
-
return {
|
|
40
|
-
...makeBasicGraphSpec(),
|
|
41
|
-
layout: {
|
|
42
|
-
type: 'force' as const,
|
|
43
|
-
clustering: { field: 'group' },
|
|
44
|
-
chargeStrength: -200,
|
|
45
|
-
linkDistance: 50,
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const compileOptions = { width: 600, height: 400 };
|
|
51
|
-
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
// Full pipeline tests
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
|
|
56
|
-
describe('compileGraph', () => {
|
|
57
|
-
it('returns a valid GraphCompilation shape', () => {
|
|
58
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
59
|
-
|
|
60
|
-
expect(result.nodes).toBeDefined();
|
|
61
|
-
expect(result.edges).toBeDefined();
|
|
62
|
-
expect(result.legend).toBeDefined();
|
|
63
|
-
expect(result.chrome).toBeDefined();
|
|
64
|
-
expect(result.tooltipDescriptors).toBeDefined();
|
|
65
|
-
expect(result.a11y).toBeDefined();
|
|
66
|
-
expect(result.theme).toBeDefined();
|
|
67
|
-
expect(result.dimensions).toBeDefined();
|
|
68
|
-
expect(result.simulationConfig).toBeDefined();
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('compiles correct number of nodes and edges', () => {
|
|
72
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
73
|
-
|
|
74
|
-
expect(result.nodes).toHaveLength(4);
|
|
75
|
-
expect(result.edges).toHaveLength(4);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('has correct total dimensions', () => {
|
|
79
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
80
|
-
|
|
81
|
-
expect(result.dimensions.width).toBe(600);
|
|
82
|
-
expect(result.dimensions.height).toBe(400);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('nodes have resolved visual properties', () => {
|
|
86
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
87
|
-
|
|
88
|
-
for (const node of result.nodes) {
|
|
89
|
-
expect(node.id).toBeTruthy();
|
|
90
|
-
expect(node.radius).toBeGreaterThan(0);
|
|
91
|
-
expect(node.fill).toBeTruthy();
|
|
92
|
-
expect(node.stroke).toBeTruthy();
|
|
93
|
-
expect(node.strokeWidth).toBeGreaterThan(0);
|
|
94
|
-
expect(node.data).toBeDefined();
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('edges have resolved visual properties', () => {
|
|
99
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
100
|
-
|
|
101
|
-
for (const edge of result.edges) {
|
|
102
|
-
expect(edge.source).toBeTruthy();
|
|
103
|
-
expect(edge.target).toBeTruthy();
|
|
104
|
-
expect(edge.stroke).toBeTruthy();
|
|
105
|
-
expect(edge.strokeWidth).toBeGreaterThan(0);
|
|
106
|
-
expect(edge.style).toBe('solid');
|
|
107
|
-
expect(edge.data).toBeDefined();
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe('with encoding', () => {
|
|
112
|
-
it('applies nodeSize encoding to produce varying radii', () => {
|
|
113
|
-
const result = compileGraph(makeEncodedGraphSpec(), compileOptions);
|
|
114
|
-
|
|
115
|
-
const radii = result.nodes.map((n) => n.radius);
|
|
116
|
-
const uniqueRadii = new Set(radii);
|
|
117
|
-
expect(uniqueRadii.size).toBeGreaterThan(1);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('applies nodeColor encoding to produce varying fill colors', () => {
|
|
121
|
-
const result = compileGraph(makeEncodedGraphSpec(), compileOptions);
|
|
122
|
-
|
|
123
|
-
const groupXNode = result.nodes.find((n) => n.data.group === 'X')!;
|
|
124
|
-
const groupYNode = result.nodes.find((n) => n.data.group === 'Y')!;
|
|
125
|
-
expect(groupXNode.fill).not.toBe(groupYNode.fill);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('applies nodeLabel encoding for labels', () => {
|
|
129
|
-
const result = compileGraph(makeEncodedGraphSpec(), compileOptions);
|
|
130
|
-
|
|
131
|
-
const alice = result.nodes.find((n) => n.id === 'a')!;
|
|
132
|
-
expect(alice.label).toBe('Alice');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('applies edgeWidth encoding to produce varying stroke widths', () => {
|
|
136
|
-
const result = compileGraph(makeEncodedGraphSpec(), compileOptions);
|
|
137
|
-
|
|
138
|
-
const widths = result.edges.map((e) => e.strokeWidth);
|
|
139
|
-
const uniqueWidths = new Set(widths);
|
|
140
|
-
expect(uniqueWidths.size).toBeGreaterThan(1);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe('with explicit scale domain/range', () => {
|
|
145
|
-
it('uses explicit domain and range for nominal nodeColor', () => {
|
|
146
|
-
const spec = {
|
|
147
|
-
...makeBasicGraphSpec(),
|
|
148
|
-
encoding: {
|
|
149
|
-
nodeColor: {
|
|
150
|
-
field: 'group',
|
|
151
|
-
type: 'nominal' as const,
|
|
152
|
-
scale: {
|
|
153
|
-
domain: ['X', 'Y'],
|
|
154
|
-
range: ['#ff0000', '#00ff00'],
|
|
155
|
-
},
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
const result = compileGraph(spec, compileOptions);
|
|
160
|
-
|
|
161
|
-
const xNode = result.nodes.find((n) => n.data.group === 'X')!;
|
|
162
|
-
const yNode = result.nodes.find((n) => n.data.group === 'Y')!;
|
|
163
|
-
expect(xNode.fill).toBe('#ff0000');
|
|
164
|
-
expect(yNode.fill).toBe('#00ff00');
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('falls back to auto-derived domain when scale is omitted', () => {
|
|
168
|
-
const result = compileGraph(makeEncodedGraphSpec(), compileOptions);
|
|
169
|
-
|
|
170
|
-
const xNode = result.nodes.find((n) => n.data.group === 'X')!;
|
|
171
|
-
const yNode = result.nodes.find((n) => n.data.group === 'Y')!;
|
|
172
|
-
// Should still produce different colors (auto-derived)
|
|
173
|
-
expect(xNode.fill).not.toBe(yNode.fill);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('explicit domain controls color ordering', () => {
|
|
177
|
-
// Reversed domain order should swap colors
|
|
178
|
-
const spec = {
|
|
179
|
-
...makeBasicGraphSpec(),
|
|
180
|
-
encoding: {
|
|
181
|
-
nodeColor: {
|
|
182
|
-
field: 'group',
|
|
183
|
-
type: 'nominal' as const,
|
|
184
|
-
scale: {
|
|
185
|
-
domain: ['Y', 'X'],
|
|
186
|
-
range: ['#ff0000', '#00ff00'],
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
const result = compileGraph(spec, compileOptions);
|
|
192
|
-
|
|
193
|
-
const xNode = result.nodes.find((n) => n.data.group === 'X')!;
|
|
194
|
-
const yNode = result.nodes.find((n) => n.data.group === 'Y')!;
|
|
195
|
-
expect(xNode.fill).toBe('#00ff00');
|
|
196
|
-
expect(yNode.fill).toBe('#ff0000');
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
describe('with community clustering', () => {
|
|
201
|
-
it('assigns communities to nodes', () => {
|
|
202
|
-
const result = compileGraph(makeClusteredGraphSpec(), compileOptions);
|
|
203
|
-
|
|
204
|
-
const groupXNodes = result.nodes.filter((n) => n.community === 'X');
|
|
205
|
-
const groupYNodes = result.nodes.filter((n) => n.community === 'Y');
|
|
206
|
-
expect(groupXNodes).toHaveLength(2);
|
|
207
|
-
expect(groupYNodes).toHaveLength(2);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('community colors override node colors', () => {
|
|
211
|
-
const result = compileGraph(makeClusteredGraphSpec(), compileOptions);
|
|
212
|
-
|
|
213
|
-
// Nodes in the same community should share a color
|
|
214
|
-
const xNodes = result.nodes.filter((n) => n.community === 'X');
|
|
215
|
-
expect(xNodes[0].fill).toBe(xNodes[1].fill);
|
|
216
|
-
|
|
217
|
-
// Different communities should have different colors
|
|
218
|
-
const yNode = result.nodes.find((n) => n.community === 'Y')!;
|
|
219
|
-
expect(xNodes[0].fill).not.toBe(yNode.fill);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('legend entries match communities', () => {
|
|
223
|
-
const result = compileGraph(makeClusteredGraphSpec(), compileOptions);
|
|
224
|
-
|
|
225
|
-
expect(result.legend.entries).toHaveLength(2);
|
|
226
|
-
const labels = result.legend.entries.map((e) => e.label).sort();
|
|
227
|
-
expect(labels).toEqual(['X', 'Y']);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
describe('legend', () => {
|
|
232
|
-
it('is empty when there is no color encoding and no clustering', () => {
|
|
233
|
-
// Every node shares one fill, so a legend would list each node label
|
|
234
|
-
// against an identical swatch and communicate nothing.
|
|
235
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
236
|
-
|
|
237
|
-
expect(result.legend.entries).toEqual([]);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it('entries come from the nodeColor field, not node labels', () => {
|
|
241
|
-
const result = compileGraph(makeEncodedGraphSpec(), compileOptions);
|
|
242
|
-
|
|
243
|
-
const labels = result.legend.entries.map((e) => e.label).sort();
|
|
244
|
-
expect(labels).toEqual(['X', 'Y']);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('is empty when the nodeColor field has only one category', () => {
|
|
248
|
-
const spec = makeEncodedGraphSpec();
|
|
249
|
-
for (const node of spec.nodes) node.group = 'X';
|
|
250
|
-
const result = compileGraph(spec, compileOptions);
|
|
251
|
-
|
|
252
|
-
expect(result.legend.entries).toEqual([]);
|
|
253
|
-
});
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
describe('tooltips', () => {
|
|
257
|
-
it('generates tooltip descriptors for each node', () => {
|
|
258
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
259
|
-
|
|
260
|
-
expect(result.tooltipDescriptors.size).toBe(4);
|
|
261
|
-
expect(result.tooltipDescriptors.has('a')).toBe(true);
|
|
262
|
-
expect(result.tooltipDescriptors.has('b')).toBe(true);
|
|
263
|
-
expect(result.tooltipDescriptors.has('c')).toBe(true);
|
|
264
|
-
expect(result.tooltipDescriptors.has('d')).toBe(true);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('tooltip has a title and data fields', () => {
|
|
268
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
269
|
-
|
|
270
|
-
const tooltip = result.tooltipDescriptors.get('a')!;
|
|
271
|
-
expect(tooltip.title).toBeTruthy();
|
|
272
|
-
expect(tooltip.fields.length).toBeGreaterThan(0);
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it('tooltip includes community when clustering is active', () => {
|
|
276
|
-
const result = compileGraph(makeClusteredGraphSpec(), compileOptions);
|
|
277
|
-
|
|
278
|
-
const tooltip = result.tooltipDescriptors.get('a')!;
|
|
279
|
-
const communityField = tooltip.fields.find((f) => f.label === 'Community');
|
|
280
|
-
expect(communityField).toBeDefined();
|
|
281
|
-
expect(communityField!.value).toBe('X');
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
describe('a11y', () => {
|
|
286
|
-
it('generates descriptive alt text', () => {
|
|
287
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
288
|
-
|
|
289
|
-
expect(result.a11y.altText).toContain('4 nodes');
|
|
290
|
-
expect(result.a11y.altText).toContain('4 edges');
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it('alt text mentions communities when clustering is active', () => {
|
|
294
|
-
const result = compileGraph(makeClusteredGraphSpec(), compileOptions);
|
|
295
|
-
|
|
296
|
-
expect(result.a11y.altText).toContain('communities');
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
it('has a data table fallback', () => {
|
|
300
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
301
|
-
|
|
302
|
-
expect(result.a11y.dataTableFallback).toHaveLength(4);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
it('is keyboard navigable when nodes exist', () => {
|
|
306
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
307
|
-
|
|
308
|
-
expect(result.a11y.keyboardNavigable).toBe(true);
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
describe('simulationConfig', () => {
|
|
313
|
-
it('reflects default layout parameters', () => {
|
|
314
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
315
|
-
|
|
316
|
-
expect(result.simulationConfig.chargeStrength).toBe(-300);
|
|
317
|
-
expect(result.simulationConfig.linkDistance).toBe(30);
|
|
318
|
-
expect(result.simulationConfig.alphaDecay).toBeCloseTo(0.0228);
|
|
319
|
-
expect(result.simulationConfig.velocityDecay).toBeCloseTo(0.4);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
it('reflects custom layout parameters', () => {
|
|
323
|
-
const result = compileGraph(makeClusteredGraphSpec(), compileOptions);
|
|
324
|
-
|
|
325
|
-
expect(result.simulationConfig.chargeStrength).toBe(-200);
|
|
326
|
-
expect(result.simulationConfig.linkDistance).toBe(50);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
it('includes clustering config when set', () => {
|
|
330
|
-
const result = compileGraph(makeClusteredGraphSpec(), compileOptions);
|
|
331
|
-
|
|
332
|
-
expect(result.simulationConfig.clustering).not.toBeNull();
|
|
333
|
-
expect(result.simulationConfig.clustering!.field).toBe('group');
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it('clustering is null when not set', () => {
|
|
337
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
338
|
-
|
|
339
|
-
expect(result.simulationConfig.clustering).toBeNull();
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it('collision radius accounts for max node radius', () => {
|
|
343
|
-
const result = compileGraph(makeBasicGraphSpec(), compileOptions);
|
|
344
|
-
|
|
345
|
-
const maxRadius = Math.max(...result.nodes.map((n) => n.radius));
|
|
346
|
-
expect(result.simulationConfig.collisionRadius).toBe(maxRadius + 2);
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
describe('chrome', () => {
|
|
351
|
-
it('resolves chrome text elements', () => {
|
|
352
|
-
const spec = {
|
|
353
|
-
...makeBasicGraphSpec(),
|
|
354
|
-
chrome: {
|
|
355
|
-
title: 'Network Graph',
|
|
356
|
-
source: 'Test Data',
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
const result = compileGraph(spec, compileOptions);
|
|
360
|
-
|
|
361
|
-
expect(result.chrome.title).toBeDefined();
|
|
362
|
-
expect(result.chrome.title!.text).toBe('Network Graph');
|
|
363
|
-
expect(result.chrome.source).toBeDefined();
|
|
364
|
-
expect(result.chrome.source!.text).toBe('Test Data');
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
describe('dark mode', () => {
|
|
369
|
-
it('applies dark mode theme when option is set', () => {
|
|
370
|
-
const result = compileGraph(makeBasicGraphSpec(), { ...compileOptions, darkMode: true });
|
|
371
|
-
|
|
372
|
-
expect(result.theme.isDark).toBe(true);
|
|
373
|
-
});
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
describe('error handling', () => {
|
|
377
|
-
it('throws for non-graph specs', () => {
|
|
378
|
-
const chartSpec = {
|
|
379
|
-
mark: 'point' as const,
|
|
380
|
-
data: [{ x: 1, y: 2 }],
|
|
381
|
-
encoding: {
|
|
382
|
-
x: { field: 'x', type: 'quantitative' as const },
|
|
383
|
-
y: { field: 'y', type: 'quantitative' as const },
|
|
384
|
-
},
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
expect(() => compileGraph(chartSpec, compileOptions)).toThrow(
|
|
388
|
-
/compileGraph received a non-graph spec/,
|
|
389
|
-
);
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it('throws for invalid specs', () => {
|
|
393
|
-
expect(() => compileGraph({}, compileOptions)).toThrow();
|
|
394
|
-
});
|
|
395
|
-
});
|
|
396
|
-
});
|