@cdc/chart 4.25.8 → 4.25.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 (89) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/dist/cdcchart.js +37524 -35243
  3. package/examples/feature/__data__/planet-example-data.json +0 -30
  4. package/examples/grouped-bar-test.json +400 -0
  5. package/examples/private/d.json +382 -0
  6. package/examples/private/example-2.json +49784 -0
  7. package/examples/private/f2.json +1 -0
  8. package/examples/private/f4.json +1577 -0
  9. package/examples/private/forecast.json +1180 -0
  10. package/examples/private/lollipop.json +468 -0
  11. package/examples/private/new.json +48756 -0
  12. package/examples/private/pie-chart-legend.json +904 -0
  13. package/examples/suppressed_tooltip.json +480 -0
  14. package/index.html +10 -22
  15. package/package.json +25 -7
  16. package/src/CdcChart.tsx +1 -2
  17. package/src/CdcChartComponent.tsx +174 -32
  18. package/src/_stories/Chart.Anchors.stories.tsx +2 -2
  19. package/src/_stories/Chart.BoxPlot.stories.tsx +1 -1
  20. package/src/_stories/Chart.CI.stories.tsx +1 -1
  21. package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
  22. package/src/_stories/Chart.DynamicSeries.stories.tsx +2 -2
  23. package/src/_stories/Chart.Filters.stories.tsx +2 -2
  24. package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
  25. package/src/_stories/Chart.Patterns.stories.tsx +19 -0
  26. package/src/_stories/Chart.ScatterPlot.stories.tsx +1 -1
  27. package/src/_stories/Chart.stories.tsx +8 -5
  28. package/src/_stories/Chart.tooltip.stories.tsx +1 -1
  29. package/src/_stories/ChartAnnotation.stories.tsx +1 -1
  30. package/src/_stories/ChartAxisLabels.stories.tsx +2 -2
  31. package/src/_stories/ChartAxisTitles.stories.tsx +2 -2
  32. package/src/_stories/ChartEditor.stories.tsx +60 -60
  33. package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
  34. package/src/_stories/ChartLine.Symbols.stories.tsx +1 -1
  35. package/src/_stories/ChartPrefixSuffix.stories.tsx +2 -2
  36. package/src/_stories/_mock/stacked-pattern-test.json +520 -0
  37. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -0
  38. package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
  39. package/src/components/BarChart/components/BarChart.Horizontal.tsx +159 -20
  40. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +138 -5
  41. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +215 -73
  42. package/src/components/BarChart/components/BarChart.Vertical.tsx +153 -21
  43. package/src/components/BarChart/helpers/index.ts +43 -4
  44. package/src/components/BarChart/helpers/lollipopColors.ts +27 -0
  45. package/src/components/BarChart/helpers/useBarChart.ts +25 -3
  46. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +2 -1
  47. package/src/components/DeviationBar.jsx +9 -6
  48. package/src/components/EditorPanel/EditorPanel.tsx +364 -39
  49. package/src/components/EditorPanel/EditorPanelContext.ts +3 -0
  50. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +414 -0
  51. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +28 -20
  52. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +115 -120
  53. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  54. package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +0 -8
  55. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +49 -48
  56. package/src/components/Forecasting/Forecasting.tsx +36 -6
  57. package/src/components/ForestPlot/ForestPlot.tsx +11 -7
  58. package/src/components/ForestPlot/ForestPlotProps.ts +1 -1
  59. package/src/components/Legend/Legend.Component.tsx +106 -13
  60. package/src/components/Legend/helpers/createFormatLabels.tsx +230 -171
  61. package/src/components/LegendWrapper.tsx +1 -1
  62. package/src/components/LineChart/components/LineChart.Circle.tsx +2 -2
  63. package/src/components/LineChart/index.tsx +2 -2
  64. package/src/components/LinearChart.tsx +22 -5
  65. package/src/components/PairedBarChart.jsx +6 -4
  66. package/src/components/PieChart/PieChart.tsx +170 -54
  67. package/src/components/Sankey/components/Sankey.tsx +7 -1
  68. package/src/components/ScatterPlot/ScatterPlot.jsx +32 -4
  69. package/src/data/initial-state.js +315 -293
  70. package/src/helpers/buildForecastPaletteMappings.ts +112 -0
  71. package/src/helpers/buildForecastPaletteOptions.ts +109 -0
  72. package/src/helpers/getColorScale.ts +72 -8
  73. package/src/helpers/getNewRuntime.ts +1 -1
  74. package/src/helpers/getTransformedData.ts +1 -1
  75. package/src/hooks/useChartHoverAnalytics.tsx +44 -0
  76. package/src/hooks/useReduceData.ts +105 -70
  77. package/src/hooks/useTooltip.tsx +57 -15
  78. package/src/index.jsx +0 -2
  79. package/src/scss/main.scss +12 -0
  80. package/src/store/chart.reducer.ts +1 -1
  81. package/src/test/CdcChart.test.jsx +8 -3
  82. package/src/types/ChartConfig.ts +30 -6
  83. package/src/types/ChartContext.ts +1 -0
  84. package/vite.config.js +1 -1
  85. package/vitest.config.ts +16 -0
  86. package/src/coreStyles_chart.scss +0 -3
  87. package/src/helpers/configHelpers.ts +0 -28
  88. package/src/helpers/generateColorsArray.ts +0 -8
  89. package/src/hooks/useColorPalette.js +0 -76
@@ -0,0 +1,414 @@
1
+ import { useContext, FC } from 'react'
2
+ import {
3
+ Accordion,
4
+ AccordionItem,
5
+ AccordionItemHeading,
6
+ AccordionItemPanel,
7
+ AccordionItemButton
8
+ } from 'react-accessible-accordion'
9
+ import Tooltip from '@cdc/core/components/ui/Tooltip'
10
+ import Icon from '@cdc/core/components/ui/Icon'
11
+ import Button from '@cdc/core/components/elements/Button'
12
+ import Alert from '@cdc/core/components/Alert'
13
+ import ConfigContext from '../../../../ConfigContext'
14
+ import { ChartContext } from '../../../../types/ChartContext'
15
+ import { PanelProps } from '../PanelProps'
16
+ import { checkColorContrast, getColorContrast } from '@cdc/core/helpers/cove/accessibility'
17
+ import { getColorScale } from '../../../../helpers/getColorScale'
18
+ import _ from 'lodash'
19
+
20
+ const PanelPatternSettings: FC<PanelProps> = props => {
21
+ const { config, updateConfig, transformedData } = useContext<ChartContext>(ConfigContext)
22
+
23
+ type LegendPattern = {
24
+ label?: string
25
+ color?: string
26
+ shape?: 'circles' | 'lines' | 'diagonalLines' | 'waves'
27
+ dataKey?: string
28
+ dataValue?: string
29
+ contrastCheck?: boolean
30
+ patternSize?: number
31
+ }
32
+
33
+ // Safe legend reference with defaults to avoid crashes when legend is undefined
34
+ const legendCfg = (config.legend || { patterns: {} }) as {
35
+ patterns: Record<string, LegendPattern>
36
+ }
37
+
38
+ const patternTypes = [
39
+ { value: 'circles', label: 'Circles' },
40
+ { value: 'lines', label: 'Horizontal Lines' },
41
+ { value: 'diagonalLines', label: 'Diagonal Lines' },
42
+ { value: 'waves', label: 'Waves' }
43
+ ]
44
+
45
+ const patternSizes = [
46
+ { value: 'small', label: 'Small' },
47
+ { value: 'medium', label: 'Medium' },
48
+ { value: 'large', label: 'Large' }
49
+ ]
50
+
51
+ // Convert numeric size to text and vice versa
52
+ const getPatternSizeText = (numericSize: number): string => {
53
+ if (numericSize <= 6) return 'small'
54
+ if (numericSize <= 12) return 'medium'
55
+ return 'large'
56
+ }
57
+
58
+ const getPatternSizeNumeric = (textSize: string): number => {
59
+ switch (textSize) {
60
+ case 'small':
61
+ return 6
62
+ case 'medium':
63
+ return 10
64
+ case 'large':
65
+ return 16
66
+ default:
67
+ return 8
68
+ }
69
+ }
70
+
71
+ // Get unique values from a specific data field for dropdown options
72
+ const getDataValueOptions = (dataKey: string) => {
73
+ if (!dataKey || !Array.isArray(transformedData) || transformedData.length === 0) {
74
+ return []
75
+ }
76
+
77
+ const uniqueValues = Array.from(new Set(transformedData.map(row => row[dataKey])))
78
+ .filter(val => val !== undefined && val !== null && val !== '')
79
+ .sort()
80
+
81
+ return uniqueValues.map(value => ({ value: String(value), label: String(value) }))
82
+ }
83
+
84
+ const getFieldOptions = () => {
85
+ if (!Array.isArray(transformedData) || transformedData.length === 0) return []
86
+
87
+ const firstRow = transformedData[0] || {}
88
+ return Object.keys(firstRow).map(key => ({ value: key, label: key }))
89
+ }
90
+
91
+ // Checks contrast and logs warning if needed
92
+ const checkAndLogContrast = (fill: string, patternColor: string, dataValue: string, dataKey: string): boolean => {
93
+ if (!fill || !patternColor) return true // Default to true if colors are missing
94
+
95
+ const contrastCheck = checkColorContrast(fill, patternColor)
96
+
97
+ if (!contrastCheck) {
98
+ console.error(
99
+ `COVE: pattern contrast check failed for ${dataValue} in ${dataKey} with:
100
+ pattern color: ${patternColor}
101
+ background color: ${fill}
102
+ contrast: ${getColorContrast(fill, patternColor)}`
103
+ )
104
+ }
105
+
106
+ return contrastCheck
107
+ }
108
+
109
+ // Perform contrast check for a specific pattern against actual bar colors
110
+ const performContrastCheck = (patternKey: string, patternColor: string) => {
111
+ if (!patternColor || patternColor === '') return true
112
+
113
+ // Get the actual bar colors that the pattern will be overlaid on
114
+ let seriesColors: string[] = []
115
+
116
+ if (config.customColors && config.customColors.length > 0) {
117
+ // Use custom colors if available
118
+ seriesColors = config.customColors
119
+ } else {
120
+ // Use the same color generation logic as the chart
121
+ try {
122
+ const colorScale = getColorScale(config)
123
+ // Get colors for all series labels (not keys!)
124
+ const seriesLabels = config.runtime?.seriesLabelsAll || []
125
+ seriesColors = seriesLabels.map(label => colorScale(label)).filter(color => color !== null)
126
+ } catch (error) {
127
+ return true
128
+ }
129
+ }
130
+
131
+ if (seriesColors.length === 0) {
132
+ return true
133
+ }
134
+
135
+ // Check contrast against all series colors (bars that the pattern will overlay)
136
+ // Pattern should have good contrast against ALL bar colors it will appear on
137
+ let allContrastsPass = true
138
+ const contrastResults: Array<{ color: string; passes: boolean; ratio: number | false }> = []
139
+
140
+ seriesColors.forEach((barColor, index) => {
141
+ const contrastPasses = checkAndLogContrast(barColor, patternColor, patternKey, `series-${index}`)
142
+ const contrastRatio = getColorContrast(barColor, patternColor)
143
+
144
+ contrastResults.push({
145
+ color: barColor,
146
+ passes: contrastPasses,
147
+ ratio: contrastRatio
148
+ })
149
+
150
+ if (!contrastPasses) {
151
+ allContrastsPass = false
152
+ }
153
+ })
154
+
155
+ return allContrastsPass
156
+ }
157
+
158
+ const fieldOptions = getFieldOptions()
159
+ const currentPatterns: Record<string, LegendPattern> = legendCfg.patterns || {}
160
+
161
+ // Check if all patterns pass contrast requirements
162
+ const checkPatternContrasts = () => {
163
+ return Object.values(currentPatterns).every(pattern => pattern.contrastCheck !== false)
164
+ }
165
+
166
+ const handleAddPattern = () => {
167
+ const currentPatterns = legendCfg.patterns || {}
168
+
169
+ // For charts, we'll add a default pattern that users can configure
170
+ const newPatternKey = `Pattern${Object.keys(currentPatterns).length + 1}`
171
+ const defaultColor = '#000000'
172
+ const defaultDataKey = fieldOptions.length > 0 ? fieldOptions[0].value : ''
173
+
174
+ const newPatterns = {
175
+ ...currentPatterns,
176
+ [newPatternKey]: {
177
+ label: newPatternKey,
178
+ color: defaultColor,
179
+ shape: 'circles' as const,
180
+ dataKey: defaultDataKey,
181
+ dataValue: '',
182
+ patternSize: 8, // Default pattern size
183
+ contrastCheck: performContrastCheck(newPatternKey, defaultColor)
184
+ }
185
+ }
186
+
187
+ updateConfig({
188
+ ...config,
189
+ legend: {
190
+ ...(config.legend || {}),
191
+ patterns: newPatterns
192
+ }
193
+ })
194
+ }
195
+
196
+ const handleRemovePattern = (patternKey: string) => {
197
+ const newPatterns = { ...(legendCfg.patterns || {}) }
198
+ delete newPatterns[patternKey]
199
+
200
+ updateConfig({
201
+ ...config,
202
+ legend: {
203
+ ...(config.legend || {}),
204
+ patterns: newPatterns
205
+ }
206
+ })
207
+ }
208
+
209
+ const handlePatternKeyChange = (oldKey: string, newKey: string) => {
210
+ if (newKey === oldKey || !newKey.trim()) return
211
+
212
+ const currentPatterns = legendCfg.patterns || {}
213
+ const patternData = currentPatterns[oldKey]
214
+
215
+ if (!patternData) return
216
+
217
+ // Create new patterns object with updated key
218
+ const newPatterns = { ...currentPatterns }
219
+ delete newPatterns[oldKey]
220
+ newPatterns[newKey] = patternData
221
+
222
+ updateConfig({
223
+ ...config,
224
+ legend: {
225
+ ...(config.legend || {}),
226
+ patterns: newPatterns
227
+ }
228
+ })
229
+ }
230
+
231
+ const handlePatternUpdate = (patternKey: string, field: string, value: any) => {
232
+ const updatedPattern = {
233
+ ...(legendCfg.patterns?.[patternKey] || {}),
234
+ [field]: value
235
+ }
236
+
237
+ // Perform contrast check if color is being updated
238
+ if (field === 'color') {
239
+ updatedPattern.contrastCheck = performContrastCheck(patternKey, value)
240
+ }
241
+
242
+ const newPatterns = {
243
+ ...(legendCfg.patterns || {}),
244
+ [patternKey]: updatedPattern
245
+ }
246
+
247
+ updateConfig({
248
+ ...config,
249
+ legend: {
250
+ ...(config.legend || {}),
251
+ patterns: newPatterns
252
+ }
253
+ })
254
+ }
255
+
256
+ return (
257
+ <AccordionItem>
258
+ <AccordionItemHeading>
259
+ <AccordionItemButton>Pattern Settings</AccordionItemButton>
260
+ </AccordionItemHeading>
261
+ <AccordionItemPanel>
262
+ {Object.keys(currentPatterns).length > 0 && (
263
+ <>
264
+ <Alert
265
+ type={checkPatternContrasts() ? 'success' : 'danger'}
266
+ message='Pattern colors must comply with <a href="https://www.w3.org/TR/WCAG21/">WCAG 2.1</a> 3:1 contrast ratio.'
267
+ showCloseButton={false}
268
+ />
269
+ <br />
270
+ </>
271
+ )}
272
+
273
+ {/* Individual Pattern Configurations */}
274
+ {Object.entries(currentPatterns).map(([patternKey, pattern], index) => {
275
+ const p: LegendPattern = pattern || {}
276
+ const dataValueOptions = p.dataKey ? getDataValueOptions(p.dataKey) : []
277
+
278
+ return (
279
+ <Accordion allowZeroExpanded key={`pattern-accordion-${index}`}>
280
+ <AccordionItem>
281
+ <AccordionItemHeading>
282
+ <AccordionItemButton>
283
+ {p.dataKey && p.dataValue ? `${p.dataKey}: ${p.dataValue}` : `Pattern ${index + 1}`}
284
+ </AccordionItemButton>
285
+ </AccordionItemHeading>
286
+ <AccordionItemPanel>
287
+ {p.contrastCheck ?? true ? (
288
+ <Alert type='success' message='This pattern passes contrast checks' showCloseButton={false} />
289
+ ) : (
290
+ <Alert
291
+ type='danger'
292
+ message='Error: <a href="https://webaim.org/resources/contrastchecker/" target="_blank"> Review Color Contrast</a>'
293
+ showCloseButton={false}
294
+ />
295
+ )}
296
+
297
+ <label htmlFor={`pattern-datakey-${patternKey}`}>Data Key:</label>
298
+ <select
299
+ id={`pattern-datakey-${patternKey}`}
300
+ value={p.dataKey || 'Select'}
301
+ onChange={e => handlePatternUpdate(patternKey, 'dataKey', e.target.value)}
302
+ >
303
+ <option value='Select'>Select Data Key</option>
304
+ {fieldOptions.map((option, index) => (
305
+ <option value={option.value} key={index}>
306
+ {option.label}
307
+ </option>
308
+ ))}
309
+ </select>
310
+
311
+ {p.dataKey && (
312
+ <>
313
+ <label htmlFor={`pattern-datavalue-${patternKey}`}>
314
+ Data Value:
315
+ <input
316
+ type='text'
317
+ id={`pattern-datavalue-${patternKey}`}
318
+ value={p.dataValue || ''}
319
+ onChange={e => handlePatternUpdate(patternKey, 'dataValue', e.target.value)}
320
+ placeholder='Enter data value'
321
+ />
322
+ </label>
323
+ </>
324
+ )}
325
+
326
+ <label htmlFor={`pattern-label-${patternKey}`}>
327
+ Label (optional):
328
+ <input
329
+ type='text'
330
+ id={`pattern-label-${patternKey}`}
331
+ value={p.label || ''}
332
+ onChange={e => handlePatternUpdate(patternKey, 'label', e.target.value)}
333
+ />
334
+ </label>
335
+
336
+ <label htmlFor={`pattern-type-${patternKey}`}>Pattern Type:</label>
337
+ <select
338
+ id={`pattern-type-${patternKey}`}
339
+ value={p.shape || 'circles'}
340
+ onChange={e => handlePatternUpdate(patternKey, 'shape', e.target.value)}
341
+ >
342
+ {patternTypes.map((patternType, index) => (
343
+ <option value={patternType.value} key={index}>
344
+ {patternType.label}
345
+ </option>
346
+ ))}
347
+ </select>
348
+
349
+ <label htmlFor={`pattern-size-${patternKey}`}>Pattern Size:</label>
350
+ <select
351
+ id={`pattern-size-${patternKey}`}
352
+ value={getPatternSizeText(p.patternSize || 8)}
353
+ onChange={e =>
354
+ handlePatternUpdate(patternKey, 'patternSize', getPatternSizeNumeric(e.target.value))
355
+ }
356
+ >
357
+ {patternSizes.map((size, index) => (
358
+ <option value={size.value} key={index}>
359
+ {size.label}
360
+ </option>
361
+ ))}
362
+ </select>
363
+
364
+ <div className='mt-3'>
365
+ <label htmlFor={`pattern-color-${patternKey}`}>
366
+ Pattern Color
367
+ <Tooltip style={{ textTransform: 'none' }}>
368
+ <Tooltip.Target>
369
+ <Icon
370
+ display='question'
371
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
372
+ />
373
+ </Tooltip.Target>
374
+ <Tooltip.Content>
375
+ <p>
376
+ If this setting is used, it is the responsibility of the visualization author to verify the
377
+ visualization colors meet WCAG 3:1 contrast ratios.
378
+ </p>
379
+ </Tooltip.Content>
380
+ </Tooltip>
381
+ <input
382
+ type='text'
383
+ value={p.color || '#666666'}
384
+ id={`pattern-color-${patternKey}`}
385
+ onChange={e => handlePatternUpdate(patternKey, 'color', e.target.value)}
386
+ placeholder='#666666'
387
+ />
388
+ </label>
389
+ </div>
390
+
391
+ <Button onClick={() => handleRemovePattern(patternKey)} className='btn btn-danger'>
392
+ Remove Pattern
393
+ </Button>
394
+ </AccordionItemPanel>
395
+ </AccordionItem>
396
+ </Accordion>
397
+ )
398
+ })}
399
+
400
+ {/* Add Pattern Button */}
401
+ <button className='btn btn-primary full-width mt-2' onClick={handleAddPattern}>
402
+ Add Pattern
403
+ </button>
404
+
405
+ {Object.keys(currentPatterns).length === 0 && (
406
+ <p style={{ color: '#666', fontStyle: 'italic' }}>
407
+ No patterns configured. Use "Add Pattern" to create pattern configurations.
408
+ </p>
409
+ )}
410
+ </AccordionItemPanel>
411
+ </AccordionItem>
412
+ )
413
+ }
414
+ export default PanelPatternSettings
@@ -5,9 +5,12 @@ import ConfigContext from '../../../../ConfigContext'
5
5
  import InputSelect from '@cdc/core/components/inputs/InputSelect'
6
6
  import Check from '@cdc/core/assets/icon-check.svg'
7
7
  import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
8
- import { sequentialPalettes } from '@cdc/core/data/colorPalettes'
8
+ import { colorPalettesChartV1, colorPalettesChartV2, sequentialPalettes } from '@cdc/core/data/colorPalettes'
9
+ import { updatePaletteNames } from '@cdc/core/helpers/updatePaletteNames'
10
+ import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
9
11
  import Icon from '@cdc/core/components/ui/Icon'
10
12
  import { Select } from '@cdc/core/components/EditorPanel/Inputs'
13
+ import { buildForecastPaletteOptions } from '../../../../helpers/buildForecastPaletteOptions'
11
14
 
12
15
  // Third Party
13
16
  import {
@@ -25,7 +28,7 @@ const SeriesContext = React.createContext({})
25
28
  const SeriesWrapper = props => {
26
29
  const { updateConfig, config, rawData } = useContext(ConfigContext)
27
30
 
28
- const { getColumns, selectComponent } = props
31
+ const { getColumns, selectComponent, handleForecastPaletteSelection } = props
29
32
 
30
33
  const supportedRightAxisTypes = ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg']
31
34
 
@@ -57,7 +60,9 @@ const SeriesWrapper = props => {
57
60
  }
58
61
 
59
62
  return (
60
- <SeriesContext.Provider value={{ updateSeries, supportedRightAxisTypes, getColumns, selectComponent }}>
63
+ <SeriesContext.Provider
64
+ value={{ updateSeries, supportedRightAxisTypes, getColumns, selectComponent, handleForecastPaletteSelection }}
65
+ >
61
66
  {props.children}
62
67
  </SeriesContext.Provider>
63
68
  )
@@ -240,7 +245,8 @@ const SeriesDropdownAxisPosition = props => {
240
245
  }
241
246
 
242
247
  const SeriesDropdownForecastColor = props => {
243
- const { config, updateConfig } = useContext(ConfigContext)
248
+ const { config } = useContext(ConfigContext)
249
+ const { handleForecastPaletteSelection } = useContext(SeriesContext)
244
250
 
245
251
  const { index, series } = props
246
252
 
@@ -249,30 +255,32 @@ const SeriesDropdownForecastColor = props => {
249
255
  // Hide AxisPositionDropdown in certain cases.
250
256
  if (!series) return
251
257
 
252
- const allowedForecastingColors = () => {
253
- return Object.keys(sequentialPalettes)
254
- }
258
+ // Determine palette version and use appropriate palette set
259
+ // Forecasting charts use sequentialPalettes for v1, sequential-only palettes for v2
260
+ const paletteVersion = getColorPaletteVersion(config)
261
+
262
+ // Get version-appropriate palettes (v1 uses sequentialPalettes, v2 uses filtered v2 palettes)
263
+ const forecastPalettes =
264
+ paletteVersion === 1
265
+ ? sequentialPalettes
266
+ : Object.fromEntries(Object.entries(colorPalettesChartV2).filter(([key]) => key.startsWith('sequential')))
267
+
268
+ // For dropdown options, only show version-specific palettes
269
+ const processedPalettes = updatePaletteNames(forecastPalettes)
270
+ const paletteOptions = buildForecastPaletteOptions(processedPalettes, paletteVersion)
255
271
 
256
272
  return series?.stages?.map((stage, stageIndex) => (
257
273
  <InputSelect
258
274
  key={`${stage}--${stageIndex}`}
259
275
  initial='Select an option'
260
- value={
261
- config.series?.[index].stages?.[stageIndex].color ? config.series?.[index].stages?.[stageIndex].color : 'Select'
262
- }
276
+ value={config.series?.[index].stages?.[stageIndex].color || 'Select'}
263
277
  label={`${stage.key} Series Color`}
264
278
  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
- })
279
+ if (handleForecastPaletteSelection) {
280
+ handleForecastPaletteSelection(event.target.value, index, stageIndex)
281
+ }
274
282
  }}
275
- options={Object.keys(sequentialPalettes)}
283
+ options={paletteOptions}
276
284
  />
277
285
  ))
278
286
  }