@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
@@ -1,18 +1,11 @@
1
- import { USE_V2_MIGRATION } from '@cdc/core/helpers/constants'
1
+ import { DEFAULT_RACE_SECONDS_PER_FRAME } from '../components/raceTiming'
2
2
 
3
- // Dynamic initial state based on migration flag
4
3
  const createInitialState = () => {
5
- const paletteDefaults = USE_V2_MIGRATION
6
- ? {
7
- isReversed: true,
8
- version: '2.0',
9
- name: 'sequential_bluereverse'
10
- }
11
- : {
12
- isReversed: true,
13
- version: '1.0',
14
- name: 'qualitative_bold'
15
- }
4
+ const paletteDefaults = {
5
+ isReversed: false,
6
+ version: '2.1',
7
+ name: 'sequential_blue'
8
+ }
16
9
 
17
10
  return {
18
11
  annotations: [],
@@ -26,11 +19,19 @@ const createInitialState = () => {
26
19
  titleStyle: 'small',
27
20
  showTitle: true,
28
21
  showDownloadMediaButton: false,
22
+ enableTooltips: false,
29
23
  theme: 'theme-blue',
30
24
  animate: false,
31
25
  lineDatapointStyle: 'hover',
32
26
  lineDatapointColor: 'Same as Line',
33
27
  barHasBorder: 'true',
28
+ barRace: {
29
+ maxBars: 10,
30
+ secondsPerFrame: DEFAULT_RACE_SECONDS_PER_FRAME
31
+ },
32
+ lineRace: {
33
+ secondsPerFrame: DEFAULT_RACE_SECONDS_PER_FRAME
34
+ },
34
35
  isLollipopChart: false,
35
36
  lollipopShape: 'circle',
36
37
  lollipopColorStyle: 'two-tone',
@@ -58,6 +59,7 @@ const createInitialState = () => {
58
59
  displayNumbersOnBar: false,
59
60
  hideLabel: false,
60
61
  hideTicks: true,
62
+ labelPlacement: 'On Date/Category Axis',
61
63
  size: 50,
62
64
  gridLines: true,
63
65
  enablePadding: false,
@@ -70,6 +72,7 @@ const createInitialState = () => {
70
72
  rightAxisSize: 0,
71
73
  rightLabel: '',
72
74
  rightLabelOffsetSize: 0,
75
+ rightTitlePlacement: 'top',
73
76
  rightAxisLabelColor: '#1c1d1f',
74
77
  rightAxisTickLabelColor: '#1c1d1f',
75
78
  rightAxisTickColor: '#1c1d1f',
@@ -80,6 +83,7 @@ const createInitialState = () => {
80
83
  tickRotation: 0,
81
84
  autoMaxStrategy: 'clean-top-tick',
82
85
  anchors: [],
86
+ rightAnchors: [],
83
87
  shoMissingDataLabel: true,
84
88
  showMissingDataLine: true,
85
89
  categories: []
@@ -112,8 +116,8 @@ const createInitialState = () => {
112
116
  },
113
117
 
114
118
  isLegendValue: false,
115
- barThickness: 0.35,
116
119
  barHeight: 25,
120
+ barThickness: 0.8,
117
121
  barSpace: 15,
118
122
  heights: {
119
123
  vertical: 300,
@@ -149,6 +153,7 @@ const createInitialState = () => {
149
153
  brushActive: false,
150
154
  brushDefaultRecentDateCount: undefined,
151
155
  brushDynamicYAxis: false,
156
+ brushHideHatching: false,
152
157
  viewportNumTicks: {
153
158
  xs: 4,
154
159
  xxs: 4
@@ -162,10 +167,11 @@ const createInitialState = () => {
162
167
  caption: '',
163
168
  showDownloadUrl: false,
164
169
  downloadUrlLabel: '',
165
- showDataTableLink: true,
170
+ showDataTableLink: false,
166
171
  showDownloadLinkBelow: true,
167
172
  search: false,
168
173
  searchPlaceholder: '',
174
+ stickyFirstColumn: false,
169
175
  indexLabel: '',
170
176
  download: false,
171
177
  showVertical: true,
@@ -175,7 +181,6 @@ const createInitialState = () => {
175
181
  collapsible: true
176
182
  },
177
183
  orientation: 'vertical',
178
- color: 'qualitative-bold',
179
184
  columns: {
180
185
  // start with a blank list
181
186
  },
@@ -315,10 +320,12 @@ const createInitialState = () => {
315
320
  },
316
321
  heatmap: {
317
322
  cellPadding: 1,
318
- rowLabelGap: 32,
319
- columnLabelGap: 56,
323
+ rowLabelGap: 0,
324
+ columnLabelGap: 20,
320
325
  xAxisPosition: 'top',
321
- showCellValues: false
326
+ showCellValues: false,
327
+ horizontalScroll: false,
328
+ minColumnWidth: 44
322
329
  },
323
330
  area: {
324
331
  isStacked: false
@@ -336,22 +343,20 @@ const createInitialState = () => {
336
343
  axisLabelOffset: 15
337
344
  },
338
345
  sankey: {
339
- title: {
340
- defaultColor: 'black'
341
- },
342
346
  iterations: 1,
343
347
  rxValue: 0.9,
344
- overallSize: {
345
- width: 900,
346
- height: 700
347
- },
348
+ horizontalScrollWidth: 900,
348
349
  margin: {
349
350
  margin_y: 25,
350
351
  margin_x: 0
351
352
  },
353
+ columns: {
354
+ source: 'source',
355
+ target: 'target',
356
+ value: 'value'
357
+ },
352
358
  nodeSize: {
353
- nodeWidth: 26,
354
- nodeHeight: 40
359
+ nodeWidth: 26
355
360
  },
356
361
  nodePadding: 55,
357
362
  nodeFontColor: 'black',
@@ -369,13 +374,54 @@ const createInitialState = () => {
369
374
  LinkOpacityDefault: 1.0,
370
375
  LinkOpacityInactive: 0.1
371
376
  },
372
- storyNodeFontColor: '#006778',
373
- storyNodeText: [],
374
377
  nodeValueStyle: {
375
378
  textBefore: '(',
376
379
  textAfter: ')'
380
+ }
381
+ },
382
+ network: {
383
+ columns: {
384
+ source: 'source',
385
+ target: 'target',
386
+ weight: '',
387
+ style: '',
388
+ nodeColor: ''
389
+ },
390
+ directed: false,
391
+ height: 500,
392
+ nodeRadius: 8,
393
+ nodeColor: '#005eaa',
394
+ linkColor: '#333333',
395
+ linkStyleLabels: {
396
+ solid: 'Solid',
397
+ dashed: 'Dashed'
398
+ },
399
+ linkDistance: 90,
400
+ chargeStrength: -240,
401
+ rotation: 0,
402
+ scale: 1,
403
+ offsetX: 0,
404
+ offsetY: 0
405
+ },
406
+ dendrogram: {
407
+ columns: {
408
+ node: 'node',
409
+ parent: 'parent',
410
+ style: 'linkStyle',
411
+ nodeColor: 'nodeColor'
412
+ },
413
+ connectionType: 'curve',
414
+ orientation: 'horizontal',
415
+ height: 500,
416
+ nodeRadius: 6,
417
+ nodeColor: '#005eaa',
418
+ linkColor: '#333333',
419
+ linkStyleLabels: {
420
+ solid: 'Solid',
421
+ dashed: 'Dashed'
377
422
  },
378
- data: []
423
+ leafSpacing: 40,
424
+ depthSpacing: 140
379
425
  },
380
426
  markupVariables: [],
381
427
  enableMarkupVariables: false
@@ -0,0 +1,71 @@
1
+ import type { ChartConfig } from '../types/ChartConfig'
2
+ import cloneDeep from 'lodash/cloneDeep'
3
+
4
+ /** Applies deterministic chart-type fields owned by current authoring paths. */
5
+ export const applyChartTypeContract = <T extends ChartConfig>(config: T): T => {
6
+ const nextConfig = cloneDeep(config)
7
+
8
+ if (Array.isArray(nextConfig.series)) {
9
+ nextConfig.series = nextConfig.series.map(series => ({ tooltip: true, axis: 'Left', ...series }))
10
+ }
11
+
12
+ switch (nextConfig.visualizationType) {
13
+ case 'Area Chart':
14
+ nextConfig.visualizationSubType = 'stacked'
15
+ break
16
+ case 'Forecasting':
17
+ if (!nextConfig.xAxis.type || nextConfig.xAxis.type === 'categorical') {
18
+ nextConfig.xAxis.type = 'date'
19
+ if (!nextConfig.xAxis.dateParseFormat) nextConfig.xAxis.dateParseFormat = '%Y-%m-%d'
20
+ if (!nextConfig.xAxis.dateDisplayFormat) nextConfig.xAxis.dateDisplayFormat = '%Y-%m-%d'
21
+ }
22
+ break
23
+ case 'HeatMap':
24
+ nextConfig.yAxis.type = 'categorical'
25
+ nextConfig.yAxis.titlePlacement = nextConfig.yAxis.titlePlacement || 'side'
26
+ nextConfig.legend.position = nextConfig.legend.position || 'top'
27
+ nextConfig.legend.style = nextConfig.legend.style || 'gradient'
28
+ nextConfig.legend.subStyle =
29
+ nextConfig.legend.subStyle === 'smooth' ? 'linear blocks' : nextConfig.legend.subStyle || 'linear blocks'
30
+ break
31
+ case 'Horizon Chart': {
32
+ nextConfig.horizon = {
33
+ numLayers: 4,
34
+ mode: 'offset',
35
+ bandGap: 15,
36
+ bottomPadding: 15,
37
+ ...nextConfig.horizon
38
+ }
39
+ if (!nextConfig.xAxis.type) nextConfig.xAxis.type = 'categorical'
40
+ const layerCount = nextConfig.horizon.numLayers ?? 4
41
+ const paletteCount = nextConfig.general.paletteColorCount ?? 4
42
+ nextConfig.general.paletteColorCount = Math.max(paletteCount, layerCount)
43
+ break
44
+ }
45
+ case 'Box Plot':
46
+ nextConfig.yAxis.labelPlacement = 'On Date/Category Axis'
47
+ break
48
+ case 'Paired Bar':
49
+ case 'Deviation Bar':
50
+ nextConfig.orientation = 'horizontal'
51
+ break
52
+ case 'Bump Chart':
53
+ nextConfig.xAxis.type = 'date-time'
54
+ break
55
+ }
56
+
57
+ if (
58
+ ['date', 'date-time'].includes(nextConfig.xAxis?.type) &&
59
+ nextConfig.xAxis?.dataKey &&
60
+ !nextConfig.table?.defaultSort?.column
61
+ ) {
62
+ nextConfig.table = {
63
+ ...nextConfig.table,
64
+ defaultSort: { column: nextConfig.xAxis.dataKey, sortDirection: 'desc' }
65
+ }
66
+ }
67
+
68
+ return nextConfig
69
+ }
70
+
71
+ export default applyChartTypeContract
@@ -1,8 +1,8 @@
1
1
  import { isMobileFontViewport } from '@cdc/core/helpers/viewports'
2
- import { ViewportSize } from '../types/ChartConfig'
2
+ import type { ViewPort } from '@cdc/core/types/ViewPort'
3
3
 
4
4
  export const AXIS_LABEL_FONT_SIZE = 18
5
5
  export const AXIS_LABEL_FONT_SIZE_SMALL = 14
6
6
 
7
- export const getAxisLabelFontSize = (vizViewport: ViewportSize) =>
7
+ export const getAxisLabelFontSize = (vizViewport: ViewPort) =>
8
8
  isMobileFontViewport(vizViewport) ? AXIS_LABEL_FONT_SIZE_SMALL : AXIS_LABEL_FONT_SIZE
@@ -0,0 +1,60 @@
1
+ import * as d3 from 'd3-array'
2
+ import { sortByNumber, toSortableNumber } from '@cdc/core/helpers/sorting'
3
+
4
+ export type BoxPlotStats = {
5
+ deviation: number | undefined
6
+ iqr: number
7
+ lowerBound: number
8
+ mean: number | undefined
9
+ median: number | undefined
10
+ nonOutliers: number[]
11
+ outliers: number[]
12
+ q1: number
13
+ q3: number
14
+ upperBound: number
15
+ values: number[]
16
+ whiskerMax: number
17
+ whiskerMin: number
18
+ }
19
+
20
+ export const getSortedBoxPlotValues = (values: unknown[] = []): number[] => {
21
+ const numericValues = values.reduce<number[]>((result, value) => {
22
+ if (value === null || value === undefined || value === '') return result
23
+
24
+ const numericValue = toSortableNumber(value)
25
+ if (Number.isFinite(numericValue)) result.push(numericValue)
26
+
27
+ return result
28
+ }, [])
29
+
30
+ return sortByNumber(numericValues)
31
+ }
32
+
33
+ export const calculateBoxPlotStats = (values: unknown[] = []): BoxPlotStats | null => {
34
+ const sortedValues = getSortedBoxPlotValues(values)
35
+ if (!sortedValues.length) return null
36
+
37
+ const q1 = d3.quantile(sortedValues, 0.25) ?? 0
38
+ const q3 = d3.quantile(sortedValues, 0.75) ?? 0
39
+ const iqr = q3 - q1
40
+ const lowerBound = q1 - 1.5 * iqr
41
+ const upperBound = q3 + 1.5 * iqr
42
+ const nonOutliers = sortedValues.filter(value => value >= lowerBound && value <= upperBound)
43
+ const outliers = sortedValues.filter(value => value < lowerBound || value > upperBound)
44
+
45
+ return {
46
+ deviation: d3.deviation(sortedValues),
47
+ iqr,
48
+ lowerBound,
49
+ mean: d3.mean(sortedValues),
50
+ median: d3.median(sortedValues),
51
+ nonOutliers,
52
+ outliers,
53
+ q1,
54
+ q3,
55
+ upperBound,
56
+ values: sortedValues,
57
+ whiskerMax: nonOutliers[nonOutliers.length - 1] ?? q3,
58
+ whiskerMin: nonOutliers[0] ?? q1
59
+ }
60
+ }
@@ -2,6 +2,7 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
2
2
  import { buildTooltipListHtml, getTooltipSeriesMarker } from './tooltipHelpers'
3
3
  import { type ChartConfig } from '../types/ChartConfig'
4
4
  import { type ColorScale } from '../types/ChartContext'
5
+ import { getSeriesValueLabel } from '@cdc/core/helpers/getSeriesName'
5
6
 
6
7
  type TooltipDateHelpers = {
7
8
  formatDate: (date: Date) => string
@@ -35,7 +36,7 @@ export const buildBumpChartTooltipHtml = ({ config, colorScale, dataRow, series,
35
36
  const xAxisLabel = config.runtime?.xAxis?.label || config.xAxis.dataKey
36
37
  const heading = `${xAxisLabel ? `${xAxisLabel}: ` : ''}${formattedXAxisValue ?? ''}`
37
38
 
38
- const seriesName = series.name || config.runtime?.seriesLabels?.[series.dataKey] || series.dataKey
39
+ const seriesName = getSeriesValueLabel(series.dataKey, { ...config, series: [series] })
39
40
  const marker = getTooltipSeriesMarker(config, colorScale, series.dataKey)
40
41
  const bodyRows = [
41
42
  {
@@ -0,0 +1,37 @@
1
+ import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
3
+ import { calculateHorizontalBarCategoryLabelWidth } from './calculateHorizontalBarCategoryLabelWidth'
4
+
5
+ vi.mock('@cdc/core/helpers/getTextWidth', () => ({
6
+ getTextWidth: vi.fn()
7
+ }))
8
+
9
+ const mockedGetTextWidth = vi.mocked(getTextWidth)
10
+
11
+ const calculateWidth = (categoryValues: string[], chartWidth = 500) =>
12
+ calculateHorizontalBarCategoryLabelWidth({
13
+ yScale: { domain: () => categoryValues },
14
+ chartWidth,
15
+ formatDate: vi.fn(),
16
+ parseDate: vi.fn(),
17
+ tickLabelFont: 'normal 16px Nunito, sans-serif',
18
+ labelPlacement: 'On Date/Category Axis'
19
+ })
20
+
21
+ describe('calculateHorizontalBarCategoryLabelWidth', () => {
22
+ beforeEach(() => {
23
+ mockedGetTextWidth.mockImplementation(label => label.length * 10)
24
+ })
25
+
26
+ it('keeps at least ten pixels between short labels and the chart area', () => {
27
+ expect(calculateWidth(['A', 'B'])).toBe(20)
28
+ })
29
+
30
+ it('retains proportional padding for longer labels', () => {
31
+ expect(calculateWidth(['abcdefghij'])).toBe(115)
32
+ })
33
+
34
+ it('still caps category label space at thirty percent of the chart width', () => {
35
+ expect(calculateWidth(['abcdefghij'], 300)).toBe(90)
36
+ })
37
+ })
@@ -47,8 +47,9 @@ export const calculateHorizontalBarCategoryLabelWidth = ({
47
47
  const labelWidths = formattedLabels.map(label => getTextWidth(label, tickLabelFont))
48
48
  const maxLabelWidth = Math.max(...labelWidths)
49
49
 
50
- // We need some extra padding or visx will wrap labels too early
51
- const paddedWidth = maxLabelWidth + Math.ceil(maxLabelWidth * 0.15)
50
+ // We need some extra padding or visx will wrap labels too early. Keep a
51
+ // minimum gap so short labels do not sit too close to the chart area.
52
+ const paddedWidth = maxLabelWidth + Math.max(10, Math.ceil(maxLabelWidth * 0.15))
52
53
 
53
54
  // Allocate at most 30% of chart width to category labels
54
55
  const maxAllowedWidth = chartWidth * 0.3
@@ -0,0 +1,60 @@
1
+ import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
2
+ import { DEFAULT_TICK_LENGTH, TICK_LABEL_MARGIN_RIGHT } from '../components/Axis/axis.constants'
3
+
4
+ type CalculateRightYAxisWidthProps = {
5
+ axisLabelFontSize: number
6
+ config: any
7
+ formatNumber: (value: any, axis?: string) => string
8
+ tickLabelFont: string
9
+ tickValues?: number[]
10
+ yScaleRight: any
11
+ }
12
+
13
+ const AXIS_LABEL_PADDING = 5
14
+
15
+ const getRightTickValues = ({ config, tickValues, yScaleRight }) => {
16
+ if (tickValues) return tickValues
17
+
18
+ if (typeof yScaleRight?.ticks === 'function') {
19
+ return yScaleRight.ticks(config.runtime?.yAxis?.rightNumTicks || undefined)
20
+ }
21
+
22
+ if (typeof yScaleRight?.domain === 'function') {
23
+ return yScaleRight.domain()
24
+ }
25
+
26
+ return []
27
+ }
28
+
29
+ const measureWidestLabel = (labels: string[], tickLabelFont: string) => {
30
+ return labels.length ? Math.max(...labels.map(label => getTextWidth(label, tickLabelFont))) : 0
31
+ }
32
+
33
+ const getSideTitleSpace = ({ config, axisLabelFontSize, tickContentSpace }) => {
34
+ const rightTitle = config.runtime?.yAxis?.rightLabel ?? config.yAxis?.rightLabel
35
+
36
+ if (config.yAxis?.rightTitlePlacement === 'top' || !rightTitle) return 0
37
+
38
+ return tickContentSpace + axisLabelFontSize + AXIS_LABEL_PADDING
39
+ }
40
+
41
+ export const calculateRightYAxisWidth = ({
42
+ axisLabelFontSize,
43
+ config,
44
+ formatNumber,
45
+ tickLabelFont,
46
+ tickValues,
47
+ yScaleRight
48
+ }: CalculateRightYAxisWidthProps): number => {
49
+ const measuredTickValues = getRightTickValues({ config, tickValues, yScaleRight })
50
+ const formattedTicks = measuredTickValues.map(tick => String(formatNumber(tick, 'right') ?? ''))
51
+ const tickLabelWidth = config.yAxis?.rightHideLabel ? 0 : measureWidestLabel(formattedTicks, tickLabelFont)
52
+ const tickLabelSpace = tickLabelWidth ? tickLabelWidth + DEFAULT_TICK_LENGTH + TICK_LABEL_MARGIN_RIGHT : 0
53
+ const tickMarkSpace = config.runtime?.yAxis?.rightHideTicks ? 0 : DEFAULT_TICK_LENGTH
54
+ const tickContentSpace = Math.max(tickLabelSpace, tickMarkSpace)
55
+ const sideTitleSpace = getSideTitleSpace({ config, axisLabelFontSize, tickContentSpace })
56
+
57
+ return Math.ceil(Math.max(tickContentSpace, sideTitleSpace))
58
+ }
59
+
60
+ export default calculateRightYAxisWidth
@@ -1,4 +1,5 @@
1
1
  import { type ChartConfig, type VisualizationType } from '../types/ChartConfig'
2
+ import { getSeriesName } from '@cdc/core/helpers/getSeriesName'
2
3
 
3
4
  export type SeriesColorAssignment = {
4
5
  key: string
@@ -45,8 +46,7 @@ export const hasSeriesColorAssignmentOverrides = (config: ChartConfig): boolean
45
46
  }
46
47
 
47
48
  export const getSeriesDisplayLabel = (config: ChartConfig, key: string): string => {
48
- const series = config.series?.find(item => item?.dataKey === key)
49
- return String(config.runtime?.seriesLabels?.[key] || series?.name || (series as any)?.label || key)
49
+ return getSeriesName(key, config)
50
50
  }
51
51
 
52
52
  export const getSeriesColorAssignmentItems = (config: ChartConfig): SeriesColorAssignmentItem[] => {
@@ -0,0 +1,56 @@
1
+ import cloneDeep from 'lodash/cloneDeep'
2
+ import mergeWith from 'lodash/mergeWith'
3
+
4
+ import defaults from '../data/initial-state'
5
+ import { ChartConfig } from '../types/ChartConfig'
6
+ import { getChartTypeDefaultPalette } from './getChartTypeDefaultPalette'
7
+ import { CURRENT_COVE_CONFIG_VERSION } from '@cdc/core/helpers/coveUpdateWorker'
8
+ import { applyChartTypeContract } from './applyChartTypeContract'
9
+
10
+ type CreateNewChartConfigOptions = {
11
+ isDashboard?: boolean
12
+ }
13
+
14
+ /**
15
+ * Creates a complete config for a brand-new chart using current defaults.
16
+ * Existing and legacy configs must continue through the normal migration and
17
+ * backfill paths instead of using this helper.
18
+ */
19
+ export const createNewChartConfig = (
20
+ starterConfig: Partial<ChartConfig> & Record<string, any>,
21
+ { isDashboard = false }: CreateNewChartConfigOptions = {}
22
+ ): ChartConfig => {
23
+ const currentDefaults = cloneDeep(defaults)
24
+ const defaultPalette = getChartTypeDefaultPalette(starterConfig.visualizationType)
25
+
26
+ if (defaultPalette && !starterConfig.general?.palette) {
27
+ currentDefaults.general.palette = cloneDeep(defaultPalette)
28
+ }
29
+
30
+ const config = applyChartTypeContract(
31
+ mergeWith(currentDefaults, cloneDeep(starterConfig), (_defaultValue, starterValue) =>
32
+ Array.isArray(starterValue) ? starterValue : undefined
33
+ ) as ChartConfig
34
+ )
35
+
36
+ if (config.orientation === 'horizontal') {
37
+ config.xAxis.hideAxis = starterConfig.xAxis?.hideAxis ?? true
38
+ config.xAxis.hideTicks = starterConfig.xAxis?.hideTicks ?? true
39
+ config.yAxis.hideAxis = starterConfig.yAxis?.hideAxis ?? false
40
+ config.yAxis.hideTicks = starterConfig.yAxis?.hideTicks ?? false
41
+ }
42
+
43
+ config.table.show = starterConfig.table?.show ?? !isDashboard
44
+ config.table.download = starterConfig.table?.download ?? !isDashboard
45
+ config.legend.unified = starterConfig.legend?.unified ?? true
46
+ config.legend.expandForecastConfidenceIntervals = starterConfig.legend?.expandForecastConfidenceIntervals ?? true
47
+
48
+ if (starterConfig.visualizationType === 'HeatMap') {
49
+ config.xAxis.manual = starterConfig.xAxis?.manual ?? false
50
+ config.xAxis.numTicks = starterConfig.xAxis?.numTicks
51
+ config.xAxis.viewportNumTicks = starterConfig.xAxis?.viewportNumTicks ?? {}
52
+ }
53
+
54
+ config.version = CURRENT_COVE_CONFIG_VERSION
55
+ return config
56
+ }
@@ -0,0 +1,18 @@
1
+ import { AllChartsConfig } from '../types/ChartConfig'
2
+ import { getDynamicYAxisCategories } from './getDynamicYAxisCategories'
3
+
4
+ type DataDrivenYAxisConfig = Pick<AllChartsConfig, 'yAxis'>
5
+
6
+ export const isDataDrivenYAxis = (config: DataDrivenYAxisConfig) =>
7
+ config.yAxis.type === 'categorical' &&
8
+ (config.yAxis.categoryMode === 'data-driven' ||
9
+ (config.yAxis.categoryMode === undefined && !!config.yAxis.dataDrivenCategories))
10
+
11
+ export const getDataDrivenYAxisCategories = (config: DataDrivenYAxisConfig, data: Record<string, any>[]) => {
12
+ if (!isDataDrivenYAxis(config) || !config.yAxis.dataDrivenCategories) return null
13
+
14
+ return getDynamicYAxisCategories({
15
+ config: config.yAxis.dataDrivenCategories,
16
+ data
17
+ })
18
+ }
@@ -0,0 +1,88 @@
1
+ export type ForecastConfidenceInterval = {
2
+ high?: string | number
3
+ low?: string | number
4
+ }
5
+
6
+ export type ForecastConfidenceIntervalGroup = {
7
+ intervalIndexes: number[]
8
+ key: string
9
+ label: string
10
+ opacity: number
11
+ }
12
+
13
+ const FORECAST_CI_KEY_PREFIX = 'forecast-ci:'
14
+
15
+ const getSharedPercentage = ({ high, low }: ForecastConfidenceInterval): number | undefined => {
16
+ const highMatches = String(high).match(/\d+(?:\.\d+)?/g)
17
+ const lowMatches = String(low).match(/\d+(?:\.\d+)?/g)
18
+
19
+ if (highMatches?.length !== 1 || lowMatches?.length !== 1) return
20
+
21
+ const highPercentage = Number(highMatches[0])
22
+ const lowPercentage = Number(lowMatches[0])
23
+
24
+ return highPercentage === lowPercentage && highPercentage >= 0 && highPercentage <= 100 ? highPercentage : undefined
25
+ }
26
+
27
+ export const getForecastConfidenceIntervalGroups = (
28
+ confidenceIntervals: ForecastConfidenceInterval[] = []
29
+ ): ForecastConfidenceIntervalGroup[] => {
30
+ const completeIntervals = confidenceIntervals
31
+ .map((interval, configIndex) => ({ interval, configIndex, percentage: getSharedPercentage(interval) }))
32
+ .filter(({ interval }) => String(interval.high ?? '').trim() && String(interval.low ?? '').trim())
33
+
34
+ if (completeIntervals.length <= 1) return []
35
+
36
+ const orderedIntervals = completeIntervals.sort((a, b) => {
37
+ if (a.percentage !== undefined && b.percentage !== undefined) return a.percentage - b.percentage
38
+ if (a.percentage !== undefined) return -1
39
+ if (b.percentage !== undefined) return 1
40
+ return a.configIndex - b.configIndex
41
+ })
42
+ const groups: ForecastConfidenceIntervalGroup[] = []
43
+ const percentageGroups = new Map<number, ForecastConfidenceIntervalGroup>()
44
+ let fallbackIndex = 0
45
+
46
+ orderedIntervals.forEach((interval, displayIndex) => {
47
+ if (interval.percentage !== undefined) {
48
+ const existingGroup = percentageGroups.get(interval.percentage)
49
+ if (existingGroup) {
50
+ existingGroup.intervalIndexes.push(interval.configIndex)
51
+ return
52
+ }
53
+ }
54
+
55
+ const fallbackNumber = interval.percentage === undefined ? ++fallbackIndex : undefined
56
+ const group = {
57
+ intervalIndexes: [interval.configIndex],
58
+ key: interval.percentage !== undefined ? `percentage:${interval.percentage}` : `interval:${interval.configIndex}`,
59
+ label: interval.percentage !== undefined ? `${interval.percentage}% CI` : `CI ${fallbackNumber}`,
60
+ opacity: 1 - Math.pow(1 - 0.5, completeIntervals.length - displayIndex)
61
+ }
62
+
63
+ groups.push(group)
64
+ if (interval.percentage !== undefined) percentageGroups.set(interval.percentage, group)
65
+ })
66
+
67
+ return groups
68
+ }
69
+
70
+ export const getForecastLegendKey = (stageKey: string, intervalKey: string): string =>
71
+ `${FORECAST_CI_KEY_PREFIX}${encodeURIComponent(stageKey)}:${encodeURIComponent(intervalKey)}`
72
+
73
+ export const getForecastConfidenceIntervalKey = (
74
+ confidenceIntervals: ForecastConfidenceInterval[] = [],
75
+ intervalIndex: number
76
+ ): string | undefined =>
77
+ getForecastConfidenceIntervalGroups(confidenceIntervals).find(group => group.intervalIndexes.includes(intervalIndex))
78
+ ?.key
79
+
80
+ export const isForecastLegendSelectionActive = (
81
+ seriesHighlight: string[],
82
+ stageKey: string,
83
+ intervalKey?: string
84
+ ): boolean => {
85
+ if (!seriesHighlight.length) return true
86
+ if (seriesHighlight.includes(stageKey)) return true
87
+ return intervalKey ? seriesHighlight.includes(getForecastLegendKey(stageKey, intervalKey)) : false
88
+ }