@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,107 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { sortData } from '../sort';
|
|
3
|
-
|
|
4
|
-
describe('sortData', () => {
|
|
5
|
-
it('sorts numbers numerically ascending', () => {
|
|
6
|
-
const data = [
|
|
7
|
-
{ name: 'C', value: 30 },
|
|
8
|
-
{ name: 'A', value: 10 },
|
|
9
|
-
{ name: 'B', value: 20 },
|
|
10
|
-
];
|
|
11
|
-
const result = sortData(data, { column: 'value', direction: 'asc' });
|
|
12
|
-
expect(result.data.map((r) => r.value)).toEqual([10, 20, 30]);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('sorts numbers numerically descending', () => {
|
|
16
|
-
const data = [
|
|
17
|
-
{ name: 'A', value: 10 },
|
|
18
|
-
{ name: 'C', value: 30 },
|
|
19
|
-
{ name: 'B', value: 20 },
|
|
20
|
-
];
|
|
21
|
-
const result = sortData(data, { column: 'value', direction: 'desc' });
|
|
22
|
-
expect(result.data.map((r) => r.value)).toEqual([30, 20, 10]);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('sorts strings via localeCompare', () => {
|
|
26
|
-
const data = [
|
|
27
|
-
{ name: 'Charlie', value: 1 },
|
|
28
|
-
{ name: 'Alice', value: 2 },
|
|
29
|
-
{ name: 'Bob', value: 3 },
|
|
30
|
-
];
|
|
31
|
-
const result = sortData(data, { column: 'name', direction: 'asc' });
|
|
32
|
-
expect(result.data.map((r) => r.name)).toEqual(['Alice', 'Bob', 'Charlie']);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('sorts dates by timestamp', () => {
|
|
36
|
-
const d1 = new Date('2020-01-01');
|
|
37
|
-
const d2 = new Date('2021-06-15');
|
|
38
|
-
const d3 = new Date('2019-12-31');
|
|
39
|
-
const data = [
|
|
40
|
-
{ date: d1, val: 1 },
|
|
41
|
-
{ date: d2, val: 2 },
|
|
42
|
-
{ date: d3, val: 3 },
|
|
43
|
-
];
|
|
44
|
-
const result = sortData(data, { column: 'date', direction: 'asc' });
|
|
45
|
-
expect(result.data.map((r) => r.val)).toEqual([3, 1, 2]);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('puts null values last regardless of sort direction', () => {
|
|
49
|
-
const data = [
|
|
50
|
-
{ name: 'A', value: null },
|
|
51
|
-
{ name: 'B', value: 20 },
|
|
52
|
-
{ name: 'C', value: 10 },
|
|
53
|
-
];
|
|
54
|
-
|
|
55
|
-
const asc = sortData(data, { column: 'value', direction: 'asc' });
|
|
56
|
-
expect(asc.data.map((r) => r.name)).toEqual(['C', 'B', 'A']);
|
|
57
|
-
|
|
58
|
-
const desc = sortData(data, { column: 'value', direction: 'desc' });
|
|
59
|
-
expect(desc.data.map((r) => r.name)).toEqual(['B', 'C', 'A']);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('is stable (preserves order for equal values)', () => {
|
|
63
|
-
const data = [
|
|
64
|
-
{ name: 'First', value: 10 },
|
|
65
|
-
{ name: 'Second', value: 10 },
|
|
66
|
-
{ name: 'Third', value: 10 },
|
|
67
|
-
];
|
|
68
|
-
const result = sortData(data, { column: 'value', direction: 'asc' });
|
|
69
|
-
expect(result.data.map((r) => r.name)).toEqual(['First', 'Second', 'Third']);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('does not mutate the original array', () => {
|
|
73
|
-
const data = [{ value: 30 }, { value: 10 }, { value: 20 }];
|
|
74
|
-
const original = [...data];
|
|
75
|
-
sortData(data, { column: 'value', direction: 'asc' });
|
|
76
|
-
expect(data).toEqual(original);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('handles single element array', () => {
|
|
80
|
-
const data = [{ value: 42 }];
|
|
81
|
-
const result = sortData(data, { column: 'value', direction: 'asc' });
|
|
82
|
-
expect(result.data).toHaveLength(1);
|
|
83
|
-
expect(result.data[0].value).toBe(42);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('handles all null values', () => {
|
|
87
|
-
const data = [
|
|
88
|
-
{ name: 'A', value: null },
|
|
89
|
-
{ name: 'B', value: null },
|
|
90
|
-
];
|
|
91
|
-
const result = sortData(data, { column: 'value', direction: 'asc' });
|
|
92
|
-
// Stable sort: original order preserved
|
|
93
|
-
expect(result.data.map((r) => r.name)).toEqual(['A', 'B']);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('returns correct originalIndices for sorted data', () => {
|
|
97
|
-
const data = [
|
|
98
|
-
{ name: 'C', value: 30 },
|
|
99
|
-
{ name: 'A', value: 10 },
|
|
100
|
-
{ name: 'B', value: 20 },
|
|
101
|
-
];
|
|
102
|
-
const result = sortData(data, { column: 'value', direction: 'asc' });
|
|
103
|
-
// A (index 1) < B (index 2) < C (index 0)
|
|
104
|
-
expect(result.originalIndices).toEqual([1, 2, 0]);
|
|
105
|
-
expect(result.data.map((r) => r.name)).toEqual(['A', 'B', 'C']);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import type { SparklineColumnConfig } from '@opendata-ai/openchart-core';
|
|
2
|
-
import { resolveTheme } from '@opendata-ai/openchart-core';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import { computeSparkline, computeSparklineForRow } from '../sparkline';
|
|
5
|
-
|
|
6
|
-
const theme = resolveTheme();
|
|
7
|
-
|
|
8
|
-
describe('computeSparkline', () => {
|
|
9
|
-
it('normalizes line points to 0-1 range', () => {
|
|
10
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
11
|
-
const result = computeSparkline([10, 20, 30, 40, 50], config, theme, false);
|
|
12
|
-
|
|
13
|
-
expect(result).not.toBeNull();
|
|
14
|
-
expect(result!.type).toBe('line');
|
|
15
|
-
expect(result!.points).toHaveLength(5);
|
|
16
|
-
|
|
17
|
-
// First point at min should be y=0, last at max should be y=1
|
|
18
|
-
expect(result!.points[0].y).toBeCloseTo(0);
|
|
19
|
-
expect(result!.points[4].y).toBeCloseTo(1);
|
|
20
|
-
|
|
21
|
-
// X values evenly distributed
|
|
22
|
-
expect(result!.points[0].x).toBeCloseTo(0);
|
|
23
|
-
expect(result!.points[4].x).toBeCloseTo(1);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('produces bar data for bar type', () => {
|
|
27
|
-
const config: SparklineColumnConfig = { type: 'bar' };
|
|
28
|
-
const result = computeSparkline([0, 50, 100], config, theme, false);
|
|
29
|
-
|
|
30
|
-
expect(result).not.toBeNull();
|
|
31
|
-
expect(result!.type).toBe('bar');
|
|
32
|
-
expect(result!.bars).toHaveLength(3);
|
|
33
|
-
|
|
34
|
-
// Normalized: 0->0, 50->0.5, 100->1
|
|
35
|
-
expect(result!.bars[0]).toBeCloseTo(0);
|
|
36
|
-
expect(result!.bars[1]).toBeCloseTo(0.5);
|
|
37
|
-
expect(result!.bars[2]).toBeCloseTo(1);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('produces column data for column type', () => {
|
|
41
|
-
const config: SparklineColumnConfig = { type: 'column' };
|
|
42
|
-
const result = computeSparkline([10, 20, 30], config, theme, false);
|
|
43
|
-
|
|
44
|
-
expect(result).not.toBeNull();
|
|
45
|
-
expect(result!.type).toBe('column');
|
|
46
|
-
expect(result!.bars).toHaveLength(3);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('returns null for empty values', () => {
|
|
50
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
51
|
-
const result = computeSparkline([], config, theme, false);
|
|
52
|
-
expect(result).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('handles all equal values (flat line)', () => {
|
|
56
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
57
|
-
const result = computeSparkline([5, 5, 5], config, theme, false);
|
|
58
|
-
|
|
59
|
-
expect(result).not.toBeNull();
|
|
60
|
-
// When range is 0, all y values should be 0.5
|
|
61
|
-
expect(result!.points[0].y).toBeCloseTo(0.5);
|
|
62
|
-
expect(result!.points[1].y).toBeCloseTo(0.5);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('handles single value', () => {
|
|
66
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
67
|
-
const result = computeSparkline([42], config, theme, false);
|
|
68
|
-
|
|
69
|
-
expect(result).not.toBeNull();
|
|
70
|
-
expect(result!.points).toHaveLength(1);
|
|
71
|
-
expect(result!.points[0].x).toBeCloseTo(0.5);
|
|
72
|
-
expect(result!.points[0].y).toBeCloseTo(0.5);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('uses custom color from config', () => {
|
|
76
|
-
const config: SparklineColumnConfig = { type: 'line', color: '#ff0000' };
|
|
77
|
-
const result = computeSparkline([1, 2, 3], config, theme, false);
|
|
78
|
-
expect(result!.color).toBe('#ff0000');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('defaults to first categorical palette color', () => {
|
|
82
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
83
|
-
const result = computeSparkline([1, 2, 3], config, theme, false);
|
|
84
|
-
expect(result!.color).toBe(theme.colors.categorical[0]);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('computeSparklineForRow', () => {
|
|
89
|
-
it('extracts values from array field', () => {
|
|
90
|
-
const row = { trend: [10, 20, 30] };
|
|
91
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
92
|
-
const result = computeSparklineForRow(row, 'trend', config, theme, false);
|
|
93
|
-
|
|
94
|
-
expect(result).not.toBeNull();
|
|
95
|
-
expect(result!.count).toBe(3);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('uses valuesField to extract from different field', () => {
|
|
99
|
-
const row = { label: 'Test', data: [5, 15, 25] };
|
|
100
|
-
const config: SparklineColumnConfig = { type: 'line', valuesField: 'data' };
|
|
101
|
-
const result = computeSparklineForRow(row, 'label', config, theme, false);
|
|
102
|
-
|
|
103
|
-
expect(result).not.toBeNull();
|
|
104
|
-
expect(result!.count).toBe(3);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('returns null when field value is not an array', () => {
|
|
108
|
-
const row = { trend: 'not an array' };
|
|
109
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
110
|
-
const result = computeSparklineForRow(row, 'trend', config, theme, false);
|
|
111
|
-
expect(result).toBeNull();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('filters out non-numeric values from array', () => {
|
|
115
|
-
const row = { trend: [10, 'bad', null, 30] };
|
|
116
|
-
const config: SparklineColumnConfig = { type: 'line' };
|
|
117
|
-
const result = computeSparklineForRow(row, 'trend', config, theme, false);
|
|
118
|
-
|
|
119
|
-
expect(result).not.toBeNull();
|
|
120
|
-
expect(result!.count).toBe(2); // only 10 and 30
|
|
121
|
-
});
|
|
122
|
-
});
|
package/src/tables/bar-column.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bar column computation for inline bar visualization in table cells.
|
|
3
|
-
*
|
|
4
|
-
* Computes bar width as a proportion of the max value.
|
|
5
|
-
* Supports negative values with bidirectional bars.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { BarColumnConfig, ResolvedTheme } from '@opendata-ai/openchart-core';
|
|
9
|
-
|
|
10
|
-
const NEGATIVE_BAR_COLOR = '#c44e52';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Compute the bar percentage, offset, and color for a single cell value.
|
|
14
|
-
*
|
|
15
|
-
* barPercent is 0-1. barOffset is 0-1 (left edge position).
|
|
16
|
-
* When the column has negative values, bars extend bidirectionally from a zero line.
|
|
17
|
-
*/
|
|
18
|
-
export function computeBarCell(
|
|
19
|
-
value: number,
|
|
20
|
-
config: BarColumnConfig,
|
|
21
|
-
columnMax: number,
|
|
22
|
-
columnMin: number,
|
|
23
|
-
theme: ResolvedTheme,
|
|
24
|
-
_darkMode: boolean,
|
|
25
|
-
): { barPercent: number; barOffset: number; barColor: string; isNegative: boolean } {
|
|
26
|
-
const barColor = config.color ?? theme.colors.categorical[0];
|
|
27
|
-
const hasNegatives = columnMin < 0;
|
|
28
|
-
|
|
29
|
-
if (!Number.isFinite(value)) {
|
|
30
|
-
return { barPercent: 0, barOffset: 0, barColor, isNegative: false };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!hasNegatives) {
|
|
34
|
-
// Positive-only column: simple left-to-right bars
|
|
35
|
-
const maxValue = config.maxValue ?? columnMax;
|
|
36
|
-
if (maxValue <= 0) {
|
|
37
|
-
return { barPercent: 0, barOffset: 0, barColor, isNegative: false };
|
|
38
|
-
}
|
|
39
|
-
const barPercent = Math.max(0, Math.min(1, value / maxValue));
|
|
40
|
-
return { barPercent, barOffset: 0, barColor, isNegative: false };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Bidirectional: zero line position proportional to data range
|
|
44
|
-
const maxPos = config.maxValue ?? columnMax;
|
|
45
|
-
const absMin = Math.abs(columnMin);
|
|
46
|
-
const totalRange = maxPos + absMin;
|
|
47
|
-
if (totalRange === 0) {
|
|
48
|
-
return { barPercent: 0, barOffset: 0, barColor, isNegative: false };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const zeroPos = absMin / totalRange;
|
|
52
|
-
|
|
53
|
-
if (value >= 0) {
|
|
54
|
-
const barPercent = value / totalRange;
|
|
55
|
-
return { barPercent, barOffset: zeroPos, barColor, isNegative: false };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Negative value: red bar extending left from zero
|
|
59
|
-
const barPercent = Math.abs(value) / totalRange;
|
|
60
|
-
return {
|
|
61
|
-
barPercent,
|
|
62
|
-
barOffset: zeroPos - barPercent,
|
|
63
|
-
barColor: config.color ?? NEGATIVE_BAR_COLOR,
|
|
64
|
-
isNegative: true,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Compute the column max and min from data for bar scaling.
|
|
70
|
-
*/
|
|
71
|
-
export function computeColumnMax(data: Record<string, unknown>[], key: string): number {
|
|
72
|
-
let max = 0;
|
|
73
|
-
for (const row of data) {
|
|
74
|
-
const val = row[key];
|
|
75
|
-
if (typeof val === 'number' && Number.isFinite(val) && val > max) {
|
|
76
|
-
max = val;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return max;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Compute the column minimum from data (for negative bar support).
|
|
84
|
-
*/
|
|
85
|
-
export function computeColumnMin(data: Record<string, unknown>[], key: string): number {
|
|
86
|
-
let min = 0;
|
|
87
|
-
for (const row of data) {
|
|
88
|
-
const val = row[key];
|
|
89
|
-
if (typeof val === 'number' && Number.isFinite(val) && val < min) {
|
|
90
|
-
min = val;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return min;
|
|
94
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Category color assignment for table columns.
|
|
3
|
-
*
|
|
4
|
-
* Maps categorical values to colors using explicit mappings or
|
|
5
|
-
* theme categorical palette, with AA-contrast text colors.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { CellStyle, ColumnConfig, ResolvedTheme } from '@opendata-ai/openchart-core';
|
|
9
|
-
import { adaptColorForDarkMode } from '@opendata-ai/openchart-core';
|
|
10
|
-
import { accessibleTextColor } from './utils';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Compute category-colored cell styles for a column.
|
|
14
|
-
*
|
|
15
|
-
* Uses column.categoryColors for explicit value-to-color mappings.
|
|
16
|
-
* Unmapped values get colors from the theme's categorical palette.
|
|
17
|
-
*
|
|
18
|
-
* Returns a Map keyed by original data index with background and text colors.
|
|
19
|
-
*/
|
|
20
|
-
export function computeCategoryColors(
|
|
21
|
-
data: Record<string, unknown>[],
|
|
22
|
-
column: ColumnConfig,
|
|
23
|
-
theme: ResolvedTheme,
|
|
24
|
-
darkMode: boolean,
|
|
25
|
-
): Map<number, CellStyle> {
|
|
26
|
-
const result = new Map<number, CellStyle>();
|
|
27
|
-
const explicitMap = column.categoryColors;
|
|
28
|
-
if (!explicitMap) return result;
|
|
29
|
-
|
|
30
|
-
const categoricalPalette = theme.colors.categorical;
|
|
31
|
-
let nextPaletteIndex = 0;
|
|
32
|
-
const autoAssigned = new Map<string, string>();
|
|
33
|
-
const lightBg = '#ffffff';
|
|
34
|
-
const darkBg = theme.colors.background;
|
|
35
|
-
|
|
36
|
-
for (let i = 0; i < data.length; i++) {
|
|
37
|
-
const raw = data[i][column.key];
|
|
38
|
-
if (raw == null) continue;
|
|
39
|
-
|
|
40
|
-
const key = String(raw);
|
|
41
|
-
let bg: string;
|
|
42
|
-
let isExplicit = false;
|
|
43
|
-
|
|
44
|
-
if (explicitMap[key] != null) {
|
|
45
|
-
if (explicitMap[key] === 'transparent' || explicitMap[key] === 'none') {
|
|
46
|
-
// Skip transparent/none — let the cell inherit default table styling
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
bg = explicitMap[key];
|
|
50
|
-
isExplicit = true;
|
|
51
|
-
} else if (column.autoAssign) {
|
|
52
|
-
// Auto-assign from palette only when explicitly opted in
|
|
53
|
-
if (autoAssigned.has(key)) {
|
|
54
|
-
bg = autoAssigned.get(key)!;
|
|
55
|
-
} else {
|
|
56
|
-
bg = categoricalPalette[nextPaletteIndex % categoricalPalette.length];
|
|
57
|
-
nextPaletteIndex++;
|
|
58
|
-
autoAssigned.set(key, bg);
|
|
59
|
-
}
|
|
60
|
-
} else {
|
|
61
|
-
// Default: skip unmapped values (no color assigned)
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Dark mode adaptation (skip for explicit user-provided colors)
|
|
66
|
-
if (darkMode && !isExplicit) {
|
|
67
|
-
bg = adaptColorForDarkMode(bg, lightBg, darkBg);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const textColor = accessibleTextColor(bg);
|
|
71
|
-
result.set(i, {
|
|
72
|
-
backgroundColor: bg,
|
|
73
|
-
color: textColor,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return result;
|
|
78
|
-
}
|