@cdc/chart 4.26.7 → 4.26.10

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 (255) hide show
  1. package/CONFIG.md +502 -368
  2. package/dist/cdcchart-CYc-Kayd.es.js +22488 -0
  3. package/dist/cdcchart-DCKn8NmI.es.js +20695 -0
  4. package/dist/cdcchart-Dyui9nk_.es.js +45828 -0
  5. package/dist/cdcchart.js +4 -97406
  6. package/examples/__data__/vega-remote-data.json +6 -0
  7. package/examples/dendrogram.json +18 -0
  8. package/examples/feature/bar/bar-chart-race-annual-change.json +68 -0
  9. package/examples/feature/heatmap/average-age-categorical.json +2 -2
  10. package/examples/feature/heatmap/calendar-heatmap.json +2 -2
  11. package/examples/feature/heatmap/cell-values-bottom-axis.json +2 -2
  12. package/examples/feature/heatmap/sparse-aggregation.json +2 -2
  13. package/examples/feature/line/line-chart-race.json +50 -0
  14. package/examples/feature/sankey/sankey-example-data.json +40 -1441
  15. package/examples/feature/sankey/sankey_chart_data.csv +9 -20
  16. package/examples/network.json +31 -0
  17. package/examples/private/box-plot.json +573 -0
  18. package/examples/private/custom-color-issue.json +625 -0
  19. package/examples/private/custom-colors.json +522 -0
  20. package/examples/private/cutoff.json +10750 -0
  21. package/examples/private/overlay-legend.json +485 -0
  22. package/examples/private/pie-test.json +469 -0
  23. package/examples/vega-remote.json +22 -0
  24. package/package.json +11 -5
  25. package/src/CdcChartComponent.tsx +405 -208
  26. package/src/_stories/Chart.BarRace.stories.tsx +443 -0
  27. package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
  28. package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
  29. package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
  30. package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
  31. package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
  32. package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
  33. package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
  34. package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
  35. package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
  36. package/src/_stories/Chart.LineRace.stories.tsx +149 -0
  37. package/src/_stories/Chart.Network.stories.tsx +432 -0
  38. package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
  39. package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
  40. package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
  41. package/src/_stories/Chart.PieLabels.stories.tsx +4 -2
  42. package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
  43. package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
  44. package/src/_stories/Chart.Sankey.stories.tsx +417 -0
  45. package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
  46. package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
  47. package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
  48. package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
  49. package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
  50. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
  51. package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
  52. package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
  53. package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
  54. package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
  55. package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
  56. package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
  57. package/src/_stories/_mock/editor-test-configs.json +20 -10
  58. package/src/_stories/_mock/editor-test-datasets.json +38 -42
  59. package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
  60. package/src/_stories/_mock/heatmap.json +2 -2
  61. package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
  62. package/src/_stories/_mock/sankey_mock.json +23 -27
  63. package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
  64. package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
  65. package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
  66. package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
  67. package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
  68. package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
  69. package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
  70. package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
  71. package/src/components/Axis/BottomAxis.tsx +1 -1
  72. package/src/components/Axis/Categorical.Axis.tsx +3 -3
  73. package/src/components/Axis/RightAxis.tsx +21 -13
  74. package/src/components/BarChart/components/BarChart.Horizontal.tsx +38 -6
  75. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +12 -3
  76. package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
  77. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +29 -25
  78. package/src/components/BarChart/components/BarChart.Vertical.tsx +37 -26
  79. package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
  80. package/src/components/BarChart/components/context.tsx +6 -0
  81. package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
  82. package/src/components/BarChart/helpers/index.ts +8 -0
  83. package/src/components/BarChart/helpers/portionPattern.ts +243 -0
  84. package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
  85. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
  86. package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
  87. package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +104 -0
  88. package/src/components/BarChart/helpers/useBarChart.ts +60 -59
  89. package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
  90. package/src/components/BarChartRace/BarChartRace.tsx +230 -0
  91. package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
  92. package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
  93. package/src/components/BarChartRace/bar-chart-race.css +160 -0
  94. package/src/components/BarChartRace/helpers.test.ts +114 -0
  95. package/src/components/BarChartRace/helpers.ts +185 -0
  96. package/src/components/BarChartRace/index.ts +3 -0
  97. package/src/components/BoxPlot/helpers/index.ts +11 -82
  98. package/src/components/Brush/BrushSelector.tsx +9 -23
  99. package/src/components/Brush/MiniChartPreview.tsx +33 -1
  100. package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
  101. package/src/components/Dendrogram/dendrogram.css +113 -0
  102. package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
  103. package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
  104. package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
  105. package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
  106. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
  107. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
  108. package/src/components/Dendrogram/index.tsx +1 -0
  109. package/src/components/Dendrogram/types/index.ts +66 -0
  110. package/src/components/EditorPanel/EditorPanel.tsx +1005 -760
  111. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
  112. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
  113. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
  114. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
  115. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +88 -54
  116. package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
  117. package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
  118. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +233 -33
  119. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
  120. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
  121. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -2
  122. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
  123. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
  124. package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
  125. package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
  126. package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
  127. package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
  128. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
  129. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
  130. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +2 -1
  131. package/src/components/EditorPanel/useEditorPermissions.test.tsx +62 -0
  132. package/src/components/EditorPanel/useEditorPermissions.ts +70 -24
  133. package/src/components/Forecasting/Forecasting.tsx +15 -6
  134. package/src/components/HeatMap/components/HeatMap.tsx +511 -178
  135. package/src/components/HeatMap/heatmap.constants.ts +7 -3
  136. package/src/components/HeatMap/heatmap.css +101 -2
  137. package/src/components/HeatMap/helpers/layout.ts +80 -0
  138. package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
  139. package/src/components/HeatMap/tests/helpers.test.ts +79 -0
  140. package/src/components/Legend/Legend.Component.tsx +110 -90
  141. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
  142. package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -34
  143. package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
  144. package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
  145. package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
  146. package/src/components/LineChart/helpers.test.ts +126 -38
  147. package/src/components/LineChart/helpers.ts +9 -4
  148. package/src/components/LineChart/index.tsx +69 -24
  149. package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
  150. package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
  151. package/src/components/LineChartRace/LineChartRace.tsx +134 -0
  152. package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
  153. package/src/components/LineChartRace/helpers.test.ts +88 -0
  154. package/src/components/LineChartRace/helpers.ts +157 -0
  155. package/src/components/LineChartRace/index.ts +3 -0
  156. package/src/components/LineChartRace/line-chart-race.css +16 -0
  157. package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
  158. package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
  159. package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
  160. package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
  161. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
  162. package/src/components/LinearChart.tsx +81 -51
  163. package/src/components/Network/components/Network.tsx +372 -0
  164. package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
  165. package/src/components/Network/helpers/layoutNetwork.ts +87 -0
  166. package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
  167. package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
  168. package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
  169. package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
  170. package/src/components/Network/index.tsx +3 -0
  171. package/src/components/Network/network.css +107 -0
  172. package/src/components/Network/types/index.ts +56 -0
  173. package/src/components/PairedBarChart.jsx +5 -10
  174. package/src/components/PairedBarChart.test.tsx +71 -0
  175. package/src/components/PieChart/PieChart.tsx +9 -8
  176. package/src/components/RacingChartRenderer.tsx +44 -0
  177. package/src/components/Regions/components/Regions.test.tsx +179 -0
  178. package/src/components/Regions/components/Regions.tsx +104 -69
  179. package/src/components/Regions/index.tsx +2 -3
  180. package/src/components/Sankey/components/Sankey.tsx +264 -437
  181. package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
  182. package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
  183. package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
  184. package/src/components/Sankey/sankey.scss +57 -116
  185. package/src/components/Sankey/types/index.ts +41 -4
  186. package/src/components/ScatterPlot/ScatterPlot.jsx +28 -4
  187. package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
  188. package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
  189. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
  190. package/src/components/raceTiming.ts +18 -0
  191. package/src/data/initial-state.js +77 -31
  192. package/src/helpers/applyChartTypeContract.ts +71 -0
  193. package/src/helpers/axisLabelFontSize.ts +2 -2
  194. package/src/helpers/boxPlotStats.ts +60 -0
  195. package/src/helpers/bumpChartTooltip.ts +2 -1
  196. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
  197. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
  198. package/src/helpers/calculateRightYAxisWidth.ts +60 -0
  199. package/src/helpers/colorAssignmentHelpers.ts +2 -2
  200. package/src/helpers/createNewChartConfig.ts +56 -0
  201. package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
  202. package/src/helpers/forecastLegend.ts +88 -0
  203. package/src/helpers/getBoxPlotConfig.ts +18 -37
  204. package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
  205. package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
  206. package/src/helpers/getColorScale.ts +31 -22
  207. package/src/helpers/getDynamicYAxisCategories.ts +69 -0
  208. package/src/helpers/getMinMax.ts +9 -7
  209. package/src/helpers/getNewRuntime.ts +5 -2
  210. package/src/helpers/getStackedSeriesMax.ts +51 -0
  211. package/src/helpers/getV21ChartDistributionColors.ts +57 -0
  212. package/src/helpers/handleRankByValue.ts +15 -15
  213. package/src/helpers/seriesColumnSettings.ts +10 -0
  214. package/src/helpers/sizeHelpers.ts +4 -3
  215. package/src/helpers/smallMultiplesHelpers.ts +10 -13
  216. package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
  217. package/src/helpers/tests/boxPlotStats.test.ts +31 -0
  218. package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
  219. package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
  220. package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
  221. package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
  222. package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
  223. package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
  224. package/src/helpers/tests/getColorScale.test.ts +226 -1
  225. package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
  226. package/src/helpers/tests/getMinMax.test.ts +41 -0
  227. package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
  228. package/src/helpers/tests/handleRankByValue.test.ts +55 -37
  229. package/src/helpers/tests/seriesColumnSettings.test.ts +23 -0
  230. package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
  231. package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
  232. package/src/helpers/tooltipHelpers.ts +16 -0
  233. package/src/hooks/tests/useTooltip.test.tsx +353 -1
  234. package/src/hooks/usePrefersReducedMotion.ts +22 -0
  235. package/src/hooks/useReduceData.ts +4 -36
  236. package/src/hooks/useScales.ts +15 -0
  237. package/src/hooks/useTooltip.tsx +95 -64
  238. package/src/scss/main.scss +74 -2
  239. package/src/store/chart.reducer.ts +4 -3
  240. package/src/test/CdcChart.dataTable.test.tsx +490 -16
  241. package/src/test/CdcChart.dendrogram.test.tsx +277 -0
  242. package/src/test/CdcChart.finalization.test.tsx +143 -0
  243. package/src/test/CdcChart.network.test.tsx +271 -0
  244. package/src/types/ChartConfig.ts +91 -54
  245. package/src/types/ChartContext.ts +5 -0
  246. package/src/types/Horizon.ts +1 -1
  247. package/LICENSE +0 -201
  248. package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
  249. package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
  250. package/src/components/Sankey/components/ColumnList.tsx +0 -19
  251. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
  252. package/src/components/Sankey/useSankeyAlert.tsx +0 -60
  253. package/src/data/legacy-defaults.ts +0 -18
  254. package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
  255. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
@@ -72,4 +72,33 @@ describe('buildBumpChartTooltipHtml', () => {
72
72
  expect(html).not.toContain('2014-01-01T00:00:00.000Z')
73
73
  expect(html).toContain('Frequency: 149')
74
74
  })
75
+
76
+ it('uses a customized column label instead of the active runtime series name', () => {
77
+ const html = buildBumpChartTooltipHtml({
78
+ config: buildConfig({
79
+ columns: {
80
+ Rank: { name: 'Rank', label: 'Rank Label' }
81
+ }
82
+ }),
83
+ colorScale: undefined,
84
+ dataRow: { Timestamp: '2014-01-01T00:00:00.000Z', Rank: '1' },
85
+ series: { dataKey: 'Rank', name: 'Runtime Rank' },
86
+ helpers
87
+ })
88
+
89
+ expect(html).toContain('Rank Label: 1')
90
+ expect(html).not.toContain('Runtime Rank: 1')
91
+ })
92
+
93
+ it('falls back to the series name when the column label matches the data key', () => {
94
+ const html = buildBumpChartTooltipHtml({
95
+ config: buildConfig(),
96
+ colorScale: undefined,
97
+ dataRow: { Timestamp: '2014-01-01T00:00:00.000Z', Rank: '1' },
98
+ series: { dataKey: 'Rank', name: 'Runtime Rank' },
99
+ helpers
100
+ })
101
+
102
+ expect(html).toContain('Runtime Rank: 1')
103
+ })
75
104
  })
@@ -0,0 +1,105 @@
1
+ import { describe, expect, it, vi } from 'vitest'
2
+ import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
3
+
4
+ vi.mock('@cdc/core/helpers/getTextWidth', () => ({
5
+ getTextWidth: (text: string) => text.length
6
+ }))
7
+
8
+ import { calculateRightYAxisWidth } from '../calculateRightYAxisWidth'
9
+
10
+ const formatNumber = (value: any) => String(value)
11
+ const tickLabelFont = 'normal 16px sans-serif'
12
+ const axisLabelFontSize = 16
13
+
14
+ const createRightAxisConfig = (yAxisOverrides = {}, runtimeYAxisOverrides = {}) => {
15
+ const baseConfig = createMockConfig()
16
+
17
+ return createMockConfig({
18
+ yAxis: {
19
+ ...baseConfig.yAxis,
20
+ rightAxisSize: 0,
21
+ rightHideAxis: false,
22
+ rightHideLabel: false,
23
+ rightHideTicks: false,
24
+ rightLabel: '',
25
+ rightLabelOffsetSize: 0,
26
+ rightTitlePlacement: 'top',
27
+ ...yAxisOverrides
28
+ },
29
+ runtime: {
30
+ ...baseConfig.runtime,
31
+ yAxis: {
32
+ ...baseConfig.runtime.yAxis,
33
+ rightHideTicks: false,
34
+ ...runtimeYAxisOverrides
35
+ }
36
+ }
37
+ })
38
+ }
39
+
40
+ describe('calculateRightYAxisWidth', () => {
41
+ it('uses measured right tick label space when saved rightAxisSize is zero', () => {
42
+ const width = calculateRightYAxisWidth({
43
+ axisLabelFontSize,
44
+ config: createRightAxisConfig(),
45
+ formatNumber,
46
+ tickLabelFont,
47
+ tickValues: [0, 1000],
48
+ yScaleRight: undefined
49
+ })
50
+
51
+ expect(width).toBe(17)
52
+ })
53
+
54
+ it('reserves measured side-title space after right tick labels while ignoring saved offset', () => {
55
+ const width = calculateRightYAxisWidth({
56
+ axisLabelFontSize,
57
+ config: createRightAxisConfig({
58
+ rightLabel: 'New Participants',
59
+ rightLabelOffsetSize: 36,
60
+ rightTitlePlacement: 'side'
61
+ }),
62
+ formatNumber,
63
+ tickLabelFont,
64
+ tickValues: [0, 10],
65
+ yScaleRight: undefined
66
+ })
67
+
68
+ expect(width).toBe(36)
69
+ })
70
+
71
+ it('ignores a larger saved rightAxisSize', () => {
72
+ const width = calculateRightYAxisWidth({
73
+ axisLabelFontSize,
74
+ config: createRightAxisConfig({ rightAxisSize: 100 }),
75
+ formatNumber,
76
+ tickLabelFont,
77
+ tickValues: [0, 1000],
78
+ yScaleRight: undefined
79
+ })
80
+
81
+ expect(width).toBe(17)
82
+ })
83
+
84
+ it('allows no gutter when all right-axis content is hidden', () => {
85
+ const width = calculateRightYAxisWidth({
86
+ axisLabelFontSize,
87
+ config: createRightAxisConfig(
88
+ {
89
+ rightHideAxis: true,
90
+ rightHideLabel: true,
91
+ rightTitlePlacement: 'top'
92
+ },
93
+ {
94
+ rightHideTicks: true
95
+ }
96
+ ),
97
+ formatNumber,
98
+ tickLabelFont,
99
+ tickValues: [],
100
+ yScaleRight: undefined
101
+ })
102
+
103
+ expect(width).toBe(0)
104
+ })
105
+ })
@@ -31,6 +31,17 @@ describe('countNumOfTicks', () => {
31
31
  expect(result).toBe(6)
32
32
  })
33
33
 
34
+ it('treats a null viewport override like the historical missing value', () => {
35
+ const result = countNumOfTicks({
36
+ ...baseArgs,
37
+ axis: 'xAxis',
38
+ runtime: { xAxis: { numTicks: 6, viewportNumTicks: null } },
39
+ currentViewport: 'xs',
40
+ isHorizontal: false
41
+ })
42
+ expect(result).toBe(6)
43
+ })
44
+
34
45
  it('falls back to numTicks when current viewport has no entry in viewportNumTicks', () => {
35
46
  const result = countNumOfTicks({
36
47
  ...baseArgs,
@@ -0,0 +1,182 @@
1
+ import defaults from '../../data/initial-state'
2
+ import { createNewChartConfig } from '../createNewChartConfig'
3
+ import { CURRENT_COVE_CONFIG_VERSION } from '@cdc/core/helpers/coveUpdateWorker'
4
+
5
+ describe('createNewChartConfig', () => {
6
+ it('deeply combines starter settings with current chart defaults', () => {
7
+ const starterConfig = {
8
+ type: 'chart',
9
+ visualizationType: 'Bar',
10
+ xAxis: { type: 'categorical', size: 100 },
11
+ filters: []
12
+ }
13
+
14
+ const config = createNewChartConfig(starterConfig)
15
+
16
+ expect(config).toMatchObject({
17
+ type: 'chart',
18
+ visualizationType: 'Bar',
19
+ version: CURRENT_COVE_CONFIG_VERSION,
20
+ titleStyle: 'small',
21
+ barThickness: defaults.barThickness,
22
+ xAxis: {
23
+ type: 'categorical',
24
+ size: 100,
25
+ numTicks: 6,
26
+ viewportNumTicks: { xs: 4, xxs: 4 }
27
+ },
28
+ yAxis: {
29
+ titlePlacement: 'top',
30
+ labelPlacement: 'On Date/Category Axis',
31
+ numTicks: 4,
32
+ gridLines: true,
33
+ hideAxis: true,
34
+ hideTicks: true,
35
+ autoMaxStrategy: 'clean-top-tick'
36
+ },
37
+ legend: { position: 'top', unified: true, expandForecastConfidenceIntervals: true },
38
+ table: {
39
+ download: true,
40
+ expanded: false,
41
+ show: true,
42
+ showDataTableLink: false,
43
+ stickyFirstColumn: false
44
+ },
45
+ dataFormat: { commas: true }
46
+ })
47
+ expect(config.filters).toEqual([])
48
+ expect(starterConfig).toEqual({
49
+ type: 'chart',
50
+ visualizationType: 'Bar',
51
+ xAxis: { type: 'categorical', size: 100 },
52
+ filters: []
53
+ })
54
+ })
55
+
56
+ it.each([
57
+ ['Area Chart', { visualizationSubType: 'stacked' }],
58
+ ['Forecasting', { xAxis: { type: 'date', dateParseFormat: '%Y-%m-%d', dateDisplayFormat: '%b. %-d %Y' } }],
59
+ ['HeatMap', { yAxis: { type: 'categorical', titlePlacement: 'top' }, legend: { position: 'top' } }],
60
+ ['Horizon Chart', { horizon: { numLayers: 4, mode: 'offset', bandGap: 15, bottomPadding: 15 } }],
61
+ ['Box Plot', { yAxis: { labelPlacement: 'On Date/Category Axis' } }],
62
+ ['Paired Bar', { orientation: 'horizontal' }],
63
+ ['Deviation Bar', { orientation: 'horizontal' }],
64
+ ['Bump Chart', { xAxis: { type: 'date-time' } }]
65
+ ])('authors the %s stable type contract', (visualizationType, expected) => {
66
+ expect(createNewChartConfig({ type: 'chart', visualizationType })).toMatchObject(expected)
67
+ })
68
+
69
+ it('uses horizontal axis visibility defaults while preserving explicit starter values', () => {
70
+ const config = createNewChartConfig({
71
+ type: 'chart',
72
+ visualizationType: 'Bar',
73
+ orientation: 'horizontal'
74
+ })
75
+
76
+ expect(config.xAxis).toMatchObject({ hideAxis: true, hideTicks: true })
77
+ expect(config.yAxis).toMatchObject({ hideAxis: false, hideTicks: false })
78
+
79
+ const explicitlyConfigured = createNewChartConfig({
80
+ type: 'chart',
81
+ visualizationType: 'Bar',
82
+ orientation: 'horizontal',
83
+ xAxis: { hideAxis: false, hideTicks: false },
84
+ yAxis: { hideAxis: true, hideTicks: true }
85
+ })
86
+
87
+ expect(explicitlyConfigured.xAxis).toMatchObject({ hideAxis: false, hideTicks: false })
88
+ expect(explicitlyConfigured.yAxis).toMatchObject({ hideAxis: true, hideTicks: true })
89
+ })
90
+
91
+ it('uses dashboard-specific table visibility', () => {
92
+ const config = createNewChartConfig({ type: 'chart', visualizationType: 'Bar' }, { isDashboard: true })
93
+
94
+ expect(config.table.show).toBe(false)
95
+ expect(config.table.download).toBe(false)
96
+ expect(config.table.showDataTableLink).toBe(false)
97
+ })
98
+
99
+ it('preserves an explicit dashboard data table link opt-in', () => {
100
+ const config = createNewChartConfig({
101
+ type: 'chart',
102
+ visualizationType: 'Bar',
103
+ table: { showDataTableLink: true }
104
+ })
105
+
106
+ expect(config.table.showDataTableLink).toBe(true)
107
+ })
108
+
109
+ it('preserves an explicit table download opt-out', () => {
110
+ const config = createNewChartConfig({
111
+ type: 'chart',
112
+ visualizationType: 'Bar',
113
+ table: { download: false }
114
+ })
115
+
116
+ expect(config.table.download).toBe(false)
117
+ })
118
+
119
+ it('preserves an explicit unified legend opt-out', () => {
120
+ const config = createNewChartConfig({
121
+ type: 'chart',
122
+ visualizationType: 'Bar',
123
+ legend: { unified: false }
124
+ })
125
+
126
+ expect(config.legend.unified).toBe(false)
127
+ })
128
+
129
+ it('enables expanded forecast confidence interval legends for new charts', () => {
130
+ const config = createNewChartConfig({ type: 'chart', visualizationType: 'Forecasting' })
131
+
132
+ expect(config.legend.expandForecastConfidenceIntervals).toBe(true)
133
+ })
134
+
135
+ it('preserves an explicit expanded forecast confidence interval legend opt-out', () => {
136
+ const config = createNewChartConfig({
137
+ type: 'chart',
138
+ visualizationType: 'Forecasting',
139
+ legend: { expandForecastConfidenceIntervals: false }
140
+ })
141
+
142
+ expect(config.legend.expandForecastConfidenceIntervals).toBe(false)
143
+ })
144
+
145
+ it('uses compact label gaps and automatic ticks for a new HeatMap', () => {
146
+ const config = createNewChartConfig({ type: 'chart', visualizationType: 'HeatMap' })
147
+
148
+ expect(config.heatmap).toMatchObject({
149
+ rowLabelGap: 0,
150
+ columnLabelGap: 20,
151
+ horizontalScroll: false,
152
+ minColumnWidth: 44
153
+ })
154
+ expect(config.xAxis.manual).toBe(false)
155
+ expect(config.xAxis.numTicks).toBeUndefined()
156
+ expect(config.xAxis.viewportNumTicks).toEqual({})
157
+ })
158
+
159
+ it.each([
160
+ ['Bar', 'sequential_blue', false],
161
+ ['Line', 'qualitative_standard', false],
162
+ ['Horizon Chart', 'sequential_blue', false],
163
+ ['HeatMap', 'sequential_blue', false],
164
+ ['Sankey', 'sequential_bluereverse', true]
165
+ ])('uses the new-chart palette for %s charts', (visualizationType, name, isReversed) => {
166
+ const config = createNewChartConfig({ type: 'chart', visualizationType })
167
+
168
+ expect(config.general.palette).toMatchObject({ name, isReversed, version: '2.1' })
169
+ expect(defaults.general.palette.version).toBe('2.1')
170
+ })
171
+
172
+ it('preserves an explicit palette instead of replacing it with the chart-type default', () => {
173
+ const config = createNewChartConfig({
174
+ type: 'chart',
175
+ visualizationType: 'Line',
176
+ general: { palette: { name: 'qualitative_bold', isReversed: true, version: '2.1' } }
177
+ })
178
+
179
+ expect(config.general.palette).toMatchObject({ name: 'qualitative_bold', isReversed: true, version: '2.1' })
180
+ expect(defaults.general.palette.name).toBe('sequential_blue')
181
+ })
182
+ })
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { getBoxPlotConfig } from '../getBoxPlotConfig'
4
+ import { ChartConfig } from '../../types/ChartConfig'
5
+
6
+ const createBoxPlotConfig = (): ChartConfig =>
7
+ ({
8
+ xAxis: {
9
+ dataKey: 'Group'
10
+ },
11
+ series: [
12
+ {
13
+ dataKey: 'Score'
14
+ }
15
+ ],
16
+ dataFormat: {
17
+ roundTo: 0
18
+ }
19
+ } as ChartConfig)
20
+
21
+ describe('getBoxPlotConfig', () => {
22
+ it('sorts generated value arrays numerically for numeric strings', () => {
23
+ const data = [
24
+ { Group: 'Group A', Score: '1' },
25
+ { Group: 'Group A', Score: '2' },
26
+ { Group: 'Group A', Score: '3' },
27
+ { Group: 'Group A', Score: '10' },
28
+ { Group: 'Group A', Score: '20' },
29
+ { Group: 'Group A', Score: '100' }
30
+ ]
31
+
32
+ const [plots] = getBoxPlotConfig(createBoxPlotConfig(), data)
33
+
34
+ expect(plots[0].values).toEqual([1, 2, 3, 10, 20, 100])
35
+ expect(plots[0].columnNonOutliers).toEqual([1, 2, 3, 10, 20])
36
+ expect(plots[0].columnOutliers).toEqual([100])
37
+ })
38
+
39
+ it('parses formatted numeric strings before sorting', () => {
40
+ const data = [
41
+ { Group: 'Group A', Score: '1,234.5' },
42
+ { Group: 'Group A', Score: '20' },
43
+ { Group: 'Group A', Score: '3' }
44
+ ]
45
+
46
+ const [plots] = getBoxPlotConfig(createBoxPlotConfig(), data)
47
+
48
+ expect(plots[0].values).toEqual([3, 20, 1234.5])
49
+ expect(plots[0].columnCount).toBe(3)
50
+ })
51
+
52
+ it('ignores blank, null, and nonnumeric values when generating box plot stats', () => {
53
+ const data = [
54
+ { Group: 'Group A', Score: '1' },
55
+ { Group: 'Group A', Score: '' },
56
+ { Group: 'Group A', Score: null },
57
+ { Group: 'Group A', Score: undefined },
58
+ { Group: 'Group A', Score: 'n/a' },
59
+ { Group: 'Group A', Score: '2' },
60
+ { Group: 'Group A', Score: '3' }
61
+ ]
62
+
63
+ const [plots] = getBoxPlotConfig(createBoxPlotConfig(), data)
64
+
65
+ expect(plots[0].values).toEqual([1, 2, 3])
66
+ expect(plots[0].columnCount).toBe(3)
67
+ expect(plots[0].columnNonOutliers).toEqual([1, 2, 3])
68
+ expect(plots[0].columnOutliers).toEqual([])
69
+ })
70
+ })
@@ -0,0 +1,113 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { ChartConfig } from '../../types/ChartConfig'
3
+ import { getColorCodeCategoryColorMap } from '../getColorCodeCategoryColorMap'
4
+
5
+ const buildConfig = (
6
+ colorCode = 'Group',
7
+ paletteOverrides: Partial<ChartConfig['general']['palette']> = {}
8
+ ): ChartConfig =>
9
+ ({
10
+ visualizationType: 'Bar',
11
+ general: {
12
+ palette: {
13
+ name: 'sequential_blue',
14
+ version: '2.0',
15
+ ...paletteOverrides
16
+ }
17
+ },
18
+ legend: { colorCode }
19
+ } as ChartConfig)
20
+
21
+ const colorPalettes = {
22
+ sequential_blue: Array.from({ length: 9 }, (_, index) => `#00000${index}`),
23
+ sequential_bluereverse: Array.from({ length: 9 }, (_, index) => `#00000${8 - index}`),
24
+ qualitative_standard: Array.from({ length: 9 }, (_, index) => `qualitative-${index}`)
25
+ }
26
+
27
+ describe('getColorCodeCategoryColorMap', () => {
28
+ it('assigns colors to unique categories in encounter order', () => {
29
+ const result = getColorCodeCategoryColorMap(
30
+ buildConfig(),
31
+ [{ Group: 'Group A' }, { Group: 'Group B' }, { Group: 'Group A' }],
32
+ colorPalettes
33
+ )
34
+
35
+ expect(Array.from(result.entries())).toEqual([
36
+ ['Group A', '#000000'],
37
+ ['Group B', '#000001']
38
+ ])
39
+ })
40
+
41
+ it('uses the 2.1 chart distribution for two categories in a sequential palette', () => {
42
+ const result = getColorCodeCategoryColorMap(
43
+ buildConfig('Group', { version: '2.1' }),
44
+ [{ Group: 'Group A' }, { Group: 'Group B' }, { Group: 'Group A' }],
45
+ colorPalettes
46
+ )
47
+
48
+ expect(Array.from(result.entries())).toEqual([
49
+ ['Group A', '#000004'],
50
+ ['Group B', '#000008']
51
+ ])
52
+ })
53
+
54
+ it('uses the full V2 chart distribution for three categories in a sequential palette', () => {
55
+ const result = getColorCodeCategoryColorMap(
56
+ buildConfig('Group', { version: '2.1' }),
57
+ [{ Group: 'Group A' }, { Group: 'Group B' }, { Group: 'Group C' }],
58
+ colorPalettes
59
+ )
60
+
61
+ expect(Array.from(result.values())).toEqual(['#000000', '#000004', '#000008'])
62
+ })
63
+
64
+ it('uses the V2 qualitative distribution for color-coded categories', () => {
65
+ const result = getColorCodeCategoryColorMap(
66
+ buildConfig('Group', { name: 'qualitative_standard', version: '2.1' }),
67
+ [{ Group: 'Group A' }, { Group: 'Group B' }, { Group: 'Group C' }],
68
+ colorPalettes
69
+ )
70
+
71
+ expect(Array.from(result.values())).toEqual(['qualitative-0', 'qualitative-4', 'qualitative-6'])
72
+ })
73
+
74
+ it('uses the V2 sequential colors in reverse order for a reversed palette', () => {
75
+ const rows = [{ Group: 'Group A' }, { Group: 'Group B' }]
76
+ const reversed = getColorCodeCategoryColorMap(
77
+ buildConfig('Group', {
78
+ name: 'sequential_bluereverse',
79
+ version: '2.1',
80
+ isReversed: true
81
+ }),
82
+ rows,
83
+ colorPalettes
84
+ )
85
+
86
+ expect(Array.from(reversed.values())).toEqual(['#000008', '#000004'])
87
+ })
88
+
89
+ it('preserves raw palette order for custom palettes', () => {
90
+ const rows = [{ Group: 'Group A' }, { Group: 'Group B' }]
91
+ const custom = getColorCodeCategoryColorMap(
92
+ buildConfig('Group', { version: '2.1', customColors: ['#111111', '#222222'] }),
93
+ rows,
94
+ colorPalettes
95
+ )
96
+
97
+ expect(Array.from(custom.values())).toEqual(['#111111', '#222222'])
98
+ })
99
+
100
+ it('repeats the palette when there are more categories than colors', () => {
101
+ const result = getColorCodeCategoryColorMap(
102
+ buildConfig(),
103
+ [{ Group: 'Group A' }, { Group: 'Group B' }, { Group: 'Group C' }],
104
+ colorPalettes
105
+ )
106
+
107
+ expect(Array.from(result.values())).toEqual(['#000000', '#000001', '#000002'])
108
+ })
109
+
110
+ it('returns an empty map without a color-code column', () => {
111
+ expect(getColorCodeCategoryColorMap(buildConfig(''), [{ Group: 'Group A' }], colorPalettes).size).toBe(0)
112
+ })
113
+ })