@cdc/chart 4.25.11 → 4.26.2

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 (181) hide show
  1. package/CLAUDE.local.md +79 -0
  2. package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
  3. package/dist/cdcchart.js +51401 -50814
  4. package/examples/default.json +378 -0
  5. package/examples/feature/__data__/horizon-chart-data.json +373 -0
  6. package/examples/feature/annotations/index.json +3 -6
  7. package/examples/feature/horizon/horizon-chart.json +395 -0
  8. package/examples/feature/pie/planet-pie-example-config.json +48 -2
  9. package/examples/line-chart-states.json +1085 -0
  10. package/examples/private/123.json +694 -0
  11. package/examples/private/DEV-12100.json +1303 -0
  12. package/examples/private/anchor-issue.json +4094 -0
  13. package/examples/private/backwards-slider.json +10430 -0
  14. package/examples/private/cat-y.json +1235 -0
  15. package/examples/private/data-points.json +228 -0
  16. package/examples/private/georgia.csv +160 -0
  17. package/examples/private/height.json +3915 -0
  18. package/examples/private/links.json +569 -0
  19. package/examples/private/quadrant.txt +30 -0
  20. package/examples/private/test-forecast.json +5510 -0
  21. package/examples/private/timeline-data.json +1 -0
  22. package/examples/private/timeline.json +389 -0
  23. package/examples/private/warming-stripe-test.json +2578 -0
  24. package/examples/private/warming-stripes.json +4763 -0
  25. package/examples/radar-chart-simple.json +133 -0
  26. package/examples/radar-chart.json +148 -0
  27. package/examples/tech-adoption-with-links.json +560 -0
  28. package/index.html +1 -36
  29. package/package.json +59 -60
  30. package/src/CdcChartComponent.tsx +206 -89
  31. package/src/_stories/Chart.Anchors.stories.tsx +10 -0
  32. package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
  33. package/src/_stories/Chart.CI.stories.tsx +13 -0
  34. package/src/_stories/Chart.Combo.stories.tsx +17 -0
  35. package/src/_stories/Chart.CustomColors.stories.tsx +4 -0
  36. package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
  37. package/src/_stories/Chart.Filters.stories.tsx +4 -0
  38. package/src/_stories/Chart.Forecast.stories.tsx +4 -0
  39. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
  40. package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
  41. package/src/_stories/Chart.Patterns.stories.tsx +4 -0
  42. package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
  43. package/src/_stories/Chart.Regions.Categorical.stories.tsx +161 -0
  44. package/src/_stories/Chart.Regions.DateScale.stories.tsx +216 -0
  45. package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +312 -0
  46. package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
  47. package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
  48. package/src/_stories/Chart.stories.tsx +45 -0
  49. package/src/_stories/Chart.tooltip.stories.tsx +7 -0
  50. package/src/_stories/ChartAnnotation.stories.tsx +10 -0
  51. package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
  52. package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
  53. package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
  54. package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
  55. package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
  56. package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
  57. package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
  58. package/src/_stories/ChartBrush.stories.tsx +57 -0
  59. package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
  60. package/src/_stories/ChartEditor.stories.tsx +7 -0
  61. package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
  62. package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
  63. package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
  64. package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
  65. package/src/_stories/TechAdoptionWithLinks.stories.tsx +34 -0
  66. package/src/_stories/_mock/brush_continuous.json +86 -0
  67. package/src/_stories/_mock/brush_date_large.json +176 -0
  68. package/src/_stories/_mock/brush_enabled.json +326 -0
  69. package/src/_stories/_mock/brush_mock.json +2 -69
  70. package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
  71. package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
  72. package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
  73. package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
  74. package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
  75. package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
  76. package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
  77. package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
  78. package/src/components/Annotations/components/AnnotationDraggable.styles.css +11 -17
  79. package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
  80. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
  81. package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
  82. package/src/components/Annotations/components/AnnotationList.styles.css +4 -10
  83. package/src/components/Annotations/components/AnnotationList.tsx +5 -4
  84. package/src/components/Annotations/components/findNearestDatum.ts +75 -85
  85. package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
  86. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
  87. package/src/components/Axis/BottomAxis.tsx +270 -0
  88. package/src/components/Axis/Categorical.Axis.tsx +6 -7
  89. package/src/components/Axis/LeftAxis.tsx +404 -0
  90. package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
  91. package/src/components/Axis/PairedBarAxis.tsx +186 -0
  92. package/src/components/Axis/README.md +94 -0
  93. package/src/components/Axis/RightAxis.tsx +108 -0
  94. package/src/components/Axis/axis.constants.ts +21 -0
  95. package/src/components/Axis/index.ts +7 -0
  96. package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
  97. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
  98. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
  99. package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
  100. package/src/components/BarChart/components/BarChart.tsx +7 -1
  101. package/src/components/BarChart/components/context.tsx +1 -0
  102. package/src/components/BarChart/helpers/useBarChart.ts +14 -2
  103. package/src/components/Brush/BrushSelector.tsx +1390 -0
  104. package/src/components/Brush/MiniChartPreview.tsx +400 -0
  105. package/src/components/DeviationBar.jsx +9 -7
  106. package/src/components/EditorPanel/EditorPanel.tsx +2734 -2595
  107. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +60 -22
  108. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
  109. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +137 -30
  110. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
  111. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
  112. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +0 -1
  113. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
  114. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +42 -28
  115. package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
  116. package/src/components/EditorPanel/useEditorPermissions.ts +81 -39
  117. package/src/components/HorizonChart/HorizonChart.tsx +131 -0
  118. package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
  119. package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
  120. package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
  121. package/src/components/HorizonChart/index.tsx +3 -0
  122. package/src/components/Legend/Legend.Component.tsx +52 -4
  123. package/src/components/Legend/Legend.tsx +4 -3
  124. package/src/components/Legend/LegendValueRange.tsx +77 -0
  125. package/src/components/Legend/helpers/createFormatLabels.tsx +164 -2
  126. package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
  127. package/src/components/Legend/helpers/index.ts +10 -6
  128. package/src/components/LineChart/helpers/README.md +292 -0
  129. package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
  130. package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
  131. package/src/components/LineChart/index.tsx +44 -8
  132. package/src/components/LinearChart/README.md +109 -0
  133. package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
  134. package/src/components/LinearChart/linearChart.constants.ts +84 -0
  135. package/src/components/LinearChart/tests/LinearChart.test.tsx +201 -0
  136. package/src/components/LinearChart/tests/mockConfigContext.ts +129 -0
  137. package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
  138. package/src/components/LinearChart.tsx +338 -1082
  139. package/src/components/PairedBarChart.jsx +20 -3
  140. package/src/components/PieChart/PieChart.tsx +1 -1
  141. package/src/components/RadarChart/RadarAxis.tsx +78 -0
  142. package/src/components/RadarChart/RadarChart.tsx +298 -0
  143. package/src/components/RadarChart/RadarGrid.tsx +64 -0
  144. package/src/components/RadarChart/RadarPolygon.tsx +91 -0
  145. package/src/components/RadarChart/helpers.ts +83 -0
  146. package/src/components/RadarChart/index.tsx +3 -0
  147. package/src/components/Regions/components/Regions.tsx +365 -122
  148. package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
  149. package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
  150. package/src/components/WarmingStripes/WarmingStripes.tsx +230 -0
  151. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
  152. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
  153. package/src/components/WarmingStripes/index.tsx +3 -0
  154. package/src/data/initial-state.js +17 -2
  155. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
  156. package/src/helpers/getExcludedData.ts +4 -0
  157. package/src/helpers/getMinMax.ts +12 -7
  158. package/src/helpers/handleChartAriaLabels.ts +19 -19
  159. package/src/helpers/handleLineType.ts +22 -18
  160. package/src/helpers/sizeHelpers.ts +0 -20
  161. package/src/helpers/smallMultiplesHelpers.ts +1 -1
  162. package/src/hooks/useChartHoverAnalytics.tsx +10 -9
  163. package/src/hooks/useProgrammaticTooltip.ts +23 -2
  164. package/src/hooks/useScales.ts +18 -1
  165. package/src/hooks/useTooltip.tsx +34 -10
  166. package/src/scss/DataTable.scss +0 -4
  167. package/src/scss/main.scss +22 -3
  168. package/src/selectors/README.md +68 -0
  169. package/src/store/chart.reducer.ts +2 -0
  170. package/src/test/CdcChart.test.jsx +1 -1
  171. package/src/types/ChartConfig.ts +21 -0
  172. package/src/types/ChartContext.ts +1 -0
  173. package/src/types/Horizon.ts +64 -0
  174. package/src/types/Label.ts +1 -0
  175. package/src/utils/analyticsTracking.ts +19 -0
  176. package/LICENSE +0 -201
  177. package/src/components/Annotations/components/helpers/index.tsx +0 -46
  178. package/src/components/Brush/BrushChart.tsx +0 -128
  179. package/src/components/Brush/BrushController.tsx +0 -71
  180. package/src/components/Brush/types.tsx +0 -8
  181. package/src/components/BrushChart.tsx +0 -223
@@ -0,0 +1,400 @@
1
+ import React, { memo } from 'react'
2
+ import { LinePath, AreaClosed, AreaStack } from '@visx/shape'
3
+ import * as allCurves from '@visx/curve'
4
+ import { handleLineType } from '../../helpers/handleLineType'
5
+ import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
6
+
7
+ interface MiniChartPreviewProps {
8
+ series: any[]
9
+ tableData: any[]
10
+ dataKey: string
11
+ xScale: any
12
+ miniYScale: any
13
+ miniRightYScale?: any
14
+ colorScale: any
15
+ config: any
16
+ xMax: number
17
+ }
18
+
19
+ const MiniChartPreview = memo<MiniChartPreviewProps>(
20
+ ({ series, tableData, dataKey, xScale, miniYScale, miniRightYScale, colorScale, config }) => {
21
+ if (!series || !series.length || !tableData || !tableData.length || !xScale || !miniYScale) {
22
+ return null
23
+ }
24
+
25
+ const bandwidth = xScale.bandwidth?.() || 0
26
+ const isComboChart = config?.visualizationType === 'Combo'
27
+ const isBarChart = config?.visualizationType === 'Bar'
28
+ const isStacked = config?.visualizationSubType === 'stacked'
29
+ const isAreaChart = config?.visualizationType === 'Area Chart'
30
+ const barSeriesTypes = new Set(['Bar', 'Paired Bar', 'Deviation Bar', 'Combo'])
31
+ const barSeries = isComboChart ? series.filter(s => barSeriesTypes.has(s.type)) : series
32
+ const areaSeries = isComboChart ? series.filter(s => s.type === 'Area Chart') : series
33
+ const lineSeries = isComboChart
34
+ ? series.filter(s => !barSeriesTypes.has(s.type) && s.type !== 'Area Chart')
35
+ : series
36
+
37
+ let barElements: React.ReactElement[] = []
38
+
39
+ // For bar charts, render rectangles
40
+ if (isBarChart || (isComboChart && barSeries.length > 0)) {
41
+ const bars: React.ReactElement[] = []
42
+ const barSeriesToRender = isBarChart ? series : barSeries
43
+
44
+ const barStrokeColor = config?.barHasBorder === 'true' ? '#000' : 'transparent'
45
+ const barStrokeWidth = config?.barHasBorder === 'true' ? 1 : 0
46
+
47
+ const getPatternUrl = (datum, seriesKey: string, value: string | number) => {
48
+ if (!config.legend?.patterns || Object.keys(config.legend.patterns).length === 0) {
49
+ return null
50
+ }
51
+
52
+ for (const [patternKey, patternObj] of Object.entries(config.legend.patterns)) {
53
+ const pattern = patternObj as any
54
+ if (pattern.dataKey && pattern.dataValue) {
55
+ if (pattern.dataKey === seriesKey && String(value) === String(pattern.dataValue)) {
56
+ return `url(#chart-pattern-${patternKey})`
57
+ }
58
+
59
+ if (!config.runtime?.seriesLabels || !config.runtime.seriesLabels[pattern.dataKey]) {
60
+ const dataFieldValue = datum[pattern.dataKey]
61
+ if (String(dataFieldValue) === String(pattern.dataValue)) {
62
+ return `url(#chart-pattern-${patternKey})`
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ return null
69
+ }
70
+
71
+ tableData.forEach((d, i) => {
72
+ const xVal = xScale(d[dataKey])
73
+ if (xVal === undefined || isNaN(xVal)) {
74
+ return
75
+ }
76
+
77
+ // Calculate bar position and width
78
+ // For band scales, center the bar in the band
79
+ const x = bandwidth > 0 ? xVal + bandwidth / 2 : xVal
80
+ const barWidth = bandwidth > 0 ? Math.max(bandwidth * 0.85, 2) : 4 // 85% of bandwidth for wider bars
81
+
82
+ if (isStacked) {
83
+ // For stacked bars, render each series as a segment
84
+ // Calculate cumulative values starting from bottom (0)
85
+ let cumulativeValue = 0
86
+ const zeroY = miniYScale(0)
87
+
88
+ barSeriesToRender.forEach((s, seriesIndex) => {
89
+ const value = parseFloat(d[s.dataKey])
90
+ if (isNaN(value) || value === 0) return
91
+
92
+ const seriesColor = colorScale?.(config.runtime.seriesLabels?.[s.dataKey] || s.dataKey) || '#666'
93
+ const patternUrl = getPatternUrl(d, s.dataKey, value)
94
+
95
+ // Calculate the bottom and top of this segment
96
+ // For stacked bars, each segment sits on top of the previous one
97
+ const bottomY = miniYScale(cumulativeValue)
98
+ cumulativeValue += value
99
+ const topY = miniYScale(cumulativeValue)
100
+ const barHeight = Math.abs(topY - bottomY)
101
+ const y = Math.min(bottomY, topY)
102
+
103
+ // Ensure minimum visible height
104
+ if (barHeight < 0.5) return
105
+
106
+ bars.push(
107
+ <rect
108
+ key={`mini-bar-stacked-${i}-${seriesIndex}`}
109
+ x={x - barWidth / 2}
110
+ y={y}
111
+ width={barWidth}
112
+ height={barHeight}
113
+ fill={seriesColor}
114
+ stroke={barStrokeColor}
115
+ strokeWidth={barStrokeWidth}
116
+ fillOpacity={1}
117
+ pointerEvents='none'
118
+ />
119
+ )
120
+ if (patternUrl) {
121
+ bars.push(
122
+ <rect
123
+ key={`mini-bar-stacked-pattern-${i}-${seriesIndex}`}
124
+ x={x - barWidth / 2}
125
+ y={y}
126
+ width={barWidth}
127
+ height={barHeight}
128
+ fill={patternUrl}
129
+ stroke='transparent'
130
+ strokeWidth={0}
131
+ fillOpacity={1}
132
+ pointerEvents='none'
133
+ />
134
+ )
135
+ }
136
+ })
137
+ } else {
138
+ // For grouped bars, render bars side by side
139
+ const seriesCount = barSeriesToRender.length
140
+ const groupBarWidth = barWidth / seriesCount
141
+ const zeroY = miniYScale(0)
142
+
143
+ barSeriesToRender.forEach((s, seriesIndex) => {
144
+ const value = parseFloat(d[s.dataKey])
145
+ if (isNaN(value)) return
146
+
147
+ const seriesColor = colorScale?.(config.runtime.seriesLabels?.[s.dataKey] || s.dataKey) || '#666'
148
+ const patternUrl = getPatternUrl(d, s.dataKey, value)
149
+
150
+ // Calculate bar position and height
151
+ const valueY = miniYScale(value)
152
+ const barHeight = Math.abs(valueY - zeroY)
153
+ const barX = x - barWidth / 2 + seriesIndex * groupBarWidth
154
+ const y = Math.min(valueY, zeroY) // Top of bar (smaller Y value)
155
+
156
+ bars.push(
157
+ <rect
158
+ key={`mini-bar-grouped-${i}-${seriesIndex}`}
159
+ x={barX}
160
+ y={y}
161
+ width={groupBarWidth}
162
+ height={barHeight}
163
+ fill={seriesColor}
164
+ stroke={barStrokeColor}
165
+ strokeWidth={barStrokeWidth}
166
+ fillOpacity={1}
167
+ pointerEvents='none'
168
+ />
169
+ )
170
+ if (patternUrl) {
171
+ bars.push(
172
+ <rect
173
+ key={`mini-bar-grouped-pattern-${i}-${seriesIndex}`}
174
+ x={barX}
175
+ y={y}
176
+ width={groupBarWidth}
177
+ height={barHeight}
178
+ fill={patternUrl}
179
+ stroke='transparent'
180
+ strokeWidth={0}
181
+ fillOpacity={1}
182
+ pointerEvents='none'
183
+ />
184
+ )
185
+ }
186
+ })
187
+ }
188
+ })
189
+
190
+ barElements = bars
191
+ if (isBarChart && !isComboChart) {
192
+ return <>{barElements}</>
193
+ }
194
+ }
195
+
196
+ // For stacked area charts, use AreaStack
197
+ // Note: If only one series, treat as non-stacked area chart
198
+ if (isAreaChart && isStacked) {
199
+ // Use the same keys order as the main chart
200
+ const seriesKeys =
201
+ config.runtime?.areaSeriesKeys?.map(s => s.dataKey) || series.map(s => s.dataKey).filter(Boolean)
202
+
203
+ if (seriesKeys.length === 0) {
204
+ return null
205
+ }
206
+
207
+ // If only one series, render as regular area chart instead of stacked
208
+ if (seriesKeys.length === 1) {
209
+ const s = series[0]
210
+ const seriesKey = s.dataKey
211
+ const seriesColor = colorScale?.(config.runtime.seriesLabels?.[seriesKey] || seriesKey) || '#666'
212
+ const curveType = config.stackedAreaChartLineType
213
+ ? allCurves[approvedCurveTypes[config.stackedAreaChartLineType]]
214
+ : allCurves.curveLinear
215
+
216
+ const validData = tableData.filter(d => {
217
+ const xVal = xScale(d[dataKey])
218
+ const yVal = parseFloat(d[s.dataKey])
219
+ return xVal !== undefined && !isNaN(yVal)
220
+ })
221
+
222
+ if (validData.length === 0) return null
223
+
224
+ const getX = d => xScale(d[dataKey]) + bandwidth / 2
225
+ const getY = d => miniYScale(parseFloat(d[s.dataKey]))
226
+
227
+ return (
228
+ <AreaClosed
229
+ key={`mini-area-single-${seriesKey}`}
230
+ data={validData}
231
+ x={getX}
232
+ y={getY}
233
+ yScale={miniYScale}
234
+ fill={seriesColor}
235
+ fillOpacity={1}
236
+ stroke={seriesColor}
237
+ strokeWidth={2}
238
+ curve={curveType}
239
+ pointerEvents='none'
240
+ />
241
+ )
242
+ }
243
+
244
+ const curveType = config.stackedAreaChartLineType
245
+ ? allCurves[approvedCurveTypes[config.stackedAreaChartLineType]]
246
+ : allCurves.curveLinear
247
+
248
+ // Filter data to only include valid entries
249
+ const validData = tableData.filter(d => {
250
+ const xVal = xScale(d[dataKey])
251
+ return xVal !== undefined
252
+ })
253
+
254
+ if (validData.length === 0) {
255
+ return null
256
+ }
257
+
258
+ return (
259
+ <AreaStack
260
+ data={validData}
261
+ keys={seriesKeys}
262
+ x0={d => {
263
+ // AreaStack transforms data, so d has a 'data' property with the original data point
264
+ const xVal = xScale(d.data[dataKey])
265
+ return xVal !== undefined ? xVal + bandwidth / 2 : 0
266
+ }}
267
+ y0={d => Number(miniYScale(d[0]))}
268
+ y1={d => Number(miniYScale(d[1]))}
269
+ curve={curveType}
270
+ >
271
+ {({ stacks, path }) => {
272
+ if (!stacks || stacks.length === 0) {
273
+ return null
274
+ }
275
+ // Don't reverse - render in the same order as main chart
276
+ // The main chart doesn't reverse, so we should match that
277
+ return stacks.map(stack => {
278
+ const seriesKey = stack.key
279
+ const seriesColor = colorScale?.(config.runtime.seriesLabels?.[seriesKey] || seriesKey) || '#666'
280
+ const pathData = path(stack)
281
+ if (!pathData) return null
282
+ return (
283
+ <path
284
+ key={`mini-area-stacked-${seriesKey}`}
285
+ d={pathData}
286
+ fill={seriesColor}
287
+ fillOpacity={1}
288
+ stroke={seriesColor}
289
+ strokeWidth={2}
290
+ pointerEvents='none'
291
+ />
292
+ )
293
+ })
294
+ }}
295
+ </AreaStack>
296
+ )
297
+ }
298
+
299
+ // For line/area charts (and combo line/area series), render lines or areas
300
+ return (
301
+ <>
302
+ {(isComboChart ? areaSeries : isAreaChart ? series : []).map((s, i) => {
303
+ const seriesKey = s.dataKey
304
+ const seriesColor = colorScale?.(config.runtime.seriesLabels?.[seriesKey] || seriesKey) || '#666'
305
+
306
+ // Get series-specific styling
307
+ const seriesWeight = s.weight || 2
308
+ const seriesLineType = s.lineType || 'curveLinear'
309
+ const seriesStyle = s.type || 'solid'
310
+ const curve = allCurves[seriesLineType] || allCurves.curveLinear
311
+ const strokeDasharray = handleLineType(seriesStyle)
312
+
313
+ // Use the right-axis scale when the series is on the right axis
314
+ const yScaleForSeries = s.axis === 'Right' && miniRightYScale ? miniRightYScale : miniYScale
315
+
316
+ // Filter to only valid data points, then sort by X position so the area
317
+ // renders left-to-right regardless of the original data order.
318
+ const validData = tableData
319
+ .filter(d => {
320
+ const xVal = xScale(d[dataKey])
321
+ const yVal = parseFloat(d[s.dataKey])
322
+ return xVal !== undefined && !isNaN(yVal)
323
+ })
324
+ .sort((a, b) => (xScale(a[dataKey]) ?? 0) - (xScale(b[dataKey]) ?? 0))
325
+
326
+ if (validData.length === 0) return null
327
+
328
+ const getX = d => xScale(d[dataKey]) + bandwidth / 2
329
+ const getY = d => yScaleForSeries(parseFloat(d[s.dataKey]))
330
+
331
+ return (
332
+ <AreaClosed
333
+ key={`mini-area-${seriesKey}-${i}`}
334
+ data={validData}
335
+ x={getX}
336
+ y={getY}
337
+ yScale={yScaleForSeries}
338
+ fill={seriesColor}
339
+ fillOpacity={1}
340
+ stroke={seriesColor}
341
+ strokeWidth={seriesWeight}
342
+ strokeDasharray={strokeDasharray}
343
+ curve={curve}
344
+ pointerEvents='none'
345
+ />
346
+ )
347
+ })}
348
+ {barElements}
349
+ {(isAreaChart && !isComboChart ? [] : isComboChart ? lineSeries : series).map((s, i) => {
350
+ const seriesKey = s.dataKey
351
+ const seriesColor = colorScale?.(config.runtime.seriesLabels?.[seriesKey] || seriesKey) || '#666'
352
+
353
+ // Get series-specific styling
354
+ const seriesWeight = s.weight || 2
355
+ const seriesLineType = s.lineType || 'curveLinear'
356
+ const seriesStyle = s.type || 'solid'
357
+ const curve = allCurves[seriesLineType] || allCurves.curveLinear
358
+ const strokeDasharray = handleLineType(seriesStyle)
359
+
360
+ // Use the right-axis scale when the series is on the right axis
361
+ const yScaleForSeries = s.axis === 'Right' && miniRightYScale ? miniRightYScale : miniYScale
362
+
363
+ // Filter to only valid data points, then sort by X position so the line
364
+ // connects left-to-right regardless of the original data order.
365
+ const validData = tableData
366
+ .filter(d => {
367
+ const xVal = xScale(d[dataKey])
368
+ const yVal = parseFloat(d[s.dataKey])
369
+ return xVal !== undefined && !isNaN(yVal)
370
+ })
371
+ .sort((a, b) => (xScale(a[dataKey]) ?? 0) - (xScale(b[dataKey]) ?? 0))
372
+
373
+ if (validData.length === 0) return null
374
+
375
+ const getX = d => xScale(d[dataKey]) + bandwidth / 2
376
+ const getY = d => yScaleForSeries(parseFloat(d[s.dataKey]))
377
+
378
+ return (
379
+ <LinePath
380
+ key={`mini-line-${seriesKey}-${i}`}
381
+ data={validData}
382
+ x={getX}
383
+ y={getY}
384
+ stroke={seriesColor}
385
+ strokeWidth={seriesWeight}
386
+ strokeDasharray={strokeDasharray}
387
+ strokeOpacity={0.8}
388
+ curve={curve}
389
+ pointerEvents='none'
390
+ />
391
+ )
392
+ })}
393
+ </>
394
+ )
395
+ }
396
+ )
397
+
398
+ MiniChartPreview.displayName = 'MiniChartPreview'
399
+
400
+ export default MiniChartPreview
@@ -9,6 +9,7 @@ import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
9
9
  import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
10
10
  import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
11
11
  import { isV1Palette } from '@cdc/core/helpers/palettes/utils'
12
+ import { isMobileFontViewport } from '@cdc/core/helpers/viewports'
12
13
 
13
14
  export default function DeviationBar({ height, xScale }) {
14
15
  const {
@@ -18,19 +19,20 @@ export default function DeviationBar({ height, xScale }) {
18
19
  twoColorPalette,
19
20
  parseDate,
20
21
  formatDate,
21
- currentViewport
22
+ vizViewport
22
23
  } = useContext(ConfigContext)
23
24
  const { barStyle, tipRounding, roundingStyle, twoColor } = config
25
+ const labelFontSize = isMobileFontViewport(vizViewport) ? 13 : 16
24
26
  const barRefs = useRef([])
25
27
  const [windowWidth, setWindowWidth] = useState(window.innerWidth)
26
28
  const radius =
27
29
  roundingStyle === 'standard'
28
30
  ? '8px'
29
31
  : roundingStyle === 'shallow'
30
- ? '5px'
31
- : roundingStyle === 'finger'
32
- ? '15px'
33
- : '0px'
32
+ ? '5px'
33
+ : roundingStyle === 'finger'
34
+ ? '15px'
35
+ : '0px'
34
36
  const isRounded = config.barStyle === 'rounded'
35
37
  const target = Number(config.xAxis.target)
36
38
  const seriesKey = config.series[0].dataKey
@@ -165,7 +167,7 @@ export default function DeviationBar({ height, xScale }) {
165
167
  config.heights.horizontal = totalheight
166
168
 
167
169
  // text,labels postiions
168
- const textWidth = getTextWidth(formatNumber(barValue, 'left'), `normal ${16}px sans-serif`)
170
+ const textWidth = getTextWidth(formatNumber(barValue, 'left'), `normal ${labelFontSize}px sans-serif`)
169
171
  const textFits = textWidth < barWidth - 6
170
172
  const textX = barBaseX
171
173
  const textY = barY + barHeight / 2
@@ -223,7 +225,7 @@ export default function DeviationBar({ height, xScale }) {
223
225
  ></div>
224
226
  </foreignObject>
225
227
  {config.yAxis.displayNumbersOnBar && (
226
- <Text verticalAnchor='middle' x={textX} y={textY} {...textProps[barPosition]}>
228
+ <Text verticalAnchor='middle' x={textX} y={textY} {...textProps[barPosition]} fontSize={labelFontSize}>
227
229
  {formatNumber(d[seriesKey], 'left')}
228
230
  </Text>
229
231
  )}