@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
@@ -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({
@@ -287,9 +298,19 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
287
298
  // State for computed left-axis width - shared across all linear-chart types.
288
299
  const [currentYAxisWidth, setCurrentYAxisWidth] = useState<number>(DEFAULT_LEFT_Y_AXIS_WIDTH)
289
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
290
311
 
291
312
  // Chart width calculation using the current y-axis width
292
- const xMax = Math.max(0, parentWidth - yAxisWidth - (hasRightAxis ? config.yAxis.rightAxisSize : 0))
313
+ const xMax = Math.max(0, parentWidth - yAxisWidth - rightAxisWidth)
293
314
 
294
315
  // Stabilize brush container dimensions when brushDynamicYAxis is enabled.
295
316
  // Without this, y-axis rescaling on each brush change creates a feedback loop:
@@ -336,6 +357,16 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
336
357
  })
337
358
  const effectiveYTickValues = sharedYAxisTickValues ?? yTickValues
338
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
+
339
370
  // Consolidated tick formatters
340
371
  const { handleLeftTickFormatting, handleBottomTickFormatting } = useTickFormatters({
341
372
  isLogarithmicAxis,
@@ -385,7 +416,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
385
416
  }
386
417
 
387
418
  return calculateLeftYAxisWidth({
388
- config,
419
+ config: axisLayoutConfig,
389
420
  data,
390
421
  yScale,
391
422
  numTicks: handleNumTicks,
@@ -399,6 +430,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
399
430
  })
400
431
  }, [
401
432
  config,
433
+ axisLayoutConfig,
402
434
  data,
403
435
  yScale,
404
436
  effectiveYTickValues,
@@ -652,8 +684,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
652
684
  onMouseMove={onMouseMove}
653
685
  width={parentWidth}
654
686
  height={isNoDataAvailable ? 1 : calculatedSvgHeight ?? parentHeight}
655
- className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${debugSvg && 'debug'
656
- } ${isDraggingAnnotation && 'dragging-annotation'}`}
687
+ className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${
688
+ debugSvg && 'debug'
689
+ } ${isDraggingAnnotation && 'dragging-annotation'}`}
657
690
  role='img'
658
691
  aria-label={a11y}
659
692
  style={{ overflow: 'visible' }}
@@ -696,6 +729,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
696
729
  })}
697
730
  </Group>
698
731
  )}
732
+ <Group left={yAxisWidth} className='region-fill-layer'>
733
+ <RegionFills layouts={regionLayout.layouts} yMax={yMax} />
734
+ </Group>
699
735
  {visualizationType === 'Paired Bar' && (
700
736
  <PairedBarAxis
701
737
  g1xScale={g1xScale}
@@ -738,33 +774,33 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
738
774
  />
739
775
  {/* Brush moved to separate overlay - no longer in main SVG */}
740
776
  {/* y anchors */}
741
- {config.yAxis.anchors &&
742
- config.yAxis.anchors.map((anchor, index) => {
743
- let position = yScale(anchor.value)
744
- let middleOffset = 0
745
-
746
- 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
747
782
  if (config.yAxis.labelPlacement === 'Below Bar') {
748
- middleOffset =
749
- BELOW_BAR_TEXT_OFFSET + Number(config.series.length * config.barHeight) / config.series.length
750
- } else {
751
- middleOffset = LABEL_PADDING_OFFSET
783
+ return BELOW_BAR_TEXT_OFFSET + Number(config.series.length * config.barHeight) / config.series.length
752
784
  }
753
-
754
- if (!position) return
755
-
756
- return (
757
- // prettier-ignore
758
- <Line
759
- key={`yAxis-${anchor.value}--${index}`}
760
- strokeDasharray={handleLineType(anchor.lineStyle)}
761
- stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
762
- className='anchor-y'
763
- from={{ x: Number(yAxisWidth), y: position - middleOffset }}
764
- to={{ x: Number(yAxisWidth) + Number(xMax), y: position - middleOffset }}
765
- />
766
- )
767
- })}
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
+ )}
768
804
  {/* x anchors */}
769
805
  {config.xAxis.anchors &&
770
806
  config.xAxis.anchors.map((anchor, index) => {
@@ -788,6 +824,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
788
824
  let anchorPosition = getAnchorPosition()
789
825
 
790
826
  if (!anchorPosition) return
827
+ const anchorXPosition = alignStrokeToPixel(Number(anchorPosition) + Number(yAxisWidth))
791
828
 
792
829
  return (
793
830
  // prettier-ignore
@@ -797,28 +834,18 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
797
834
  stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
798
835
  fill={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
799
836
  className='anchor-x'
800
- from={{ x: Number(anchorPosition) + Number(yAxisWidth), y: 0 }}
801
- to={{ x: Number(anchorPosition) + Number(yAxisWidth), y: yMax }}
837
+ from={{ x: anchorXPosition, y: 0 }}
838
+ to={{ x: anchorXPosition, y: yMax }}
802
839
  />
803
840
  )
804
841
  })}
805
- {/* we are handling regions in bar charts differently, so that we can calculate the bar group into the region space. */}
806
- {/* prettier-ignore */}
807
- {config.visualizationType !== 'Bar' && config.visualizationType !== 'Combo' && (
808
- <Group left={yAxisWidth}>
809
- <Regions
810
- xScale={xScale}
811
- handleTooltipClick={handleTooltipClick}
812
- handleTooltipMouseOff={handleTooltipMouseOff}
813
- handleTooltipMouseOver={handleTooltipMouseOver}
814
- showTooltip={showTooltip}
815
- hideTooltip={hideTooltip}
816
- tooltipData={tooltipData}
817
- yMax={yMax}
818
- xMax={xMax}
819
- />
820
- </Group>
821
- )}
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>
822
849
  {isNoDataAvailable && (
823
850
  <Text
824
851
  x={yAxisWidth + Number(xMax / 2)}
@@ -890,6 +917,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
890
917
  xMax={xMax}
891
918
  yMax={yMax}
892
919
  leftSize={yAxisWidth - config.yAxis.axisPadding}
920
+ categories={renderedYAxisCategories}
893
921
  />
894
922
  )}
895
923
  {/* Right Axis */}
@@ -899,6 +927,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
899
927
  yMax={yMax}
900
928
  xMax={xMax}
901
929
  yAxisWidth={yAxisWidth}
930
+ rightAxisWidth={rightAxisWidth}
902
931
  tickValues={rightTickValues}
903
932
  tickLabelFontSize={tickLabelFontSize}
904
933
  axisLabelFontSize={axisLabelFontSize}
@@ -951,8 +980,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
951
980
  tooltipData.dataXPosition &&
952
981
  !config.tooltips.singleSeries && (
953
982
  <>
954
- <style>{`.tooltip {background-color: rgba(255,255,255, ${config.tooltips.opacity / 100
955
- }) !important;`}</style>
983
+ <style>{`.tooltip {background-color: rgba(255,255,255, ${
984
+ config.tooltips.opacity / 100
985
+ }) !important;`}</style>
956
986
  <style>{`.tooltip {max-width:300px} !important; word-wrap: break-word; `}</style>
957
987
  <TooltipWithBounds
958
988
  ref={tooltipRef}