@opendata-ai/openchart-engine 8.0.0-rc.12 → 8.0.0-rc.3

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.
Files changed (271) hide show
  1. package/dist/index.d.ts +0 -7
  2. package/dist/index.js +161 -579
  3. package/dist/index.js.map +1 -1
  4. package/package.json +4 -3
  5. package/src/__test-fixtures__/specs.ts +182 -0
  6. package/src/__tests__/__snapshots__/compile-snapshot.test.ts.snap +2339 -0
  7. package/src/__tests__/annotation-behavior.test.ts +721 -0
  8. package/src/__tests__/axes.test.ts +1579 -0
  9. package/src/__tests__/compile-animation.test.ts +161 -0
  10. package/src/__tests__/compile-chart.test.ts +1303 -0
  11. package/src/__tests__/compile-footnotes.test.ts +277 -0
  12. package/src/__tests__/compile-layer.test.ts +969 -0
  13. package/src/__tests__/compile-snapshot.test.ts +156 -0
  14. package/src/__tests__/compound-labels.test.ts +147 -0
  15. package/src/__tests__/dimensions.test.ts +918 -0
  16. package/src/__tests__/encoding-sugar.test.ts +390 -0
  17. package/src/__tests__/facet.test.ts +215 -0
  18. package/src/__tests__/helpers/invariants.ts +299 -0
  19. package/src/__tests__/known-bugs.test.ts +566 -0
  20. package/src/__tests__/layout-fuzz.test.ts +358 -0
  21. package/src/__tests__/layout-invariants.test.ts +535 -0
  22. package/src/__tests__/legend.test.ts +721 -0
  23. package/src/__tests__/mark-keys.test.ts +294 -0
  24. package/src/__tests__/ranking-recipes.test.ts +260 -0
  25. package/src/__tests__/rotation.test.ts +83 -0
  26. package/src/__tests__/scales.test.ts +323 -0
  27. package/src/__tests__/series-search.test.ts +92 -0
  28. package/src/__tests__/spec-sugar.test.ts +801 -0
  29. package/src/__tests__/you-draw-it.test.ts +160 -0
  30. package/src/annotations/__tests__/compute.test.ts +1672 -0
  31. package/src/annotations/__tests__/geometry.test.ts +66 -0
  32. package/src/annotations/__tests__/placement-fuzz.test.ts +256 -0
  33. package/src/annotations/__tests__/placement-invariants.test.ts +345 -0
  34. package/src/annotations/__tests__/placement.test.ts +316 -0
  35. package/src/annotations/__tests__/resolve-text.test.ts +788 -0
  36. package/src/annotations/__tests__/rich-text.test.ts +91 -0
  37. package/src/annotations/__tests__/thinning.test.ts +285 -0
  38. package/src/annotations/collisions.ts +326 -0
  39. package/src/annotations/compute.ts +333 -0
  40. package/src/annotations/constants.ts +162 -0
  41. package/src/annotations/geometry.ts +370 -0
  42. package/src/annotations/move.ts +135 -0
  43. package/src/annotations/placement.ts +636 -0
  44. package/src/annotations/position.ts +131 -0
  45. package/src/annotations/resolve-range.ts +117 -0
  46. package/src/annotations/resolve-refline.ts +157 -0
  47. package/src/annotations/resolve-text.ts +539 -0
  48. package/src/annotations/rich-text.ts +65 -0
  49. package/src/annotations/thinning.ts +190 -0
  50. package/src/barlist/__tests__/compile-barlist.test.ts +200 -0
  51. package/src/barlist/compile-barlist.ts +391 -0
  52. package/src/barlist/types.ts +28 -0
  53. package/src/charts/__tests__/post-process.test.ts +237 -0
  54. package/src/charts/__tests__/registry.test.ts +116 -0
  55. package/src/charts/__tests__/utils.test.ts +195 -0
  56. package/src/charts/_shared/__tests__/density-filter.test.ts +32 -0
  57. package/src/charts/_shared/density-filter.ts +26 -0
  58. package/src/charts/_shared/format-label-value.ts +16 -0
  59. package/src/charts/bar/__tests__/compute.test.ts +934 -0
  60. package/src/charts/bar/__tests__/gradient-orient.test.ts +127 -0
  61. package/src/charts/bar/__tests__/labels.test.ts +292 -0
  62. package/src/charts/bar/compute.ts +539 -0
  63. package/src/charts/bar/index.ts +60 -0
  64. package/src/charts/bar/labels.ts +311 -0
  65. package/src/charts/beeswarm/__tests__/compute.test.ts +280 -0
  66. package/src/charts/beeswarm/__tests__/dodge.test.ts +148 -0
  67. package/src/charts/beeswarm/compute.ts +203 -0
  68. package/src/charts/beeswarm/dodge.ts +155 -0
  69. package/src/charts/beeswarm/index.ts +32 -0
  70. package/src/charts/builtin.ts +75 -0
  71. package/src/charts/calendar/__tests__/compute.test.ts +417 -0
  72. package/src/charts/calendar/compute.ts +319 -0
  73. package/src/charts/calendar/index.ts +32 -0
  74. package/src/charts/column/__tests__/compute.test.ts +540 -0
  75. package/src/charts/column/__tests__/labels.test.ts +179 -0
  76. package/src/charts/column/compute.ts +493 -0
  77. package/src/charts/column/index.ts +53 -0
  78. package/src/charts/column/labels.ts +229 -0
  79. package/src/charts/dot/__tests__/compute.test.ts +380 -0
  80. package/src/charts/dot/__tests__/labels.test.ts +98 -0
  81. package/src/charts/dot/compute.ts +285 -0
  82. package/src/charts/dot/index.ts +62 -0
  83. package/src/charts/dot/labels.ts +121 -0
  84. package/src/charts/line/__tests__/compute.test.ts +1194 -0
  85. package/src/charts/line/__tests__/curves.test.ts +51 -0
  86. package/src/charts/line/__tests__/labels.test.ts +94 -0
  87. package/src/charts/line/__tests__/mark-options.test.ts +175 -0
  88. package/src/charts/line/area.ts +484 -0
  89. package/src/charts/line/compute.ts +356 -0
  90. package/src/charts/line/curves.ts +85 -0
  91. package/src/charts/line/index.ts +176 -0
  92. package/src/charts/line/labels.ts +177 -0
  93. package/src/charts/parliament/__tests__/compute.test.ts +411 -0
  94. package/src/charts/parliament/compute.ts +313 -0
  95. package/src/charts/parliament/index.ts +32 -0
  96. package/src/charts/pie/__tests__/compute.test.ts +384 -0
  97. package/src/charts/pie/__tests__/labels.test.ts +132 -0
  98. package/src/charts/pie/compute.ts +324 -0
  99. package/src/charts/pie/index.ts +49 -0
  100. package/src/charts/pie/labels.ts +143 -0
  101. package/src/charts/post-process.ts +229 -0
  102. package/src/charts/range/__tests__/compute.test.ts +486 -0
  103. package/src/charts/range/compute.ts +273 -0
  104. package/src/charts/range/index.ts +82 -0
  105. package/src/charts/range/labels.ts +169 -0
  106. package/src/charts/rect/__tests__/compute.test.ts +120 -0
  107. package/src/charts/rect/index.ts +127 -0
  108. package/src/charts/registry.ts +72 -0
  109. package/src/charts/rule/index.ts +139 -0
  110. package/src/charts/scatter/__tests__/compute.test.ts +488 -0
  111. package/src/charts/scatter/__tests__/trendline.test.ts +191 -0
  112. package/src/charts/scatter/compute.ts +192 -0
  113. package/src/charts/scatter/index.ts +46 -0
  114. package/src/charts/scatter/trendline.ts +100 -0
  115. package/src/charts/text/__tests__/compute.test.ts +307 -0
  116. package/src/charts/text/index.ts +141 -0
  117. package/src/charts/tick/__tests__/compute.test.ts +75 -0
  118. package/src/charts/tick/index.ts +99 -0
  119. package/src/charts/utils.ts +170 -0
  120. package/src/charts/waffle/__tests__/compute.test.ts +305 -0
  121. package/src/charts/waffle/compute.ts +197 -0
  122. package/src/charts/waffle/index.ts +31 -0
  123. package/src/compile/__tests__/color-scale-range.test.ts +158 -0
  124. package/src/compile/__tests__/color-scale-reverse.test.ts +89 -0
  125. package/src/compile/__tests__/data-clip.test.ts +127 -0
  126. package/src/compile/__tests__/fill-patterns.test.ts +154 -0
  127. package/src/compile/__tests__/size-scale.test.ts +108 -0
  128. package/src/compile/__tests__/watermark-obstacle.test.ts +93 -0
  129. package/src/compile/color-scale-range.ts +101 -0
  130. package/src/compile/data-clip.ts +48 -0
  131. package/src/compile/fill-patterns.ts +74 -0
  132. package/src/compile/layer.ts +626 -0
  133. package/src/compile/size-scale.ts +141 -0
  134. package/src/compile/spec-sugar.ts +758 -0
  135. package/src/compile/watermark-obstacle.ts +54 -0
  136. package/src/compile/you-draw-it.ts +123 -0
  137. package/src/compile.ts +1450 -0
  138. package/src/compiler/__tests__/a11y-warnings.test.ts +148 -0
  139. package/src/compiler/__tests__/animation.test.ts +177 -0
  140. package/src/compiler/__tests__/compile.test.ts +87 -0
  141. package/src/compiler/__tests__/fixtures/vl-parity/axis-null.json +19 -0
  142. package/src/compiler/__tests__/fixtures/vl-parity/color-value.json +20 -0
  143. package/src/compiler/__tests__/fixtures/vl-parity/count-aggregate.json +22 -0
  144. package/src/compiler/__tests__/fixtures/vl-parity/data-values.json +19 -0
  145. package/src/compiler/__tests__/fixtures/vl-parity/layer-axis-null.json +14 -0
  146. package/src/compiler/__tests__/fixtures/vl-parity/layer-count.json +34 -0
  147. package/src/compiler/__tests__/fixtures/vl-parity/layer-data-values.json +22 -0
  148. package/src/compiler/__tests__/fixtures/vl-parity/layer-legend-null.json +41 -0
  149. package/src/compiler/__tests__/fixtures/vl-parity/layer-scheme.json +38 -0
  150. package/src/compiler/__tests__/fixtures/vl-parity/layer-sort.json +28 -0
  151. package/src/compiler/__tests__/fixtures/vl-parity/layer-theta.json +28 -0
  152. package/src/compiler/__tests__/fixtures/vl-parity/layer-title.json +30 -0
  153. package/src/compiler/__tests__/fixtures/vl-parity/layer-value-def.json +29 -0
  154. package/src/compiler/__tests__/fixtures/vl-parity/legend-null.json +32 -0
  155. package/src/compiler/__tests__/fixtures/vl-parity/scheme.json +25 -0
  156. package/src/compiler/__tests__/fixtures/vl-parity/sort-by-value.json +19 -0
  157. package/src/compiler/__tests__/fixtures/vl-parity/theta-arc.json +19 -0
  158. package/src/compiler/__tests__/fixtures/vl-parity/title-subtitle.json +21 -0
  159. package/src/compiler/__tests__/fixtures/vl-parity/width-height.json +23 -0
  160. package/src/compiler/__tests__/keys.test.ts +83 -0
  161. package/src/compiler/__tests__/normalize-highlight.test.ts +103 -0
  162. package/src/compiler/__tests__/normalize.test.ts +399 -0
  163. package/src/compiler/__tests__/sparkline-defaults.test.ts +153 -0
  164. package/src/compiler/__tests__/validate-did-you-mean.test.ts +86 -0
  165. package/src/compiler/__tests__/validate-highlight.test.ts +112 -0
  166. package/src/compiler/__tests__/validate-near-miss.test.ts +89 -0
  167. package/src/compiler/__tests__/validate.test.ts +629 -0
  168. package/src/compiler/__tests__/vl-parity.test.ts +68 -0
  169. package/src/compiler/a11y-warnings.ts +110 -0
  170. package/src/compiler/animation.ts +142 -0
  171. package/src/compiler/index.ts +47 -0
  172. package/src/compiler/keys.ts +55 -0
  173. package/src/compiler/normalize.ts +734 -0
  174. package/src/compiler/sparkline-defaults.ts +138 -0
  175. package/src/compiler/types.ts +241 -0
  176. package/src/compiler/validate.ts +1871 -0
  177. package/src/endpoint-labels/__tests__/compute.test.ts +587 -0
  178. package/src/endpoint-labels/compute.ts +621 -0
  179. package/src/endpoint-labels/constants.ts +54 -0
  180. package/src/endpoint-labels/format.ts +60 -0
  181. package/src/endpoint-labels/predict.ts +139 -0
  182. package/src/format/__tests__/field-format.test.ts +164 -0
  183. package/src/format/field-format.ts +27 -0
  184. package/src/geo/__tests__/compile-map.test.ts +832 -0
  185. package/src/geo/compile-map.ts +1243 -0
  186. package/src/geo/join.ts +115 -0
  187. package/src/geo/projections.ts +45 -0
  188. package/src/geo/types.ts +26 -0
  189. package/src/graphs/__tests__/community.test.ts +228 -0
  190. package/src/graphs/__tests__/compile-graph.test.ts +396 -0
  191. package/src/graphs/__tests__/encoding.test.ts +415 -0
  192. package/src/graphs/community.ts +92 -0
  193. package/src/graphs/compile-graph.ts +329 -0
  194. package/src/graphs/encoding.ts +356 -0
  195. package/src/graphs/types.ts +106 -0
  196. package/src/index.ts +177 -0
  197. package/src/layout/__tests__/metrics.test.ts +97 -0
  198. package/src/layout/__tests__/scales-highlight.test.ts +192 -0
  199. package/src/layout/axes/rotation.ts +152 -0
  200. package/src/layout/axes/thinning.ts +98 -0
  201. package/src/layout/axes/ticks.ts +409 -0
  202. package/src/layout/axes.ts +757 -0
  203. package/src/layout/dimensions.ts +912 -0
  204. package/src/layout/facet.ts +91 -0
  205. package/src/layout/gridlines.ts +84 -0
  206. package/src/layout/metrics.ts +132 -0
  207. package/src/layout/plan.ts +631 -0
  208. package/src/layout/scales.ts +1053 -0
  209. package/src/layout/shared.ts +44 -0
  210. package/src/legend/__tests__/continuous.test.ts +318 -0
  211. package/src/legend/__tests__/redundancy.test.ts +108 -0
  212. package/src/legend/__tests__/size.test.ts +348 -0
  213. package/src/legend/__tests__/suppression.test.ts +382 -0
  214. package/src/legend/compute.ts +747 -0
  215. package/src/legend/continuous.ts +327 -0
  216. package/src/legend/size.ts +407 -0
  217. package/src/legend/suppression.ts +226 -0
  218. package/src/legend/wrap.ts +117 -0
  219. package/src/sankey/__tests__/compile-sankey.test.ts +458 -0
  220. package/src/sankey/__tests__/layout.test.ts +165 -0
  221. package/src/sankey/__tests__/node-label-wrap.test.ts +114 -0
  222. package/src/sankey/__tests__/node-sort.test.ts +45 -0
  223. package/src/sankey/compile-sankey.ts +706 -0
  224. package/src/sankey/layout.ts +184 -0
  225. package/src/sankey/types.ts +41 -0
  226. package/src/tables/__tests__/bar-column.test.ts +147 -0
  227. package/src/tables/__tests__/category-colors.test.ts +230 -0
  228. package/src/tables/__tests__/compile-table.test.ts +236 -0
  229. package/src/tables/__tests__/format-cells.test.ts +143 -0
  230. package/src/tables/__tests__/heatmap.test.ts +148 -0
  231. package/src/tables/__tests__/pagination.test.ts +78 -0
  232. package/src/tables/__tests__/search.test.ts +94 -0
  233. package/src/tables/__tests__/sort.test.ts +107 -0
  234. package/src/tables/__tests__/sparkline.test.ts +122 -0
  235. package/src/tables/bar-column.ts +94 -0
  236. package/src/tables/category-colors.ts +78 -0
  237. package/src/tables/compile-table.ts +427 -0
  238. package/src/tables/format-cells.ts +115 -0
  239. package/src/tables/heatmap.ts +141 -0
  240. package/src/tables/pagination.ts +46 -0
  241. package/src/tables/search.ts +66 -0
  242. package/src/tables/sort.ts +69 -0
  243. package/src/tables/sparkline.ts +113 -0
  244. package/src/tables/utils.ts +16 -0
  245. package/src/tilemap/__tests__/compile-tilemap.test.ts +576 -0
  246. package/src/tilemap/compile-tilemap.ts +755 -0
  247. package/src/tilemap/layout.ts +172 -0
  248. package/src/tilemap/types.ts +33 -0
  249. package/src/tooltips/__tests__/compute.test.ts +582 -0
  250. package/src/tooltips/compute.ts +700 -0
  251. package/src/transforms/__tests__/aggregate.test.ts +159 -0
  252. package/src/transforms/__tests__/bin.test.ts +88 -0
  253. package/src/transforms/__tests__/calculate.test.ts +146 -0
  254. package/src/transforms/__tests__/conditional.test.ts +127 -0
  255. package/src/transforms/__tests__/filter-relative.test.ts +202 -0
  256. package/src/transforms/__tests__/filter.test.ts +59 -0
  257. package/src/transforms/__tests__/fold.test.ts +79 -0
  258. package/src/transforms/__tests__/index.test.ts +93 -0
  259. package/src/transforms/__tests__/predicates.test.ts +193 -0
  260. package/src/transforms/__tests__/timeunit.test.ts +129 -0
  261. package/src/transforms/__tests__/window.test.ts +286 -0
  262. package/src/transforms/aggregate.ts +131 -0
  263. package/src/transforms/bin.ts +87 -0
  264. package/src/transforms/calculate.ts +60 -0
  265. package/src/transforms/conditional.ts +46 -0
  266. package/src/transforms/filter.ts +122 -0
  267. package/src/transforms/fold.ts +49 -0
  268. package/src/transforms/index.ts +60 -0
  269. package/src/transforms/predicates.ts +123 -0
  270. package/src/transforms/timeunit.ts +88 -0
  271. package/src/transforms/window.ts +185 -0
@@ -0,0 +1,382 @@
1
+ /**
2
+ * Truth-table tests for the centralized legend / endpoint / end-of-line label
3
+ * suppression helper. Covers all 8 cells of the matrix described in
4
+ * `suppression.ts`, plus the special cases (single-series, stacked area,
5
+ * compact strategy).
6
+ */
7
+
8
+ import { describe, expect, it } from 'vitest';
9
+
10
+ import type { NormalizedChartSpec } from '../../compiler/types';
11
+ import { countColorSeries, resolveSuppression } from '../suppression';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Spec factory
15
+ // ---------------------------------------------------------------------------
16
+
17
+ function makeMultiSeriesLineSpec(
18
+ overrides: Partial<NormalizedChartSpec> = {},
19
+ ): NormalizedChartSpec {
20
+ return {
21
+ markType: 'line',
22
+ markDef: { type: 'line' },
23
+ data: [
24
+ { date: '2020', value: 10, country: 'US' },
25
+ { date: '2021', value: 20, country: 'US' },
26
+ { date: '2020', value: 5, country: 'UK' },
27
+ { date: '2021', value: 15, country: 'UK' },
28
+ ],
29
+ encoding: {
30
+ x: { field: 'date', type: 'temporal' },
31
+ y: { field: 'value', type: 'quantitative' },
32
+ color: { field: 'country', type: 'nominal' },
33
+ },
34
+ chrome: {},
35
+ annotations: [],
36
+ responsive: true,
37
+ theme: {},
38
+ darkMode: 'off',
39
+ labels: { density: 'auto', format: '', prefix: '' },
40
+ hiddenSeries: [],
41
+ seriesStyles: {},
42
+ watermark: true,
43
+ display: 'full',
44
+ userExplicit: {
45
+ chrome: false,
46
+ legend: false,
47
+ endpointLabels: false,
48
+ xAxis: false,
49
+ yAxis: false,
50
+ labels: false,
51
+ animation: false,
52
+ watermark: false,
53
+ crosshair: false,
54
+ },
55
+ ...overrides,
56
+ };
57
+ }
58
+
59
+ const baseCtx = {
60
+ seriesCount: 2,
61
+ labelsHiddenByStrategy: false,
62
+ labelsDensityNone: false,
63
+ };
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Tests
67
+ // ---------------------------------------------------------------------------
68
+
69
+ describe('countColorSeries', () => {
70
+ it('returns the number of distinct values in the color field', () => {
71
+ const spec = makeMultiSeriesLineSpec();
72
+ expect(countColorSeries(spec)).toBe(2);
73
+ });
74
+
75
+ it('returns 0 when no color encoding is set', () => {
76
+ const spec = makeMultiSeriesLineSpec({
77
+ encoding: {
78
+ x: { field: 'date', type: 'temporal' },
79
+ y: { field: 'value', type: 'quantitative' },
80
+ },
81
+ });
82
+ expect(countColorSeries(spec)).toBe(0);
83
+ });
84
+
85
+ it('returns 0 when color is quantitative', () => {
86
+ const spec = makeMultiSeriesLineSpec({
87
+ encoding: {
88
+ x: { field: 'date', type: 'temporal' },
89
+ y: { field: 'value', type: 'quantitative' },
90
+ color: { field: 'value', type: 'quantitative' },
91
+ },
92
+ });
93
+ expect(countColorSeries(spec)).toBe(0);
94
+ });
95
+ });
96
+
97
+ describe('resolveSuppression - 8-cell truth table', () => {
98
+ // Cell 1: legend unset, endpointLabels unset
99
+ // -> traditional legend hidden, endpoint column shown, end-of-line hidden
100
+ it('cell 1 (unset, unset): hides legend, shows endpoint column', () => {
101
+ const spec = makeMultiSeriesLineSpec();
102
+ const result = resolveSuppression(spec, baseCtx);
103
+ expect(result.showTraditionalLegend).toBe(false);
104
+ expect(result.showEndpointLabels).toBe(true);
105
+ expect(result.showEndOfLineLabels).toBe(false);
106
+ });
107
+
108
+ // Cell 2: legend: { show: true }, endpointLabels unset
109
+ // -> traditional legend shown, endpoint column shown, end-of-line hidden
110
+ it('cell 2 (legend: true, unset): shows both legend and endpoint column', () => {
111
+ const spec = makeMultiSeriesLineSpec({ legend: { show: true } });
112
+ const result = resolveSuppression(spec, baseCtx);
113
+ expect(result.showTraditionalLegend).toBe(true);
114
+ expect(result.showEndpointLabels).toBe(true);
115
+ expect(result.showEndOfLineLabels).toBe(false);
116
+ });
117
+
118
+ // Cell 3: legend unset, endpointLabels: false
119
+ // -> traditional legend shown (auto-revoked), column hidden, end-of-line hidden
120
+ it('cell 3 (unset, endpointLabels: false): auto-revokes legend, hides column', () => {
121
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: false });
122
+ const result = resolveSuppression(spec, baseCtx);
123
+ expect(result.showTraditionalLegend).toBe(true);
124
+ expect(result.showEndpointLabels).toBe(false);
125
+ expect(result.showEndOfLineLabels).toBe(false);
126
+ });
127
+
128
+ // Cell 4: legend: { show: false }, endpointLabels: false
129
+ // -> all legend surfaces off, end-of-line labels are last resort
130
+ it('cell 4 (legend: false, endpointLabels: false): only end-of-line labels show', () => {
131
+ const spec = makeMultiSeriesLineSpec({
132
+ legend: { show: false },
133
+ endpointLabels: false,
134
+ });
135
+ const result = resolveSuppression(spec, baseCtx);
136
+ expect(result.showTraditionalLegend).toBe(false);
137
+ expect(result.showEndpointLabels).toBe(false);
138
+ expect(result.showEndOfLineLabels).toBe(true);
139
+ });
140
+
141
+ // Cell 5: legend: { show: true }, endpointLabels: false
142
+ // -> legend shown, column hidden, end-of-line hidden
143
+ it('cell 5 (legend: true, endpointLabels: false): legend only', () => {
144
+ const spec = makeMultiSeriesLineSpec({
145
+ legend: { show: true },
146
+ endpointLabels: false,
147
+ });
148
+ const result = resolveSuppression(spec, baseCtx);
149
+ expect(result.showTraditionalLegend).toBe(true);
150
+ expect(result.showEndpointLabels).toBe(false);
151
+ expect(result.showEndOfLineLabels).toBe(false);
152
+ });
153
+
154
+ // Cell 6: legend: { show: false }, endpointLabels: true
155
+ // -> legend off, column on, end-of-line hidden
156
+ it('cell 6 (legend: false, endpointLabels: true): column only', () => {
157
+ const spec = makeMultiSeriesLineSpec({
158
+ legend: { show: false },
159
+ endpointLabels: true,
160
+ });
161
+ const result = resolveSuppression(spec, baseCtx);
162
+ expect(result.showTraditionalLegend).toBe(false);
163
+ expect(result.showEndpointLabels).toBe(true);
164
+ expect(result.showEndOfLineLabels).toBe(false);
165
+ });
166
+
167
+ // Cell 7: legend: { show: true }, endpointLabels: true
168
+ // -> both surfaces shown, end-of-line hidden
169
+ it('cell 7 (legend: true, endpointLabels: true): both shown', () => {
170
+ const spec = makeMultiSeriesLineSpec({
171
+ legend: { show: true },
172
+ endpointLabels: true,
173
+ });
174
+ const result = resolveSuppression(spec, baseCtx);
175
+ expect(result.showTraditionalLegend).toBe(true);
176
+ expect(result.showEndpointLabels).toBe(true);
177
+ expect(result.showEndOfLineLabels).toBe(false);
178
+ });
179
+
180
+ // Cell 8 (the implicit eighth cell): unset + endpointLabels: true
181
+ // matches cell 1's column-on behavior, legend auto-suppressed.
182
+ it('cell 8 (unset, endpointLabels: true): legend auto-suppressed, column on', () => {
183
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: true });
184
+ const result = resolveSuppression(spec, baseCtx);
185
+ expect(result.showTraditionalLegend).toBe(false);
186
+ expect(result.showEndpointLabels).toBe(true);
187
+ expect(result.showEndOfLineLabels).toBe(false);
188
+ });
189
+ });
190
+
191
+ describe('resolveSuppression - special cases', () => {
192
+ it('single-series chart: column always off, legend follows its own rules', () => {
193
+ const spec = makeMultiSeriesLineSpec();
194
+ const result = resolveSuppression(spec, { ...baseCtx, seriesCount: 1 });
195
+ expect(result.showEndpointLabels).toBe(false);
196
+ expect(result.showEndOfLineLabels).toBe(false);
197
+ expect(result.showTraditionalLegend).toBe(true);
198
+ });
199
+
200
+ it('non-line/area mark: column always off', () => {
201
+ const spec = makeMultiSeriesLineSpec({ markType: 'bar' });
202
+ const result = resolveSuppression(spec, baseCtx);
203
+ expect(result.showEndpointLabels).toBe(false);
204
+ expect(result.showEndOfLineLabels).toBe(false);
205
+ });
206
+
207
+ it('compact strategy (labelsHiddenByStrategy): column off, end-of-line off', () => {
208
+ const spec = makeMultiSeriesLineSpec();
209
+ const result = resolveSuppression(spec, {
210
+ ...baseCtx,
211
+ labelsHiddenByStrategy: true,
212
+ });
213
+ expect(result.showEndpointLabels).toBe(false);
214
+ expect(result.showEndOfLineLabels).toBe(false);
215
+ });
216
+
217
+ it('labels.density === none: scoped to end-of-line labels only (column + legend follow truth table)', () => {
218
+ // labels.density: 'none' is the legacy switch for end-of-line labels.
219
+ // It must NOT suppress the endpoint column or the traditional legend —
220
+ // those follow the truth table independently. End-of-line labels stay off.
221
+ const spec = makeMultiSeriesLineSpec();
222
+ const result = resolveSuppression(spec, { ...baseCtx, labelsDensityNone: true });
223
+ expect(result.showEndpointLabels).toBe(true);
224
+ expect(result.showEndOfLineLabels).toBe(false);
225
+ // Cell 1 (legend unset, endpointLabels unset): traditional legend hidden.
226
+ expect(result.showTraditionalLegend).toBe(false);
227
+ });
228
+
229
+ it('labels.density === none + legend.show: true: legend stays shown', () => {
230
+ const spec = makeMultiSeriesLineSpec({ legend: { show: true } });
231
+ const result = resolveSuppression(spec, { ...baseCtx, labelsDensityNone: true });
232
+ expect(result.showTraditionalLegend).toBe(true);
233
+ expect(result.showEndpointLabels).toBe(true);
234
+ expect(result.showEndOfLineLabels).toBe(false);
235
+ });
236
+
237
+ it('stacked area: legend shown by default, column off', () => {
238
+ const spec = makeMultiSeriesLineSpec({
239
+ markType: 'area',
240
+ markDef: { type: 'area' },
241
+ encoding: {
242
+ x: { field: 'date', type: 'temporal' },
243
+ y: { field: 'value', type: 'quantitative', stack: 'zero' },
244
+ color: { field: 'country', type: 'nominal' },
245
+ },
246
+ });
247
+ const result = resolveSuppression(spec, baseCtx);
248
+ expect(result.showTraditionalLegend).toBe(true);
249
+ expect(result.showEndpointLabels).toBe(false);
250
+ expect(result.showEndOfLineLabels).toBe(false);
251
+ });
252
+
253
+ it('stacked area with explicit endpointLabels: true: column shown', () => {
254
+ const spec = makeMultiSeriesLineSpec({
255
+ markType: 'area',
256
+ markDef: { type: 'area' },
257
+ encoding: {
258
+ x: { field: 'date', type: 'temporal' },
259
+ y: { field: 'value', type: 'quantitative', stack: 'zero' },
260
+ color: { field: 'country', type: 'nominal' },
261
+ },
262
+ endpointLabels: true,
263
+ });
264
+ const result = resolveSuppression(spec, baseCtx);
265
+ expect(result.showEndpointLabels).toBe(true);
266
+ });
267
+
268
+ it('overlap area (stack: null): runs through 8-cell table', () => {
269
+ const spec = makeMultiSeriesLineSpec({
270
+ markType: 'area',
271
+ markDef: { type: 'area' },
272
+ encoding: {
273
+ x: { field: 'date', type: 'temporal' },
274
+ y: { field: 'value', type: 'quantitative', stack: null },
275
+ color: { field: 'country', type: 'nominal' },
276
+ },
277
+ });
278
+ const result = resolveSuppression(spec, baseCtx);
279
+ // Cell 1 behavior: legend hidden, column on
280
+ expect(result.showTraditionalLegend).toBe(false);
281
+ expect(result.showEndpointLabels).toBe(true);
282
+ });
283
+
284
+ it('area with no explicit stack: stacks by default (v8), legend shown, column off', () => {
285
+ const spec = makeMultiSeriesLineSpec({
286
+ markType: 'area',
287
+ markDef: { type: 'area' },
288
+ });
289
+ const result = resolveSuppression(spec, baseCtx);
290
+ expect(result.showTraditionalLegend).toBe(true);
291
+ expect(result.showEndpointLabels).toBe(false);
292
+ expect(result.showEndOfLineLabels).toBe(false);
293
+ });
294
+
295
+ it('endpointLabels: { show: false } counts as explicit off', () => {
296
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: { show: false } });
297
+ const result = resolveSuppression(spec, baseCtx);
298
+ expect(result.showEndpointLabels).toBe(false);
299
+ // Auto-revoke legend
300
+ expect(result.showTraditionalLegend).toBe(true);
301
+ });
302
+
303
+ it('endpointLabels: { width: 120 } counts as explicit on', () => {
304
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: { width: 120 } });
305
+ const result = resolveSuppression(spec, baseCtx);
306
+ expect(result.showEndpointLabels).toBe(true);
307
+ // Legend auto-suppressed
308
+ expect(result.showTraditionalLegend).toBe(false);
309
+ });
310
+
311
+ it('endpointLabels: { maxSeries: 12 } alone does NOT count as explicit on', () => {
312
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: { maxSeries: 12 } });
313
+ const result = resolveSuppression(spec, { ...baseCtx, seriesCount: 14 });
314
+ expect(result.showEndpointLabels).toBe(false);
315
+ expect(result.showTraditionalLegend).toBe(true);
316
+ });
317
+ });
318
+
319
+ describe('resolveSuppression - series-count cutoff', () => {
320
+ it('10-series line chart exceeds default cutoff (8): legend shown, endpoint labels hidden', () => {
321
+ const spec = makeMultiSeriesLineSpec();
322
+ const result = resolveSuppression(spec, { ...baseCtx, seriesCount: 10 });
323
+ expect(result.showEndpointLabels).toBe(false);
324
+ expect(result.showTraditionalLegend).toBe(true);
325
+ });
326
+
327
+ it('7-series line chart within default cutoff: endpoint labels shown', () => {
328
+ const spec = makeMultiSeriesLineSpec();
329
+ const result = resolveSuppression(spec, { ...baseCtx, seriesCount: 7 });
330
+ expect(result.showEndpointLabels).toBe(true);
331
+ expect(result.showTraditionalLegend).toBe(false);
332
+ });
333
+
334
+ it('8-series line chart at exact cutoff: endpoint labels shown', () => {
335
+ const spec = makeMultiSeriesLineSpec();
336
+ const result = resolveSuppression(spec, { ...baseCtx, seriesCount: 8 });
337
+ expect(result.showEndpointLabels).toBe(true);
338
+ expect(result.showTraditionalLegend).toBe(false);
339
+ });
340
+
341
+ it('10-series with endpointLabels: true overrides cutoff', () => {
342
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: true });
343
+ const result = resolveSuppression(spec, { ...baseCtx, seriesCount: 10 });
344
+ expect(result.showEndpointLabels).toBe(true);
345
+ expect(result.showTraditionalLegend).toBe(false);
346
+ });
347
+
348
+ it('10-series with endpointLabels: { show: true } overrides cutoff', () => {
349
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: { show: true } });
350
+ const result = resolveSuppression(spec, { ...baseCtx, seriesCount: 10 });
351
+ expect(result.showEndpointLabels).toBe(true);
352
+ });
353
+
354
+ it('custom maxSeries: 12 allows up to 12 series', () => {
355
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: { maxSeries: 12 } });
356
+ const result12 = resolveSuppression(spec, { ...baseCtx, seriesCount: 12 });
357
+ expect(result12.showEndpointLabels).toBe(true);
358
+
359
+ const result13 = resolveSuppression(spec, { ...baseCtx, seriesCount: 13 });
360
+ expect(result13.showEndpointLabels).toBe(false);
361
+ expect(result13.showTraditionalLegend).toBe(true);
362
+ });
363
+
364
+ it('endpointLabelsDemoted hides endpoint labels and shows legend', () => {
365
+ const spec = makeMultiSeriesLineSpec();
366
+ const result = resolveSuppression(spec, {
367
+ ...baseCtx,
368
+ endpointLabelsDemoted: true,
369
+ });
370
+ expect(result.showEndpointLabels).toBe(false);
371
+ expect(result.showTraditionalLegend).toBe(true);
372
+ });
373
+
374
+ it('endpointLabelsDemoted does not override explicit endpointLabels: true', () => {
375
+ const spec = makeMultiSeriesLineSpec({ endpointLabels: true });
376
+ const result = resolveSuppression(spec, {
377
+ ...baseCtx,
378
+ endpointLabelsDemoted: true,
379
+ });
380
+ expect(result.showEndpointLabels).toBe(true);
381
+ });
382
+ });