@cdc/chart 4.26.6 → 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 (276) hide show
  1. package/CONFIG.md +504 -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 -96498
  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/flueview.json +22051 -0
  22. package/examples/private/overlay-legend.json +485 -0
  23. package/examples/private/pie-test.json +469 -0
  24. package/examples/vega-remote.json +22 -0
  25. package/package.json +11 -13
  26. package/src/CdcChart.tsx +2 -2
  27. package/src/CdcChartComponent.tsx +444 -217
  28. package/src/_stories/Chart.BarRace.stories.tsx +443 -0
  29. package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
  30. package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
  31. package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
  32. package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
  33. package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
  34. package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
  35. package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
  36. package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
  37. package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
  38. package/src/_stories/Chart.LineRace.stories.tsx +149 -0
  39. package/src/_stories/Chart.Network.stories.tsx +432 -0
  40. package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
  41. package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
  42. package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
  43. package/src/_stories/Chart.PieLabels.stories.tsx +49 -0
  44. package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
  45. package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
  46. package/src/_stories/Chart.Sankey.stories.tsx +417 -0
  47. package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
  48. package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
  49. package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
  50. package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
  51. package/src/_stories/Chart.smoke.stories.tsx +14 -1
  52. package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
  53. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
  54. package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
  55. package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
  56. package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
  57. package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
  58. package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
  59. package/src/_stories/ChartPrefixSuffix.smoke.stories.tsx +28 -1
  60. package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
  61. package/src/_stories/_mock/editor-test-configs.json +20 -10
  62. package/src/_stories/_mock/editor-test-datasets.json +38 -42
  63. package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
  64. package/src/_stories/_mock/heatmap.json +2 -2
  65. package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
  66. package/src/_stories/_mock/sankey_mock.json +23 -27
  67. package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
  68. package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
  69. package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
  70. package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
  71. package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
  72. package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
  73. package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
  74. package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
  75. package/src/components/Axis/BottomAxis.tsx +1 -1
  76. package/src/components/Axis/Categorical.Axis.tsx +3 -3
  77. package/src/components/Axis/RightAxis.tsx +21 -13
  78. package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -8
  79. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +13 -4
  80. package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
  81. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +30 -26
  82. package/src/components/BarChart/components/BarChart.Vertical.tsx +38 -28
  83. package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
  84. package/src/components/BarChart/components/context.tsx +6 -0
  85. package/src/components/BarChart/helpers/getBarData.ts +28 -28
  86. package/src/components/BarChart/helpers/getBarHeights.ts +2 -2
  87. package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
  88. package/src/components/BarChart/helpers/index.ts +8 -0
  89. package/src/components/BarChart/helpers/portionPattern.ts +243 -0
  90. package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
  91. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
  92. package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
  93. package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +202 -0
  94. package/src/components/BarChart/helpers/useBarChart.ts +60 -60
  95. package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
  96. package/src/components/BarChartRace/BarChartRace.tsx +230 -0
  97. package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
  98. package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
  99. package/src/components/BarChartRace/bar-chart-race.css +160 -0
  100. package/src/components/BarChartRace/helpers.test.ts +114 -0
  101. package/src/components/BarChartRace/helpers.ts +185 -0
  102. package/src/components/BarChartRace/index.ts +3 -0
  103. package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +7 -6
  104. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +9 -7
  105. package/src/components/BoxPlot/helpers/index.ts +13 -83
  106. package/src/components/Brush/BrushSelector.tsx +10 -24
  107. package/src/components/Brush/MiniChartPreview.tsx +33 -1
  108. package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
  109. package/src/components/Dendrogram/dendrogram.css +113 -0
  110. package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
  111. package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
  112. package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
  113. package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
  114. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
  115. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
  116. package/src/components/Dendrogram/index.tsx +1 -0
  117. package/src/components/Dendrogram/types/index.ts +66 -0
  118. package/src/components/EditorPanel/EditorPanel.tsx +1111 -805
  119. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
  120. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
  121. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
  122. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
  123. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +104 -61
  124. package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
  125. package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
  126. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +236 -36
  127. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
  128. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
  129. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +21 -6
  130. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
  131. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
  132. package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
  133. package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
  134. package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
  135. package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
  136. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
  137. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
  138. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +5 -4
  139. package/src/components/EditorPanel/useEditorPermissions.test.tsx +94 -0
  140. package/src/components/EditorPanel/useEditorPermissions.ts +75 -25
  141. package/src/components/Forecasting/Forecasting.tsx +16 -7
  142. package/src/components/HeatMap/components/HeatMap.tsx +511 -178
  143. package/src/components/HeatMap/heatmap.constants.ts +7 -3
  144. package/src/components/HeatMap/heatmap.css +101 -2
  145. package/src/components/HeatMap/helpers/layout.ts +80 -0
  146. package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
  147. package/src/components/HeatMap/tests/helpers.test.ts +79 -0
  148. package/src/components/Legend/Legend.Component.tsx +163 -148
  149. package/src/components/Legend/Legend.Suppression.tsx +3 -3
  150. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
  151. package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -35
  152. package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
  153. package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
  154. package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
  155. package/src/components/LineChart/helpers.test.ts +126 -38
  156. package/src/components/LineChart/helpers.ts +9 -6
  157. package/src/components/LineChart/index.tsx +71 -27
  158. package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
  159. package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
  160. package/src/components/LineChartRace/LineChartRace.tsx +134 -0
  161. package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
  162. package/src/components/LineChartRace/helpers.test.ts +88 -0
  163. package/src/components/LineChartRace/helpers.ts +157 -0
  164. package/src/components/LineChartRace/index.ts +3 -0
  165. package/src/components/LineChartRace/line-chart-race.css +16 -0
  166. package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
  167. package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
  168. package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
  169. package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
  170. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
  171. package/src/components/LinearChart.tsx +85 -54
  172. package/src/components/Network/components/Network.tsx +372 -0
  173. package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
  174. package/src/components/Network/helpers/layoutNetwork.ts +87 -0
  175. package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
  176. package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
  177. package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
  178. package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
  179. package/src/components/Network/index.tsx +3 -0
  180. package/src/components/Network/network.css +107 -0
  181. package/src/components/Network/types/index.ts +56 -0
  182. package/src/components/PairedBarChart.jsx +5 -10
  183. package/src/components/PairedBarChart.test.tsx +71 -0
  184. package/src/components/PieChart/PieChart.tsx +121 -13
  185. package/src/components/PieChart/helpers/labelPlacement.test.ts +88 -0
  186. package/src/components/PieChart/helpers/labelPlacement.ts +95 -0
  187. package/src/components/RacingChartRenderer.tsx +44 -0
  188. package/src/components/Regions/components/Regions.test.tsx +179 -0
  189. package/src/components/Regions/components/Regions.tsx +104 -69
  190. package/src/components/Regions/index.tsx +2 -3
  191. package/src/components/Sankey/components/Sankey.tsx +264 -437
  192. package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
  193. package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
  194. package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
  195. package/src/components/Sankey/sankey.scss +57 -116
  196. package/src/components/Sankey/types/index.ts +41 -4
  197. package/src/components/ScatterPlot/ScatterPlot.jsx +28 -12
  198. package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
  199. package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
  200. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
  201. package/src/components/raceTiming.ts +18 -0
  202. package/src/data/initial-state.js +79 -31
  203. package/src/helpers/applyChartTypeContract.ts +71 -0
  204. package/src/helpers/axisLabelFontSize.ts +2 -2
  205. package/src/helpers/boxPlotStats.ts +60 -0
  206. package/src/helpers/bumpChartTooltip.ts +2 -1
  207. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
  208. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
  209. package/src/helpers/calculateRightYAxisWidth.ts +60 -0
  210. package/src/helpers/categoryOrder.ts +60 -0
  211. package/src/helpers/colorAssignmentHelpers.ts +2 -2
  212. package/src/helpers/createNewChartConfig.ts +56 -0
  213. package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
  214. package/src/helpers/forecastLegend.ts +88 -0
  215. package/src/helpers/getBoxPlotConfig.ts +26 -38
  216. package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
  217. package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
  218. package/src/helpers/getColorScale.ts +31 -22
  219. package/src/helpers/getComboChartConfig.ts +0 -1
  220. package/src/helpers/getDynamicYAxisCategories.ts +69 -0
  221. package/src/helpers/getExcludedData.ts +0 -1
  222. package/src/helpers/getHasBoundarySuppression.ts +4 -3
  223. package/src/helpers/getMinMax.ts +11 -8
  224. package/src/helpers/getNewRuntime.ts +4 -3
  225. package/src/helpers/getPiePercent.ts +3 -3
  226. package/src/helpers/getStackedSeriesMax.ts +51 -0
  227. package/src/helpers/getV21ChartDistributionColors.ts +57 -0
  228. package/src/helpers/handleRankByValue.ts +15 -15
  229. package/src/helpers/isConvertLineToBarGraph.ts +2 -2
  230. package/src/helpers/seriesColumnSettings.ts +31 -3
  231. package/src/helpers/sizeHelpers.ts +4 -5
  232. package/src/helpers/smallMultiplesHelpers.ts +10 -13
  233. package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
  234. package/src/helpers/tests/boxPlotStats.test.ts +31 -0
  235. package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
  236. package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
  237. package/src/helpers/tests/categoryOrder.test.ts +57 -0
  238. package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
  239. package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
  240. package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
  241. package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
  242. package/src/helpers/tests/getColorScale.test.ts +226 -1
  243. package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
  244. package/src/helpers/tests/getMinMax.test.ts +41 -0
  245. package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
  246. package/src/helpers/tests/handleRankByValue.test.ts +55 -37
  247. package/src/helpers/tests/seriesColumnSettings.test.ts +83 -0
  248. package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
  249. package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
  250. package/src/helpers/tooltipHelpers.ts +16 -0
  251. package/src/hooks/tests/useRightAxis.test.tsx +143 -2
  252. package/src/hooks/tests/useScales.test.tsx +3 -6
  253. package/src/hooks/tests/useTooltip.test.tsx +484 -0
  254. package/src/hooks/usePrefersReducedMotion.ts +22 -0
  255. package/src/hooks/useReduceData.ts +4 -36
  256. package/src/hooks/useRightAxis.ts +19 -26
  257. package/src/hooks/useScales.ts +15 -0
  258. package/src/hooks/useTooltip.tsx +106 -71
  259. package/src/scss/main.scss +74 -2
  260. package/src/store/chart.reducer.ts +4 -4
  261. package/src/test/CdcChart.dataTable.test.tsx +582 -14
  262. package/src/test/CdcChart.dendrogram.test.tsx +277 -0
  263. package/src/test/CdcChart.finalization.test.tsx +143 -0
  264. package/src/test/CdcChart.network.test.tsx +271 -0
  265. package/src/types/ChartConfig.ts +91 -54
  266. package/src/types/ChartContext.ts +5 -0
  267. package/src/types/Horizon.ts +1 -1
  268. package/LICENSE +0 -201
  269. package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
  270. package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
  271. package/src/components/Sankey/components/ColumnList.tsx +0 -19
  272. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
  273. package/src/components/Sankey/useSankeyAlert.tsx +0 -60
  274. package/src/data/legacy-defaults.ts +0 -18
  275. package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
  276. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
@@ -0,0 +1,141 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { describe, expect, it, vi } from 'vitest'
4
+ import { scaleBand, scaleLinear } from '@visx/scale'
5
+ import VisualizationRenderer from '../VisualizationRenderer'
6
+ import ConfigContext from '../../../ConfigContext'
7
+ import { createMockChartContext } from './mockConfigContext'
8
+
9
+ vi.mock('../../AreaChart', () => ({
10
+ AreaChartStacked: () => <g data-testid='area-chart-stacked' />
11
+ }))
12
+
13
+ vi.mock('../../BarChart', () => ({
14
+ default: () => <g data-testid='bar-chart' />
15
+ }))
16
+
17
+ vi.mock('../../LineChart', () => ({
18
+ default: () => <g className='line-chart-group' data-testid='line-chart' />
19
+ }))
20
+
21
+ vi.mock('../../Forecasting', () => ({
22
+ default: () => <g data-testid='forecasting' />
23
+ }))
24
+
25
+ vi.mock('../../BoxPlot/BoxPlot.Vertical', () => ({
26
+ default: () => <g data-testid='box-plot-vertical' />
27
+ }))
28
+
29
+ vi.mock('../../BoxPlot/BoxPlot.Horizontal', () => ({
30
+ default: () => <g data-testid='box-plot-horizontal' />
31
+ }))
32
+
33
+ vi.mock('../../DeviationBar', () => ({
34
+ default: () => <g data-testid='deviation-bar' />
35
+ }))
36
+
37
+ vi.mock('../../ForestPlot', () => ({
38
+ default: () => <g data-testid='forest-plot' />
39
+ }))
40
+
41
+ vi.mock('../../HorizonChart', () => ({
42
+ HorizonChart: () => <g data-testid='horizon-chart' />
43
+ }))
44
+
45
+ vi.mock('../../PairedBarChart', () => ({
46
+ default: () => <g data-testid='paired-bar-chart' />
47
+ }))
48
+
49
+ vi.mock('../../ScatterPlot', () => ({
50
+ default: () => <g data-testid='scatter-plot' />
51
+ }))
52
+
53
+ vi.mock('../../WarmingStripes', () => ({
54
+ default: () => <g data-testid='warming-stripes' />
55
+ }))
56
+
57
+ const renderVisualizationRenderer = (configOverrides = {}) => {
58
+ const context = createMockChartContext(configOverrides)
59
+ const xScale = scaleBand({ domain: ['Jan', 'Feb'], range: [0, 400] })
60
+ const yScale = scaleLinear({ domain: [0, 100], range: [300, 0] })
61
+
62
+ return render(
63
+ <ConfigContext.Provider value={context}>
64
+ <svg>
65
+ <VisualizationRenderer
66
+ xScale={xScale}
67
+ yScale={yScale}
68
+ xMax={400}
69
+ yMax={300}
70
+ seriesScale={undefined}
71
+ xScaleNoPadding={xScale}
72
+ min={0}
73
+ max={100}
74
+ parentWidth={500}
75
+ yAxisWidth={50}
76
+ forestHeight={300}
77
+ animatedChart={false}
78
+ tooltipData={undefined}
79
+ showTooltip={undefined}
80
+ handleTooltipMouseOver={() => {}}
81
+ handleTooltipMouseOff={() => {}}
82
+ handleTooltipClick={() => {}}
83
+ getXAxisData={d => d.Date}
84
+ getYAxisData={(d, seriesKey) => d[seriesKey]}
85
+ svgRef={{ current: null }}
86
+ forestPlotRightLabelRef={{ current: null }}
87
+ />
88
+ </svg>
89
+ </ConfigContext.Provider>
90
+ )
91
+ }
92
+
93
+ describe('VisualizationRenderer', () => {
94
+ it('renders only the Combo layers represented by runtime series keys', () => {
95
+ const { container } = renderVisualizationRenderer({
96
+ visualizationType: 'Combo',
97
+ runtime: {
98
+ ...createMockChartContext().config.runtime,
99
+ barSeriesKeys: ['cases'],
100
+ lineSeriesKeys: ['coverage'],
101
+ areaSeriesKeys: [],
102
+ forecastingSeriesKeys: []
103
+ }
104
+ })
105
+
106
+ expect(container.querySelectorAll('[data-testid="bar-chart"]')).toHaveLength(1)
107
+ expect(container.querySelectorAll('.line-chart-group')).toHaveLength(1)
108
+ expect(container.querySelectorAll('[data-testid="area-chart-stacked"]')).toHaveLength(0)
109
+ expect(container.querySelectorAll('[data-testid="forecasting"]')).toHaveLength(0)
110
+ })
111
+
112
+ it('does not render BarChart or LineChart for Combo when those runtime buckets are empty', () => {
113
+ const { container } = renderVisualizationRenderer({
114
+ visualizationType: 'Combo',
115
+ runtime: {
116
+ ...createMockChartContext().config.runtime,
117
+ barSeriesKeys: [],
118
+ lineSeriesKeys: [],
119
+ areaSeriesKeys: [{ dataKey: 'area' }],
120
+ forecastingSeriesKeys: [{ dataKey: 'forecast' }]
121
+ }
122
+ })
123
+
124
+ expect(container.querySelectorAll('[data-testid="bar-chart"]')).toHaveLength(0)
125
+ expect(container.querySelectorAll('.line-chart-group')).toHaveLength(0)
126
+ expect(container.querySelectorAll('[data-testid="area-chart-stacked"]')).toHaveLength(1)
127
+ expect(container.querySelectorAll('[data-testid="forecasting"]')).toHaveLength(1)
128
+ })
129
+
130
+ it('renders the Bump Chart line layer once', () => {
131
+ const { container } = renderVisualizationRenderer({ visualizationType: 'Bump Chart' })
132
+
133
+ expect(container.querySelectorAll('.line-chart-group')).toHaveLength(1)
134
+ })
135
+
136
+ it('keeps non-Combo Bar rendering unchanged', () => {
137
+ const { container } = renderVisualizationRenderer({ visualizationType: 'Bar' })
138
+
139
+ expect(container.querySelectorAll('[data-testid="bar-chart"]')).toHaveLength(1)
140
+ })
141
+ })
@@ -39,7 +39,8 @@ export const createMockConfig = (overrides: Partial<ChartConfig> = {}): ChartCon
39
39
  anchors: [],
40
40
  axisPadding: 0,
41
41
  labelPlacement: 'On Date/Category Axis',
42
- rightAxisSize: 0
42
+ rightAxisSize: 0,
43
+ rightTitlePlacement: 'top'
43
44
  },
44
45
  runtime: {
45
46
  xAxis: {
@@ -24,10 +24,11 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
24
24
  import ConfigContext from '../ConfigContext'
25
25
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
26
26
  import useIntersectionObserver from '../hooks/useIntersectionObserver'
27
- import Regions from './Regions'
27
+ import { RegionFills, RegionLabels, useRegionLayouts, type RegionBoundaryMode } from './Regions'
28
28
  import { CategoricalYAxis, LeftAxis, LeftAxisGridlines, BottomAxis, PairedBarAxis, RightAxis } from './Axis'
29
29
  import BrushSelector from './Brush/BrushSelector'
30
30
  import VisualizationRenderer from './LinearChart/VisualizationRenderer'
31
+ import ValueAxisAnchors, { alignStrokeToPixel } from './LinearChart/ValueAxisAnchors'
31
32
  import { TYPES_WITHOUT_GRID, TYPES_WITH_TOOLTIP_GUIDES } from './LinearChart/linearChart.constants'
32
33
  import { useTickFormatters } from './LinearChart/utils/tickFormatting'
33
34
 
@@ -36,6 +37,8 @@ import { isLegendWrapViewport, isMobileFontViewport } from '@cdc/core/helpers/vi
36
37
  import { calcInitialHeight } from '../helpers/sizeHelpers'
37
38
  import { calculateHorizontalBarCategoryLabelWidth } from '../helpers/calculateHorizontalBarCategoryLabelWidth'
38
39
  import { calculateLeftYAxisWidth } from '../helpers/calculateLeftYAxisWidth'
40
+ import { getDataDrivenYAxisCategories, isDataDrivenYAxis } from '../helpers/dataDrivenYAxisCategories'
41
+ import { calculateRightYAxisWidth } from '../helpers/calculateRightYAxisWidth'
39
42
  import { getAxisLabelFontSize } from '../helpers/axisLabelFontSize'
40
43
  import { hasSpacedInlineLabel } from '../helpers/hasSpacedInlineLabel'
41
44
  import { getYAxisDomainData, getYAxisFilterDomainBehavior } from '../helpers/getYAxisDomainData'
@@ -108,6 +111,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
108
111
  // prettier-ignore
109
112
  const {
110
113
  config,
114
+ convertLineToBarGraph,
111
115
  currentViewport,
112
116
  vizViewport,
113
117
  formatDate,
@@ -135,6 +139,13 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
135
139
  const { visualizationType, orientation, xAxis, yAxis, runtime, legend, forestPlot, debugSvg } = config
136
140
 
137
141
  const { inlineLabel } = config.yAxis
142
+ const dataDrivenYAxisCategories = getDataDrivenYAxisCategories(config, data)
143
+ const renderedYAxisCategories = isDataDrivenYAxis(config)
144
+ ? dataDrivenYAxisCategories?.categories || []
145
+ : config.yAxis.categories
146
+ const axisLayoutConfig = isDataDrivenYAxis(config)
147
+ ? { ...config, yAxis: { ...config.yAxis, categories: renderedYAxisCategories } }
148
+ : config
138
149
 
139
150
  // HOOKS % STATES
140
151
  const dataForMinMax = getYAxisDomainData({
@@ -144,9 +155,10 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
144
155
  fullEligibleDomainData: yAxisDomainData
145
156
  })
146
157
  const isStableYAxisDomain = getYAxisFilterDomainBehavior(config) === 'stable'
147
- const rawEligibleYAxisDomainData = Array.isArray(config.yAxisDomainData) && config.yAxisDomainData.length > 0
148
- ? getExcludedData(config, config.yAxisDomainData)
149
- : isStableYAxisDomain && Array.isArray(excludedData)
158
+ const rawEligibleYAxisDomainData =
159
+ Array.isArray(config.yAxisDomainData) && config.yAxisDomainData.length > 0
160
+ ? getExcludedData(config, config.yAxisDomainData)
161
+ : isStableYAxisDomain && Array.isArray(excludedData)
150
162
  ? excludedData
151
163
  : tableData
152
164
  const suppressionDomainData = getYAxisDomainData({
@@ -286,9 +298,19 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
286
298
  // State for computed left-axis width - shared across all linear-chart types.
287
299
  const [currentYAxisWidth, setCurrentYAxisWidth] = useState<number>(DEFAULT_LEFT_Y_AXIS_WIDTH)
288
300
  const yAxisWidth = currentYAxisWidth
301
+ const rightAxisWidth = hasRightAxis
302
+ ? calculateRightYAxisWidth({
303
+ axisLabelFontSize,
304
+ config,
305
+ formatNumber,
306
+ tickLabelFont: GET_TEXT_WIDTH_FONT,
307
+ tickValues: rightTickValues,
308
+ yScaleRight
309
+ })
310
+ : 0
289
311
 
290
312
  // Chart width calculation using the current y-axis width
291
- const xMax = Math.max(0, parentWidth - yAxisWidth - (hasRightAxis ? config.yAxis.rightAxisSize : 0))
313
+ const xMax = Math.max(0, parentWidth - yAxisWidth - rightAxisWidth)
292
314
 
293
315
  // Stabilize brush container dimensions when brushDynamicYAxis is enabled.
294
316
  // Without this, y-axis rescaling on each brush change creates a feedback loop:
@@ -335,6 +357,16 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
335
357
  })
336
358
  const effectiveYTickValues = sharedYAxisTickValues ?? yTickValues
337
359
 
360
+ const comboHasBarSeries = visualizationType === 'Combo' && Boolean(config.runtime?.barSeriesKeys?.length)
361
+ const regionBoundaryMode: RegionBoundaryMode =
362
+ visualizationType === 'Bar' || comboHasBarSeries || convertLineToBarGraph ? 'band' : 'point'
363
+ const regionLayout = useRegionLayouts({
364
+ xScale,
365
+ seriesScale,
366
+ boundaryMode: regionBoundaryMode,
367
+ xMax
368
+ })
369
+
338
370
  // Consolidated tick formatters
339
371
  const { handleLeftTickFormatting, handleBottomTickFormatting } = useTickFormatters({
340
372
  isLogarithmicAxis,
@@ -384,7 +416,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
384
416
  }
385
417
 
386
418
  return calculateLeftYAxisWidth({
387
- config,
419
+ config: axisLayoutConfig,
388
420
  data,
389
421
  yScale,
390
422
  numTicks: handleNumTicks,
@@ -398,6 +430,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
398
430
  })
399
431
  }, [
400
432
  config,
433
+ axisLayoutConfig,
401
434
  data,
402
435
  yScale,
403
436
  effectiveYTickValues,
@@ -651,8 +684,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
651
684
  onMouseMove={onMouseMove}
652
685
  width={parentWidth}
653
686
  height={isNoDataAvailable ? 1 : calculatedSvgHeight ?? parentHeight}
654
- className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'
655
- } ${isDraggingAnnotation && 'dragging-annotation'}`}
687
+ className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${
688
+ debugSvg && 'debug'
689
+ } ${isDraggingAnnotation && 'dragging-annotation'}`}
656
690
  role='img'
657
691
  aria-label={a11y}
658
692
  style={{ overflow: 'visible' }}
@@ -695,6 +729,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
695
729
  })}
696
730
  </Group>
697
731
  )}
732
+ <Group left={yAxisWidth} className='region-fill-layer'>
733
+ <RegionFills layouts={regionLayout.layouts} yMax={yMax} />
734
+ </Group>
698
735
  {visualizationType === 'Paired Bar' && (
699
736
  <PairedBarAxis
700
737
  g1xScale={g1xScale}
@@ -737,33 +774,33 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
737
774
  />
738
775
  {/* Brush moved to separate overlay - no longer in main SVG */}
739
776
  {/* y anchors */}
740
- {config.yAxis.anchors &&
741
- config.yAxis.anchors.map((anchor, index) => {
742
- let position = yScale(anchor.value)
743
- let middleOffset = 0
744
-
745
- if (!anchor.value) return
777
+ <ValueAxisAnchors
778
+ anchors={config.yAxis.anchors}
779
+ className='anchor-y'
780
+ getOffset={() => {
781
+ if (orientation !== 'horizontal' || visualizationType !== 'Bar') return 0
746
782
  if (config.yAxis.labelPlacement === 'Below Bar') {
747
- middleOffset =
748
- BELOW_BAR_TEXT_OFFSET + Number(config.series.length * config.barHeight) / config.series.length
749
- } else {
750
- middleOffset = LABEL_PADDING_OFFSET
783
+ return BELOW_BAR_TEXT_OFFSET + Number(config.series.length * config.barHeight) / config.series.length
751
784
  }
752
-
753
- if (!position) return
754
-
755
- return (
756
- // prettier-ignore
757
- <Line
758
- key={`yAxis-${anchor.value}--${index}`}
759
- strokeDasharray={handleLineType(anchor.lineStyle)}
760
- stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
761
- className='anchor-y'
762
- from={{ x: Number(yAxisWidth), y: position - middleOffset }}
763
- to={{ x: Number(yAxisWidth) + Number(xMax), y: position - middleOffset }}
764
- />
765
- )
766
- })}
785
+ return LABEL_PADDING_OFFSET
786
+ }}
787
+ handleLineType={handleLineType}
788
+ keyPrefix='yAxis'
789
+ xStart={Number(yAxisWidth)}
790
+ xEnd={Number(yAxisWidth) + Number(xMax)}
791
+ yScale={yScale}
792
+ />
793
+ {hasRightAxis && (
794
+ <ValueAxisAnchors
795
+ anchors={config.yAxis.rightAnchors}
796
+ className='anchor-y-right'
797
+ handleLineType={handleLineType}
798
+ keyPrefix='yAxisRight'
799
+ xStart={Number(yAxisWidth)}
800
+ xEnd={Number(yAxisWidth) + Number(xMax)}
801
+ yScale={yScaleRight}
802
+ />
803
+ )}
767
804
  {/* x anchors */}
768
805
  {config.xAxis.anchors &&
769
806
  config.xAxis.anchors.map((anchor, index) => {
@@ -787,6 +824,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
787
824
  let anchorPosition = getAnchorPosition()
788
825
 
789
826
  if (!anchorPosition) return
827
+ const anchorXPosition = alignStrokeToPixel(Number(anchorPosition) + Number(yAxisWidth))
790
828
 
791
829
  return (
792
830
  // prettier-ignore
@@ -796,28 +834,18 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
796
834
  stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
797
835
  fill={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
798
836
  className='anchor-x'
799
- from={{ x: Number(anchorPosition) + Number(yAxisWidth), y: 0 }}
800
- to={{ x: Number(anchorPosition) + Number(yAxisWidth), y: yMax }}
837
+ from={{ x: anchorXPosition, y: 0 }}
838
+ to={{ x: anchorXPosition, y: yMax }}
801
839
  />
802
840
  )
803
841
  })}
804
- {/* we are handling regions in bar charts differently, so that we can calculate the bar group into the region space. */}
805
- {/* prettier-ignore */}
806
- {config.visualizationType !== 'Bar' && config.visualizationType !== 'Combo' && (
807
- <Group left={yAxisWidth}>
808
- <Regions
809
- xScale={xScale}
810
- handleTooltipClick={handleTooltipClick}
811
- handleTooltipMouseOff={handleTooltipMouseOff}
812
- handleTooltipMouseOver={handleTooltipMouseOver}
813
- showTooltip={showTooltip}
814
- hideTooltip={hideTooltip}
815
- tooltipData={tooltipData}
816
- yMax={yMax}
817
- xMax={xMax}
818
- />
819
- </Group>
820
- )}
842
+ <Group left={yAxisWidth} className='region-label-layer'>
843
+ <RegionLabels
844
+ layouts={regionLayout.layouts}
845
+ regionLabelFontSize={regionLayout.regionLabelFontSize}
846
+ isMobileViewport={regionLayout.isMobileViewport}
847
+ />
848
+ </Group>
821
849
  {isNoDataAvailable && (
822
850
  <Text
823
851
  x={yAxisWidth + Number(xMax / 2)}
@@ -889,6 +917,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
889
917
  xMax={xMax}
890
918
  yMax={yMax}
891
919
  leftSize={yAxisWidth - config.yAxis.axisPadding}
920
+ categories={renderedYAxisCategories}
892
921
  />
893
922
  )}
894
923
  {/* Right Axis */}
@@ -898,6 +927,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
898
927
  yMax={yMax}
899
928
  xMax={xMax}
900
929
  yAxisWidth={yAxisWidth}
930
+ rightAxisWidth={rightAxisWidth}
901
931
  tickValues={rightTickValues}
902
932
  tickLabelFontSize={tickLabelFontSize}
903
933
  axisLabelFontSize={axisLabelFontSize}
@@ -950,8 +980,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
950
980
  tooltipData.dataXPosition &&
951
981
  !config.tooltips.singleSeries && (
952
982
  <>
953
- <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100
954
- }) !important;`}</style>
983
+ <style>{`.tooltip {background-color: rgba(255,255,255, ${
984
+ config.tooltips.opacity / 100
985
+ }) !important;`}</style>
955
986
  <style>{`.tooltip {max-width:300px} !important; word-wrap: break-word; `}</style>
956
987
  <TooltipWithBounds
957
988
  ref={tooltipRef}