@opendata-ai/openchart-engine 8.0.0-rc.8 → 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 +894 -171
- 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 -2339
- 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 -626
- 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 -1450
- 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 -1004
- package/src/geo/compile-map.ts +0 -1341
- package/src/geo/join.ts +0 -115
- package/src/geo/projections.ts +0 -45
- 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 -747
- 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,116 +0,0 @@
|
|
|
1
|
-
import type { LayoutStrategy, Mark, Rect } from '@opendata-ai/openchart-core';
|
|
2
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
-
import type { NormalizedChartSpec } from '../../compiler/types';
|
|
4
|
-
import type { ResolvedScales } from '../../layout/scales';
|
|
5
|
-
import { registerBuiltinRenderers } from '../builtin';
|
|
6
|
-
import { clearRenderers, getChartRenderer, registerChartRenderer } from '../registry';
|
|
7
|
-
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
// Fixtures
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A minimal renderer that returns a single rect mark.
|
|
14
|
-
* Used to verify the registry lifecycle without needing real chart logic.
|
|
15
|
-
*/
|
|
16
|
-
function stubRenderer(
|
|
17
|
-
_spec: NormalizedChartSpec,
|
|
18
|
-
_scales: ResolvedScales,
|
|
19
|
-
_chartArea: Rect,
|
|
20
|
-
_strategy: LayoutStrategy,
|
|
21
|
-
): Mark[] {
|
|
22
|
-
return [
|
|
23
|
-
{
|
|
24
|
-
type: 'rect',
|
|
25
|
-
x: 10,
|
|
26
|
-
y: 20,
|
|
27
|
-
width: 100,
|
|
28
|
-
height: 50,
|
|
29
|
-
fill: '#ff0000',
|
|
30
|
-
data: {},
|
|
31
|
-
aria: { label: 'stub mark' },
|
|
32
|
-
},
|
|
33
|
-
];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
// Tests
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
|
|
40
|
-
describe('chart renderer registry', () => {
|
|
41
|
-
afterEach(() => {
|
|
42
|
-
// Clear test-registered renderers AND restore the built-in set. The
|
|
43
|
-
// built-ins register as a module side-effect of importing `compile.ts`,
|
|
44
|
-
// so once cleared they don't come back on their own — leaving later
|
|
45
|
-
// tests in this suite with an empty registry.
|
|
46
|
-
clearRenderers();
|
|
47
|
-
registerBuiltinRenderers();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('returns undefined for an unregistered chart type', () => {
|
|
51
|
-
expect(getChartRenderer('nonexistent')).toBeUndefined();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('registers and retrieves a renderer by type', () => {
|
|
55
|
-
registerChartRenderer('test-type', stubRenderer);
|
|
56
|
-
|
|
57
|
-
const retrieved = getChartRenderer('test-type');
|
|
58
|
-
expect(retrieved).toBe(stubRenderer);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('registered renderer produces marks when called', () => {
|
|
62
|
-
registerChartRenderer('test-type', stubRenderer);
|
|
63
|
-
|
|
64
|
-
const renderer = getChartRenderer('test-type')!;
|
|
65
|
-
const marks = renderer(
|
|
66
|
-
{} as NormalizedChartSpec,
|
|
67
|
-
{} as ResolvedScales,
|
|
68
|
-
{ x: 0, y: 0, width: 600, height: 400 },
|
|
69
|
-
{} as LayoutStrategy,
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
expect(marks).toHaveLength(1);
|
|
73
|
-
expect(marks[0].type).toBe('rect');
|
|
74
|
-
if (marks[0].type === 'rect') {
|
|
75
|
-
expect(marks[0].width).toBe(100);
|
|
76
|
-
expect(marks[0].fill).toBe('#ff0000');
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('overwrites a previously registered renderer for the same type', () => {
|
|
81
|
-
const secondRenderer = () => [] as Mark[];
|
|
82
|
-
|
|
83
|
-
registerChartRenderer('test-type', stubRenderer);
|
|
84
|
-
registerChartRenderer('test-type', secondRenderer);
|
|
85
|
-
|
|
86
|
-
expect(getChartRenderer('test-type')).toBe(secondRenderer);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('clearRenderers removes all registered renderers', () => {
|
|
90
|
-
registerChartRenderer('type-a', stubRenderer);
|
|
91
|
-
registerChartRenderer('type-b', stubRenderer);
|
|
92
|
-
|
|
93
|
-
// Both should be registered
|
|
94
|
-
expect(getChartRenderer('type-a')).toBe(stubRenderer);
|
|
95
|
-
expect(getChartRenderer('type-b')).toBe(stubRenderer);
|
|
96
|
-
|
|
97
|
-
clearRenderers();
|
|
98
|
-
|
|
99
|
-
// Both should be gone
|
|
100
|
-
expect(getChartRenderer('type-a')).toBeUndefined();
|
|
101
|
-
expect(getChartRenderer('type-b')).toBeUndefined();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('multiple types can be registered independently', () => {
|
|
105
|
-
const rendererA = () => [] as Mark[];
|
|
106
|
-
const rendererB = () => [] as Mark[];
|
|
107
|
-
|
|
108
|
-
registerChartRenderer('type-a', rendererA);
|
|
109
|
-
registerChartRenderer('type-b', rendererB);
|
|
110
|
-
|
|
111
|
-
expect(getChartRenderer('type-a')).toBe(rendererA);
|
|
112
|
-
expect(getChartRenderer('type-b')).toBe(rendererB);
|
|
113
|
-
// Unregistered type still returns undefined
|
|
114
|
-
expect(getChartRenderer('type-c')).toBeUndefined();
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { sortByField } from '../utils';
|
|
3
|
-
|
|
4
|
-
describe('sortByField', () => {
|
|
5
|
-
// -----------------------------------------------------------------------
|
|
6
|
-
// Numeric sorting
|
|
7
|
-
// -----------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
it('sorts numeric values ascending', () => {
|
|
10
|
-
const data = [{ v: 30 }, { v: 10 }, { v: 20 }];
|
|
11
|
-
const sorted = sortByField(data, 'v');
|
|
12
|
-
expect(sorted.map((r) => r.v)).toEqual([10, 20, 30]);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('sorts negative and positive numbers correctly', () => {
|
|
16
|
-
const data = [{ v: 5 }, { v: -3 }, { v: 0 }, { v: -1 }, { v: 2 }];
|
|
17
|
-
const sorted = sortByField(data, 'v');
|
|
18
|
-
expect(sorted.map((r) => r.v)).toEqual([-3, -1, 0, 2, 5]);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('sorts floating point numbers correctly', () => {
|
|
22
|
-
const data = [{ v: 1.5 }, { v: 1.1 }, { v: 1.9 }, { v: 1.3 }];
|
|
23
|
-
const sorted = sortByField(data, 'v');
|
|
24
|
-
expect(sorted.map((r) => r.v)).toEqual([1.1, 1.3, 1.5, 1.9]);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// -----------------------------------------------------------------------
|
|
28
|
-
// Date string sorting (ISO format)
|
|
29
|
-
// -----------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
it('sorts ISO date strings (YYYY-MM-DD) lexicographically', () => {
|
|
32
|
-
const data = [
|
|
33
|
-
{ date: '2022-01-01', v: 1 },
|
|
34
|
-
{ date: '2020-06-15', v: 2 },
|
|
35
|
-
{ date: '2021-03-10', v: 3 },
|
|
36
|
-
];
|
|
37
|
-
const sorted = sortByField(data, 'date');
|
|
38
|
-
expect(sorted.map((r) => r.date)).toEqual(['2020-06-15', '2021-03-10', '2022-01-01']);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('sorts dates within the same year by month and day', () => {
|
|
42
|
-
const data = [
|
|
43
|
-
{ date: '2020-12-25' },
|
|
44
|
-
{ date: '2020-01-01' },
|
|
45
|
-
{ date: '2020-06-15' },
|
|
46
|
-
{ date: '2020-03-20' },
|
|
47
|
-
];
|
|
48
|
-
const sorted = sortByField(data, 'date');
|
|
49
|
-
expect(sorted.map((r) => r.date)).toEqual([
|
|
50
|
-
'2020-01-01',
|
|
51
|
-
'2020-03-20',
|
|
52
|
-
'2020-06-15',
|
|
53
|
-
'2020-12-25',
|
|
54
|
-
]);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('sorts full ISO datetime strings with time component', () => {
|
|
58
|
-
const data = [
|
|
59
|
-
{ ts: '2020-01-01T23:59:59Z' },
|
|
60
|
-
{ ts: '2020-01-01T00:00:00Z' },
|
|
61
|
-
{ ts: '2020-01-01T12:30:00Z' },
|
|
62
|
-
];
|
|
63
|
-
const sorted = sortByField(data, 'ts');
|
|
64
|
-
expect(sorted.map((r) => r.ts)).toEqual([
|
|
65
|
-
'2020-01-01T00:00:00Z',
|
|
66
|
-
'2020-01-01T12:30:00Z',
|
|
67
|
-
'2020-01-01T23:59:59Z',
|
|
68
|
-
]);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('sorts reverse-ordered dates correctly', () => {
|
|
72
|
-
const data = [
|
|
73
|
-
{ date: '2025-01-01' },
|
|
74
|
-
{ date: '2024-01-01' },
|
|
75
|
-
{ date: '2023-01-01' },
|
|
76
|
-
{ date: '2022-01-01' },
|
|
77
|
-
];
|
|
78
|
-
const sorted = sortByField(data, 'date');
|
|
79
|
-
expect(sorted.map((r) => r.date)).toEqual([
|
|
80
|
-
'2022-01-01',
|
|
81
|
-
'2023-01-01',
|
|
82
|
-
'2024-01-01',
|
|
83
|
-
'2025-01-01',
|
|
84
|
-
]);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// -----------------------------------------------------------------------
|
|
88
|
-
// String-encoded numbers (year columns from CSV data)
|
|
89
|
-
// -----------------------------------------------------------------------
|
|
90
|
-
|
|
91
|
-
it('sorts string-encoded year numbers numerically', () => {
|
|
92
|
-
const data = [{ year: '2022' }, { year: '2020' }, { year: '2021' }];
|
|
93
|
-
const sorted = sortByField(data, 'year');
|
|
94
|
-
expect(sorted.map((r) => r.year)).toEqual(['2020', '2021', '2022']);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('sorts string-encoded decimal numbers numerically', () => {
|
|
98
|
-
const data = [{ v: '10.5' }, { v: '2.3' }, { v: '100.1' }];
|
|
99
|
-
const sorted = sortByField(data, 'v');
|
|
100
|
-
expect(sorted.map((r) => r.v)).toEqual(['2.3', '10.5', '100.1']);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// -----------------------------------------------------------------------
|
|
104
|
-
// Date objects
|
|
105
|
-
// -----------------------------------------------------------------------
|
|
106
|
-
|
|
107
|
-
it('sorts Date objects by timestamp', () => {
|
|
108
|
-
const d1 = new Date('2020-06-15T00:00:00');
|
|
109
|
-
const d2 = new Date('2021-06-15T00:00:00');
|
|
110
|
-
const d3 = new Date('2022-06-15T00:00:00');
|
|
111
|
-
const data = [{ d: d3 }, { d: d1 }, { d: d2 }];
|
|
112
|
-
const sorted = sortByField(data, 'd');
|
|
113
|
-
expect(sorted.map((r) => r.d)).toEqual([d1, d2, d3]);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// -----------------------------------------------------------------------
|
|
117
|
-
// Null / undefined handling
|
|
118
|
-
// -----------------------------------------------------------------------
|
|
119
|
-
|
|
120
|
-
it('pushes nulls to the end', () => {
|
|
121
|
-
const data = [{ v: null }, { v: 10 }, { v: 30 }, { v: null }, { v: 20 }];
|
|
122
|
-
const sorted = sortByField(data, 'v');
|
|
123
|
-
expect(sorted.map((r) => r.v)).toEqual([10, 20, 30, null, null]);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('pushes undefined (missing field) to the end', () => {
|
|
127
|
-
const data = [{ other: 1 }, { v: 10, other: 2 }, { v: 20, other: 3 }];
|
|
128
|
-
const sorted = sortByField(data, 'v');
|
|
129
|
-
expect(sorted.map((r) => r.v)).toEqual([10, 20, undefined]);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('handles all-null values without crashing', () => {
|
|
133
|
-
const data = [{ v: null }, { v: null }, { v: null }];
|
|
134
|
-
const sorted = sortByField(data, 'v');
|
|
135
|
-
expect(sorted).toHaveLength(3);
|
|
136
|
-
expect(sorted.every((r) => r.v === null)).toBe(true);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// -----------------------------------------------------------------------
|
|
140
|
-
// Duplicate values
|
|
141
|
-
// -----------------------------------------------------------------------
|
|
142
|
-
|
|
143
|
-
it('handles duplicate values preserving both rows', () => {
|
|
144
|
-
const data = [{ v: 20 }, { v: 10 }, { v: 20 }, { v: 10 }];
|
|
145
|
-
const sorted = sortByField(data, 'v');
|
|
146
|
-
expect(sorted.map((r) => r.v)).toEqual([10, 10, 20, 20]);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('handles duplicate date strings', () => {
|
|
150
|
-
const data = [
|
|
151
|
-
{ date: '2021-01-01', id: 'c' },
|
|
152
|
-
{ date: '2020-01-01', id: 'b' },
|
|
153
|
-
{ date: '2021-01-01', id: 'a' },
|
|
154
|
-
];
|
|
155
|
-
const sorted = sortByField(data, 'date');
|
|
156
|
-
// Both 2021 rows follow the 2020 row
|
|
157
|
-
expect(sorted[0].date).toBe('2020-01-01');
|
|
158
|
-
expect(sorted[1].date).toBe('2021-01-01');
|
|
159
|
-
expect(sorted[2].date).toBe('2021-01-01');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// -----------------------------------------------------------------------
|
|
163
|
-
// Edge cases
|
|
164
|
-
// -----------------------------------------------------------------------
|
|
165
|
-
|
|
166
|
-
it('returns a new array (no mutation)', () => {
|
|
167
|
-
const data = [{ v: 30 }, { v: 10 }];
|
|
168
|
-
const sorted = sortByField(data, 'v');
|
|
169
|
-
expect(sorted).not.toBe(data);
|
|
170
|
-
expect(data[0].v).toBe(30);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('handles empty array', () => {
|
|
174
|
-
expect(sortByField([], 'v')).toEqual([]);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('handles single element', () => {
|
|
178
|
-
const data = [{ v: 42 }];
|
|
179
|
-
const sorted = sortByField(data, 'v');
|
|
180
|
-
expect(sorted).toHaveLength(1);
|
|
181
|
-
expect(sorted[0].v).toBe(42);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('handles already-sorted data', () => {
|
|
185
|
-
const data = [{ v: 1 }, { v: 2 }, { v: 3 }];
|
|
186
|
-
const sorted = sortByField(data, 'v');
|
|
187
|
-
expect(sorted.map((r) => r.v)).toEqual([1, 2, 3]);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('sorts pure string values lexicographically', () => {
|
|
191
|
-
const data = [{ name: 'cherry' }, { name: 'apple' }, { name: 'banana' }];
|
|
192
|
-
const sorted = sortByField(data, 'name');
|
|
193
|
-
expect(sorted.map((r) => r.name)).toEqual(['apple', 'banana', 'cherry']);
|
|
194
|
-
});
|
|
195
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { filterByDensity } from '../density-filter';
|
|
3
|
-
|
|
4
|
-
describe('filterByDensity', () => {
|
|
5
|
-
const marks = ['a', 'b', 'c', 'd'];
|
|
6
|
-
|
|
7
|
-
it("returns [] for 'none'", () => {
|
|
8
|
-
expect(filterByDensity(marks, 'none')).toEqual([]);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("returns first + last for 'endpoints'", () => {
|
|
12
|
-
expect(filterByDensity(marks, 'endpoints')).toEqual(['a', 'd']);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("returns marks unchanged for 'all'", () => {
|
|
16
|
-
expect(filterByDensity(marks, 'all')).toBe(marks);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("returns marks unchanged for 'auto'", () => {
|
|
20
|
-
expect(filterByDensity(marks, 'auto')).toBe(marks);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("returns single-element array unchanged for 'endpoints'", () => {
|
|
24
|
-
const single = ['only'];
|
|
25
|
-
expect(filterByDensity(single, 'endpoints')).toBe(single);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("returns empty array unchanged for 'endpoints'", () => {
|
|
29
|
-
const empty: string[] = [];
|
|
30
|
-
expect(filterByDensity(empty, 'endpoints')).toBe(empty);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared density filter for data labels.
|
|
3
|
-
*
|
|
4
|
-
* Maps a `LabelDensity` setting to the subset of marks eligible for
|
|
5
|
-
* labeling. The `'auto'` branch is a pass-through — auto resolution
|
|
6
|
-
* happens upstream in the per-chart label modules (typically via
|
|
7
|
-
* collision detection on the full candidate set).
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type { LabelDensity } from '@opendata-ai/openchart-core';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Filter a mark array by label density.
|
|
14
|
-
*
|
|
15
|
-
* - `'none'` returns `[]` (no labels)
|
|
16
|
-
* - `'endpoints'` returns first + last marks when `marks.length > 1`,
|
|
17
|
-
* otherwise the input unchanged (preserves single-element arrays as-is)
|
|
18
|
-
* - `'all'` and `'auto'` return the input unchanged
|
|
19
|
-
*/
|
|
20
|
-
export function filterByDensity<T>(marks: T[], density: LabelDensity): T[] {
|
|
21
|
-
if (density === 'none') return [];
|
|
22
|
-
if (density === 'endpoints' && marks.length > 1) {
|
|
23
|
-
return [marks[0], marks[marks.length - 1]];
|
|
24
|
-
}
|
|
25
|
-
return marks;
|
|
26
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared numeric value formatter for data labels.
|
|
3
|
-
*
|
|
4
|
-
* Used by bar, column, and dot label computation to display a value
|
|
5
|
-
* with smart compact defaults (k/M/B/T).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { FieldFormatContext } from '@opendata-ai/openchart-core';
|
|
9
|
-
import { defaultNumberFormatter } from '@opendata-ai/openchart-core';
|
|
10
|
-
|
|
11
|
-
const defaultFmt = defaultNumberFormatter();
|
|
12
|
-
|
|
13
|
-
/** Format a label value for display (compact by default). */
|
|
14
|
-
export function formatLabelValue(value: number, ctx?: FieldFormatContext): string {
|
|
15
|
-
return (ctx ? defaultNumberFormatter(ctx) : defaultFmt)(value);
|
|
16
|
-
}
|