@cdc/chart 4.25.8 → 4.25.11

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 (145) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/dist/{cdcchart-1a1724a1.es.js → cdcchart-dgT_1dIT.es.js} +136 -151
  3. package/dist/cdcchart.js +44236 -40355
  4. package/examples/feature/__data__/planet-example-data.json +0 -30
  5. package/examples/feature/boxplot/valid-boxplot.csv +38 -17
  6. package/examples/grouped-bar-test.json +400 -0
  7. package/examples/private/DEV-11825.json +573 -0
  8. package/examples/private/d.json +382 -0
  9. package/examples/private/example-2.json +49784 -0
  10. package/examples/private/f2.json +1 -0
  11. package/examples/private/f4.json +1577 -0
  12. package/examples/private/forecast.json +1180 -0
  13. package/examples/private/lollipop.json +468 -0
  14. package/examples/private/na.json +913 -0
  15. package/examples/private/new.json +48756 -0
  16. package/examples/private/pie-chart-legend.json +904 -0
  17. package/examples/private/test-data.csv +28 -0
  18. package/examples/suppressed_tooltip.json +480 -0
  19. package/index.html +2 -133
  20. package/package.json +25 -7
  21. package/src/CdcChart.tsx +9 -13
  22. package/src/CdcChartComponent.tsx +403 -92
  23. package/src/_stories/Chart.Anchors.stories.tsx +2 -2
  24. package/src/_stories/Chart.BoxPlot.stories.tsx +1 -1
  25. package/src/_stories/Chart.CI.stories.tsx +1 -1
  26. package/src/_stories/Chart.Combo.stories.tsx +18 -0
  27. package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
  28. package/src/_stories/Chart.DynamicSeries.stories.tsx +2 -2
  29. package/src/_stories/Chart.Filters.stories.tsx +2 -2
  30. package/src/_stories/Chart.Forecast.stories.tsx +36 -0
  31. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +520 -0
  32. package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
  33. package/src/_stories/Chart.Patterns.stories.tsx +20 -0
  34. package/src/_stories/Chart.PreserveDecimals.stories.tsx +220 -0
  35. package/src/_stories/Chart.ScatterPlot.stories.tsx +1 -1
  36. package/src/_stories/Chart.SmallMultiples.stories.tsx +47 -0
  37. package/src/_stories/Chart.stories.tsx +8 -5
  38. package/src/_stories/Chart.tooltip.stories.tsx +1 -1
  39. package/src/_stories/ChartAnnotation.stories.tsx +7 -4
  40. package/src/_stories/ChartAxisLabels.stories.tsx +2 -2
  41. package/src/_stories/ChartAxisTitles.stories.tsx +2 -2
  42. package/src/_stories/ChartBar.Editor.stories.tsx +3580 -0
  43. package/src/_stories/ChartEditor.Editor.stories.tsx +658 -0
  44. package/src/_stories/ChartEditor.stories.tsx +59 -60
  45. package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
  46. package/src/_stories/ChartLine.Symbols.stories.tsx +1 -1
  47. package/src/_stories/ChartPrefixSuffix.stories.tsx +2 -2
  48. package/src/_stories/_mock/combo.json +451 -0
  49. package/src/_stories/_mock/editor-test-configs.json +376 -0
  50. package/src/_stories/_mock/editor-test-datasets.json +477 -0
  51. package/src/_stories/_mock/editor-tests/bar-chart-editor-test.json +255 -0
  52. package/src/_stories/_mock/editor-tests/bar-chart-general-test.json +267 -0
  53. package/src/_stories/_mock/editor-tests/bar-chart-test.json +237 -0
  54. package/src/_stories/_mock/forecast_combo_with_gaps.json +913 -0
  55. package/src/_stories/_mock/pie_config.json +257 -62
  56. package/src/_stories/_mock/small_multiples/small_multiples_bars.json +1944 -0
  57. package/src/_stories/_mock/small_multiples/small_multiples_big_data_bars.json +1114 -0
  58. package/src/_stories/_mock/small_multiples/small_multiples_lines.json +2646 -0
  59. package/src/_stories/_mock/small_multiples/small_multiples_lines_colors.json +1305 -0
  60. package/src/_stories/_mock/small_multiples/small_multiples_stacked_bars.json +1936 -0
  61. package/src/_stories/_mock/stacked-pattern-test.json +520 -0
  62. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -0
  63. package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
  64. package/src/components/Annotations/components/findNearestDatum.ts +6 -41
  65. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -6
  66. package/src/components/AreaChart/index.tsx +1 -2
  67. package/src/components/BarChart/components/BarChart.Horizontal.tsx +161 -22
  68. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +138 -5
  69. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +215 -73
  70. package/src/components/BarChart/components/BarChart.Vertical.tsx +155 -22
  71. package/src/components/BarChart/helpers/index.ts +43 -4
  72. package/src/components/BarChart/helpers/lollipopColors.ts +27 -0
  73. package/src/components/BarChart/helpers/useBarChart.ts +25 -3
  74. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +2 -1
  75. package/src/components/BoxPlot/helpers/index.ts +3 -3
  76. package/src/components/Brush/BrushChart.tsx +1 -1
  77. package/src/components/DeviationBar.jsx +9 -6
  78. package/src/components/EditorPanel/EditorPanel.tsx +563 -229
  79. package/src/components/EditorPanel/EditorPanelContext.ts +3 -0
  80. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +96 -111
  81. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +19 -1
  82. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +461 -0
  83. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +80 -67
  84. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +422 -0
  85. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +188 -139
  86. package/src/components/EditorPanel/components/Panels/index.tsx +5 -1
  87. package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +0 -8
  88. package/src/components/EditorPanel/editor-panel.scss +0 -20
  89. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +49 -48
  90. package/src/components/EditorPanel/useEditorPermissions.ts +7 -15
  91. package/src/components/Forecasting/Forecasting.tsx +175 -27
  92. package/src/components/ForestPlot/ForestPlot.tsx +11 -7
  93. package/src/components/ForestPlot/ForestPlotProps.ts +1 -1
  94. package/src/components/Legend/Legend.Component.tsx +114 -14
  95. package/src/components/Legend/helpers/createFormatLabels.tsx +230 -171
  96. package/src/components/Legend/helpers/getLegendClasses.ts +0 -1
  97. package/src/components/LegendWrapper.tsx +1 -1
  98. package/src/components/LineChart/LineChartProps.ts +0 -3
  99. package/src/components/LineChart/components/LineChart.Circle.tsx +2 -2
  100. package/src/components/LineChart/helpers.ts +1 -1
  101. package/src/components/LineChart/index.tsx +38 -15
  102. package/src/components/LinearChart.tsx +96 -84
  103. package/src/components/PairedBarChart.jsx +6 -4
  104. package/src/components/PieChart/PieChart.tsx +170 -54
  105. package/src/components/Regions/components/Regions.tsx +3 -24
  106. package/src/components/Sankey/components/Sankey.tsx +7 -1
  107. package/src/components/Sankey/types/index.ts +1 -1
  108. package/src/components/ScatterPlot/ScatterPlot.jsx +32 -4
  109. package/src/components/SmallMultiples/SmallMultipleTile.tsx +198 -0
  110. package/src/components/SmallMultiples/SmallMultiples.css +32 -0
  111. package/src/components/SmallMultiples/SmallMultiples.tsx +271 -0
  112. package/src/components/SmallMultiples/index.ts +2 -0
  113. package/src/data/initial-state.js +327 -293
  114. package/src/helpers/buildForecastPaletteMappings.ts +112 -0
  115. package/src/helpers/buildForecastPaletteOptions.ts +71 -0
  116. package/src/helpers/getColorScale.ts +82 -8
  117. package/src/{hooks/useMinMax.ts → helpers/getMinMax.ts} +14 -7
  118. package/src/helpers/getNewRuntime.ts +1 -1
  119. package/src/helpers/getTransformedData.ts +1 -1
  120. package/src/helpers/getYAxisAutoPadding.ts +53 -0
  121. package/src/helpers/smallMultiplesHelpers.ts +529 -0
  122. package/src/hooks/useChartHoverAnalytics.tsx +44 -0
  123. package/src/hooks/useProgrammaticTooltip.ts +96 -0
  124. package/src/hooks/useReduceData.ts +105 -70
  125. package/src/hooks/useScales.ts +88 -34
  126. package/src/hooks/useSmallMultipleSynchronization.ts +59 -0
  127. package/src/hooks/useTooltip.tsx +116 -29
  128. package/src/index.jsx +0 -2
  129. package/src/scss/main.scss +13 -80
  130. package/src/store/chart.actions.ts +2 -0
  131. package/src/store/chart.reducer.ts +5 -1
  132. package/src/test/CdcChart.test.jsx +8 -3
  133. package/src/types/ChartConfig.ts +53 -11
  134. package/src/types/ChartContext.ts +4 -0
  135. package/vite.config.js +1 -1
  136. package/vitest.config.ts +16 -0
  137. package/src/_stories/_mock/pie_data.json +0 -218
  138. package/src/components/AreaChart/components/AreaChart.jsx +0 -109
  139. package/src/coreStyles_chart.scss +0 -3
  140. package/src/helpers/configHelpers.ts +0 -28
  141. package/src/helpers/generateColorsArray.ts +0 -8
  142. package/src/helpers/sort.ts +0 -7
  143. package/src/hooks/useActiveElement.js +0 -19
  144. package/src/hooks/useChartClasses.js +0 -41
  145. package/src/hooks/useColorPalette.js +0 -76
@@ -2,12 +2,14 @@ import React, { useContext } from 'react'
2
2
  import ConfigContext from '../../../../ConfigContext'
3
3
 
4
4
  // Core
5
- import InputSelect from '@cdc/core/components/inputs/InputSelect'
6
5
  import Check from '@cdc/core/assets/icon-check.svg'
7
6
  import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
8
- import { sequentialPalettes } from '@cdc/core/data/colorPalettes'
7
+ import { colorPalettesChartV1, colorPalettesChartV2, sequentialPalettes } from '@cdc/core/data/colorPalettes'
8
+ import { updatePaletteNames } from '@cdc/core/helpers/updatePaletteNames'
9
+ import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
9
10
  import Icon from '@cdc/core/components/ui/Icon'
10
11
  import { Select } from '@cdc/core/components/EditorPanel/Inputs'
12
+ import { buildForecastPaletteOptions } from '../../../../helpers/buildForecastPaletteOptions'
11
13
 
12
14
  // Third Party
13
15
  import {
@@ -25,7 +27,7 @@ const SeriesContext = React.createContext({})
25
27
  const SeriesWrapper = props => {
26
28
  const { updateConfig, config, rawData } = useContext(ConfigContext)
27
29
 
28
- const { getColumns, selectComponent } = props
30
+ const { getColumns, selectComponent, handleForecastPaletteSelection } = props
29
31
 
30
32
  const supportedRightAxisTypes = ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg']
31
33
 
@@ -57,7 +59,9 @@ const SeriesWrapper = props => {
57
59
  }
58
60
 
59
61
  return (
60
- <SeriesContext.Provider value={{ updateSeries, supportedRightAxisTypes, getColumns, selectComponent }}>
62
+ <SeriesContext.Provider
63
+ value={{ updateSeries, supportedRightAxisTypes, getColumns, selectComponent, handleForecastPaletteSelection }}
64
+ >
61
65
  {props.children}
62
66
  </SeriesContext.Provider>
63
67
  )
@@ -93,12 +97,12 @@ const SeriesDropdownLineType = props => {
93
97
  })
94
98
 
95
99
  return (
96
- <InputSelect
100
+ <Select
97
101
  initial='Select an option'
98
102
  value={series.lineType ? series.lineType : 'curveLinear'}
99
103
  label='Series Line Type'
100
- onChange={event => {
101
- changeLineType(index, event.target.value)
104
+ updateField={(_section, _subsection, _fieldName, value) => {
105
+ changeLineType(index, value)
102
106
  }}
103
107
  options={options}
104
108
  />
@@ -113,35 +117,35 @@ const SeriesDropdownSeriesType = props => {
113
117
 
114
118
  const getOptions = () => {
115
119
  if (config.visualizationType === 'Combo') {
116
- return {
117
- Bar: 'Bar',
118
- Line: 'Line',
119
- 'dashed-sm': 'Small Dashed',
120
- 'dashed-md': 'Medium Dashed',
121
- 'dashed-lg': 'Large Dashed',
122
- 'Area Chart': 'Area Chart',
123
- Forecasting: 'Forecasting'
124
- }
120
+ return [
121
+ { value: 'Bar', label: 'Bar' },
122
+ { value: 'Line', label: 'Line' },
123
+ { value: 'dashed-sm', label: 'Small Dashed' },
124
+ { value: 'dashed-md', label: 'Medium Dashed' },
125
+ { value: 'dashed-lg', label: 'Large Dashed' },
126
+ { value: 'Area Chart', label: 'Area Chart' },
127
+ { value: 'Forecasting', label: 'Forecasting' }
128
+ ]
125
129
  }
126
130
  if (config.visualizationType === 'Line' || config.visualizationType === 'Bump Chart') {
127
- return {
128
- Line: 'Line',
129
- 'dashed-sm': 'Small Dashed',
130
- 'dashed-md': 'Medium Dashed',
131
- 'dashed-lg': 'Large Dashed'
132
- }
131
+ return [
132
+ { value: 'Line', label: 'Line' },
133
+ { value: 'dashed-sm', label: 'Small Dashed' },
134
+ { value: 'dashed-md', label: 'Medium Dashed' },
135
+ { value: 'dashed-lg', label: 'Large Dashed' }
136
+ ]
133
137
  }
134
138
  }
135
139
 
136
140
  // Allowable changes
137
141
  if (!['Line', 'Combo', 'Bump Chart'].includes(config.visualizationType)) return
138
142
  return (
139
- <InputSelect
143
+ <Select
140
144
  initial='Select an option'
141
145
  value={series.type}
142
146
  label='Series Type'
143
- onChange={event => {
144
- updateSeries(index, event.target.value, 'type')
147
+ updateField={(_section, _subsection, _fieldName, value) => {
148
+ updateSeries(index, value, 'type')
145
149
  }}
146
150
  options={getOptions()}
147
151
  />
@@ -157,13 +161,13 @@ const SeriesDropdownForecastingStage = props => {
157
161
  // Only combo charts are allowed to have different options
158
162
 
159
163
  return (
160
- <InputSelect
164
+ <Select
161
165
  initial='Select an option'
162
166
  value={series.stageColumn}
163
167
  label='Add Forecasting Stages'
164
- onChange={e => {
168
+ updateField={(_section, _subsection, _fieldName, value) => {
165
169
  let stageObjects = []
166
- let tempGroups = new Set(rawData?.map(item => item[e.target.value])) // [estimate, forecast, etc.]
170
+ let tempGroups = new Set(rawData?.map(item => item[value])) // [estimate, forecast, etc.]
167
171
  tempGroups = Array.from(tempGroups) // convert set to array
168
172
 
169
173
  tempGroups = tempGroups.filter(group => group !== undefined) // removes undefined
@@ -171,7 +175,7 @@ const SeriesDropdownForecastingStage = props => {
171
175
  tempGroups.forEach(group => stageObjects.push({ key: group }))
172
176
 
173
177
  const copyOfSeries = [...config.series] // copy the entire series array
174
- copyOfSeries[index] = { ...copyOfSeries[index], stages: stageObjects, stageColumn: e.target.value }
178
+ copyOfSeries[index] = { ...copyOfSeries[index], stages: stageObjects, stageColumn: value }
175
179
 
176
180
  updateConfig({
177
181
  ...config,
@@ -195,19 +199,19 @@ const SeriesDropdownForecastingColumn = props => {
195
199
  if (!series.stageColumn) return
196
200
 
197
201
  let tempGroups = new Set(rawData.map(item => item[series.stageColumn])) // [estimate, forecast, etc.]
198
- tempGroups = Array.from(tempGroups) // convert set to array
202
+ let tempGroupsArray = Array.from(tempGroups) // convert set to array
199
203
 
200
- tempGroups = tempGroups.filter(group => group !== undefined) // removes undefined
204
+ tempGroupsArray = tempGroupsArray.filter(group => group !== undefined) // removes undefined
201
205
 
202
206
  return (
203
- <InputSelect
207
+ <Select
204
208
  initial='Select an option'
205
209
  value={series.stageItem}
206
210
  label='Forecasting Item Column'
207
- onChange={event => {
208
- updateSeries(index, event.target.value, 'stageItem')
211
+ updateField={(_section, _subsection, _fieldName, value) => {
212
+ updateSeries(index, value, 'stageItem')
209
213
  }}
210
- options={tempGroups}
214
+ options={tempGroupsArray}
211
215
  />
212
216
  )
213
217
  }
@@ -224,23 +228,24 @@ const SeriesDropdownAxisPosition = props => {
224
228
  return
225
229
  }
226
230
  return (
227
- <InputSelect
231
+ <Select
228
232
  initial='Select an option'
229
233
  value={series.axis ? series.axis : 'Left'}
230
234
  label='Series Axis'
231
- onChange={event => {
232
- updateSeries(index, event.target.value, 'axis')
233
- }}
234
- options={{
235
- ['Left']: 'Left',
236
- ['Right']: 'Right'
235
+ updateField={(_section, _subsection, _fieldName, value) => {
236
+ updateSeries(index, value, 'axis')
237
237
  }}
238
+ options={[
239
+ { value: 'Left', label: 'Left' },
240
+ { value: 'Right', label: 'Right' }
241
+ ]}
238
242
  />
239
243
  )
240
244
  }
241
245
 
242
246
  const SeriesDropdownForecastColor = props => {
243
- const { config, updateConfig } = useContext(ConfigContext)
247
+ const { config } = useContext(ConfigContext)
248
+ const { handleForecastPaletteSelection } = useContext(SeriesContext)
244
249
 
245
250
  const { index, series } = props
246
251
 
@@ -249,30 +254,38 @@ const SeriesDropdownForecastColor = props => {
249
254
  // Hide AxisPositionDropdown in certain cases.
250
255
  if (!series) return
251
256
 
252
- const allowedForecastingColors = () => {
253
- return Object.keys(sequentialPalettes)
254
- }
257
+ // Determine palette version and use appropriate palette set
258
+ // Forecasting charts use sequentialPalettes for v1, sequential-only palettes for v2
259
+ const paletteVersion = getColorPaletteVersion(config)
260
+
261
+ // Get version-appropriate palettes (v1 uses sequentialPalettes, v2 uses filtered v2 palettes)
262
+ const forecastPalettes =
263
+ paletteVersion === 1
264
+ ? sequentialPalettes
265
+ : Object.fromEntries(Object.entries(colorPalettesChartV2).filter(([key]) => key.startsWith('sequential')))
266
+
267
+ // For dropdown options, only show version-specific palettes
268
+ const processedPalettes = updatePaletteNames(forecastPalettes)
269
+ const paletteOptionsObject = buildForecastPaletteOptions(processedPalettes, paletteVersion)
270
+
271
+ // Convert object to array format for Select component
272
+ const paletteOptions = Object.entries(paletteOptionsObject).map(([value, label]) => ({
273
+ value,
274
+ label
275
+ }))
255
276
 
256
277
  return series?.stages?.map((stage, stageIndex) => (
257
- <InputSelect
278
+ <Select
258
279
  key={`${stage}--${stageIndex}`}
259
280
  initial='Select an option'
260
- value={
261
- config.series?.[index].stages?.[stageIndex].color ? config.series?.[index].stages?.[stageIndex].color : 'Select'
262
- }
281
+ value={config.series?.[index].stages?.[stageIndex].color || 'Select'}
263
282
  label={`${stage.key} Series Color`}
264
- onChange={event => {
265
- const copyOfSeries = [...config.series] // copy the entire series array
266
- const copyOfStages = copyOfSeries[index].stages
267
- copyOfStages[stageIndex].color = event.target.value
268
- copyOfSeries[index] = { ...copyOfSeries[index], stages: copyOfStages }
269
-
270
- updateConfig({
271
- ...config,
272
- series: copyOfSeries
273
- })
283
+ updateField={(_section, _subsection, _fieldName, value) => {
284
+ if (handleForecastPaletteSelection) {
285
+ handleForecastPaletteSelection(value, index, stageIndex)
286
+ }
274
287
  }}
275
- options={Object.keys(sequentialPalettes)}
288
+ options={paletteOptions}
276
289
  />
277
290
  ))
278
291
  }
@@ -317,7 +330,7 @@ const SeriesDropdownConfidenceInterval = props => {
317
330
  </>
318
331
  </AccordionItemHeading>
319
332
  <AccordionItemPanel>
320
- <InputSelect
333
+ <Select
321
334
  initial='Select an option'
322
335
  value={
323
336
  config.series[index].confidenceIntervals[ciIndex].low
@@ -325,9 +338,9 @@ const SeriesDropdownConfidenceInterval = props => {
325
338
  : 'Select'
326
339
  }
327
340
  label='Low Confidence Interval'
328
- onChange={e => {
341
+ updateField={(_section, _subsection, _fieldName, value) => {
329
342
  const copiedConfidenceArray = [...config.series[index].confidenceIntervals]
330
- copiedConfidenceArray[ciIndex].low = e.target.value
343
+ copiedConfidenceArray[ciIndex].low = value
331
344
  const copyOfSeries = [...config.series] // copy the entire series array
332
345
  copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: copiedConfidenceArray }
333
346
  updateConfig({
@@ -337,7 +350,7 @@ const SeriesDropdownConfidenceInterval = props => {
337
350
  }}
338
351
  options={getColumns()}
339
352
  />
340
- <InputSelect
353
+ <Select
341
354
  initial='Select an option'
342
355
  value={
343
356
  config.series[index].confidenceIntervals[ciIndex].high
@@ -345,9 +358,9 @@ const SeriesDropdownConfidenceInterval = props => {
345
358
  : 'Select'
346
359
  }
347
360
  label='High Confidence Interval'
348
- onChange={e => {
361
+ updateField={(_section, _subsection, _fieldName, value) => {
349
362
  const copiedConfidenceArray = [...config.series[index].confidenceIntervals]
350
- copiedConfidenceArray[ciIndex].high = e.target.value
363
+ copiedConfidenceArray[ciIndex].high = value
351
364
  const copyOfSeries = [...config.series] // copy the entire series array
352
365
  copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: copiedConfidenceArray }
353
366
  updateConfig({