@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
@@ -1,6 +1,7 @@
1
1
  import parse from 'html-react-parser'
2
2
  import React from 'react'
3
3
  import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
4
+ import { PatternLines, PatternCircles, PatternWaves } from '@visx/pattern'
4
5
  import LegendShape from '@cdc/core/components/LegendShape'
5
6
  import Button from '@cdc/core/components/elements/Button'
6
7
  import { getLegendClasses } from './helpers/getLegendClasses'
@@ -18,6 +19,7 @@ import LegendLineShape from './LegendLine.Shape'
18
19
  import LegendGroup from './LegendGroup'
19
20
  import { getSeriesWithData } from '../../helpers/dataHelpers'
20
21
  import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
22
+ import { getVizTitle, getVizSubType } from '@cdc/core/helpers/metrics/utils'
21
23
 
22
24
  const LEGEND_PADDING = 36
23
25
 
@@ -36,7 +38,6 @@ export interface LegendProps {
36
38
  interactionLabel: string
37
39
  }
38
40
 
39
- /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
40
41
  const Legend: React.FC<LegendProps> = forwardRef(
41
42
  (
42
43
  {
@@ -72,6 +73,7 @@ const Legend: React.FC<LegendProps> = forwardRef(
72
73
 
73
74
  const { HighLightedBarUtils } = useHighlightedBars(config)
74
75
  let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
76
+
75
77
  if (!legend) return null
76
78
  return (
77
79
  <aside
@@ -140,23 +142,34 @@ const Legend: React.FC<LegendProps> = forwardRef(
140
142
  onKeyDown={e => {
141
143
  if (e.key === 'Enter') {
142
144
  e.preventDefault()
143
- publishAnalyticsEvent(
144
- `chart_legend_item_toggled--${legend.behavior}-mode`,
145
- 'keydown',
146
- `${interactionLabel}|${label.text}`,
147
- 'chart'
148
- )
145
+ publishAnalyticsEvent({
146
+ vizType: config?.type,
147
+ vizSubType: getVizSubType(config),
148
+ vizTitle: getVizTitle(config),
149
+ eventType: `chart_legend_item_toggled` as any,
150
+ eventAction: 'keydown',
151
+ eventLabel: interactionLabel,
152
+ specifics: config.visualizationType === 'Bar'
153
+ ? `label: ${label.text}, orientation: ${config.orientation === 'horizontal' ? 'horizontal' : 'vertical'}, mode: ${legend.behavior}`
154
+ : `label: ${label.text}, mode: ${legend.behavior}`,
155
+ })
149
156
  highlight(label)
150
157
  }
151
158
  }}
152
159
  onClick={e => {
153
160
  e.preventDefault()
154
- publishAnalyticsEvent(
155
- `chart_legend_item_toggled--${legend.behavior}-mode`,
156
- 'click',
157
- `${interactionLabel}|${label.text}`,
158
- 'chart'
159
- )
161
+ publishAnalyticsEvent({
162
+ vizType: config?.type,
163
+ vizSubType: getVizSubType(config),
164
+ eventType: `chart_legend_item_toggled` as any,
165
+ eventAction: 'click',
166
+ eventLabel: interactionLabel,
167
+ specifics: config.visualizationType === 'Bar'
168
+ ? `label: ${label.text}, orientation: ${config.orientation === 'horizontal' ? 'horizontal' : 'vertical'}, mode: ${legend.behavior}`
169
+ : `label: ${label.text}, mode: ${legend.behavior}`,
170
+
171
+ vizTitle: getVizTitle(config)
172
+ })
160
173
  highlight(label)
161
174
  }}
162
175
  role='button'
@@ -221,6 +234,86 @@ const Legend: React.FC<LegendProps> = forwardRef(
221
234
  </div>
222
235
 
223
236
  <LegendSuppression config={config} isLegendBottom={isLegendBottom} />
237
+
238
+ {/* Pattern Legend Items */}
239
+ {config.legend.patterns && Object.keys(config.legend.patterns).length > 0 && (
240
+ <div
241
+ className={`legend-patterns d-flex ${['top', 'bottom'].includes(config.legend.position) ? 'flex-row flex-wrap' : 'flex-column'
242
+ }`}
243
+ >
244
+ {Object.entries(config.legend.patterns).map(([key, pattern]) => {
245
+ const patternId = `legend-pattern-${key}`
246
+ const size = config.legend.patternSize || 8
247
+ const legendSize = 16
248
+ const pColor = (pattern as any)?.color || '#666666'
249
+
250
+ return (
251
+ <LegendItem
252
+ key={patternId}
253
+ className='legend-item legend-item--pattern d-flex align-items-center'
254
+ tabIndex={0}
255
+ role='button'
256
+ >
257
+ <span className='me-2'>
258
+ <svg width={legendSize} height={legendSize}>
259
+ <defs>
260
+ {pattern.shape === 'circles' && (
261
+ <PatternCircles
262
+ id={patternId}
263
+ height={size}
264
+ width={size}
265
+ fill={pColor}
266
+ radius={1.25}
267
+ />
268
+ )}
269
+ {pattern.shape === 'lines' && (
270
+ <PatternLines
271
+ id={patternId}
272
+ height={size}
273
+ width={size}
274
+ stroke={pColor}
275
+ strokeWidth={0.75}
276
+ orientation={['horizontal']}
277
+ />
278
+ )}
279
+ {pattern.shape === 'diagonalLines' && (
280
+ <PatternLines
281
+ id={patternId}
282
+ height={size}
283
+ width={size}
284
+ stroke={pColor}
285
+ strokeWidth={0.75}
286
+ orientation={['diagonalRightToLeft']}
287
+ />
288
+ )}
289
+ {pattern.shape === 'waves' && (
290
+ <PatternWaves
291
+ id={patternId}
292
+ height={size}
293
+ width={size}
294
+ fill={pColor}
295
+ strokeWidth={0.25}
296
+ />
297
+ )}
298
+ </defs>
299
+ <circle
300
+ fill={`url(#${patternId})`}
301
+ r={legendSize / 2}
302
+ cx={legendSize / 2}
303
+ cy={legendSize / 2}
304
+ stroke='#0000004d'
305
+ strokeWidth={1}
306
+ />
307
+ </svg>
308
+ </span>
309
+ <LegendLabel align='left' className='m-0'>
310
+ {parse(String((pattern as any)?.label || key))}
311
+ </LegendLabel>
312
+ </LegendItem>
313
+ )
314
+ })}
315
+ </div>
316
+ )}
224
317
  </>
225
318
  )
226
319
  }}
@@ -1,171 +1,230 @@
1
- import { colorPalettesChart as colorPalettes, sequentialPalettes, twoColorPalette } from '@cdc/core/data/colorPalettes'
2
- import { FaStar } from 'react-icons/fa'
3
- import { Label } from '../../../types/Label'
4
- import { ColorScale, TransformedData } from '../../../types/ChartContext'
5
- import { ChartConfig } from '../../../types/ChartConfig'
6
- import _ from 'lodash'
7
-
8
- export const createFormatLabels =
9
- (config: ChartConfig, tableData: Object[], data: TransformedData[], colorScale: ColorScale) =>
10
- (defaultLabels: Label[]): Label[] => {
11
- const { visualizationType, visualizationSubType, series, runtime, legend } = config
12
- const sortVertical = labels =>
13
- legend.verticalSorted
14
- ? _.sortBy(_.cloneDeep(labels), label => {
15
- const match = label.datum?.match(/-?\d+(\.\d+)?/)
16
- return match ? parseFloat(match[0]) : Number.MAX_SAFE_INTEGER
17
- })
18
- : labels
19
- const reverseLabels = labels => {
20
- if (config.series.some(series => series.dynamicCategory)) {
21
- return orderDynamicLabels(labels)
22
- }
23
-
24
- return config.legend.reverseLabelOrder ? sortVertical(labels).reverse() : sortVertical(labels)
25
- }
26
-
27
- const orderDynamicLabels = labels => {
28
- // Handle different ordering configurations
29
- switch (config.legend.order) {
30
- case 'dataColumn':
31
- return labels
32
- case 'asc':
33
- case 'desc':
34
- return labels.sort((a, b) => {
35
- const valA = a.datum || a.text
36
- const valB = b.datum || b.text
37
- const numA = parseFloat(valA)
38
- const numB = parseFloat(valB)
39
- if (!isNaN(numA) && !isNaN(numB)) {
40
- return config.legend.order === 'asc' ? numA - numB : numB - numA
41
- } else {
42
- return config.legend.order === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA)
43
- }
44
- })
45
-
46
- default:
47
- return labels // Default case to handle any unexpected config.legend.order values
48
- }
49
- }
50
- const colorCode = config.legend?.colorCode
51
- if (visualizationType === 'Deviation Bar') {
52
- const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
53
- const labelBelow = {
54
- datum: 'X',
55
- index: 0,
56
- text: `Below ${config.xAxis.targetLabel}`,
57
- value: belowColor
58
- }
59
- const labelAbove = {
60
- datum: 'X',
61
- index: 1,
62
- text: `Above ${config.xAxis.targetLabel}`,
63
- value: aboveColor
64
- }
65
-
66
- return reverseLabels([labelBelow, labelAbove])
67
- }
68
- if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
69
- let palette = colorPalettes[config.palette]
70
-
71
- while (tableData.length > palette.length) {
72
- palette = palette.concat(palette)
73
- }
74
- palette = palette.slice(0, data.length)
75
- //store unique values to Set by colorCode
76
- const set = new Set()
77
-
78
- tableData.forEach(d => set.add(d[colorCode]))
79
-
80
- // create labels with unique values
81
- const uniqueLabels = Array.from(set).map((val, i) => {
82
- const newLabel = {
83
- datum: val,
84
- index: i,
85
- text: val,
86
- value: palette[i]
87
- }
88
- return newLabel
89
- })
90
-
91
- return reverseLabels(uniqueLabels)
92
- }
93
-
94
- // get forecasting items inside of combo
95
- if (runtime?.forecastingSeriesKeys?.length > 0) {
96
- let seriesLabels = []
97
-
98
- //store unique values to Set by colorCode
99
- // loop through each stage/group/area on the chart and create a label
100
- config.runtime?.forecastingSeriesKeys?.map((outerGroup, index) => {
101
- return outerGroup?.stages?.map((stage, index) => {
102
- let colorValue = sequentialPalettes[stage.color]?.[2]
103
- ? sequentialPalettes[stage.color]?.[2]
104
- : colorPalettes[stage.color]?.[2]
105
- ? colorPalettes[stage.color]?.[2]
106
- : '#ccc'
107
-
108
- const newLabel = {
109
- datum: stage.key,
110
- index: index,
111
- text: stage.key,
112
- value: colorValue
113
- }
114
-
115
- seriesLabels.push(newLabel)
116
- })
117
- })
118
-
119
- // loop through bars for now to meet requirements.
120
- config.runtime.barSeriesKeys &&
121
- config.runtime.barSeriesKeys.forEach((bar, index) => {
122
- let colorValue = colorPalettes[config.palette][index] ? colorPalettes[config.palette][index] : '#ccc'
123
-
124
- const newLabel = {
125
- datum: bar,
126
- index: index,
127
- text: bar,
128
- value: colorValue
129
- }
130
-
131
- seriesLabels.push(newLabel)
132
- })
133
-
134
- return reverseLabels(seriesLabels)
135
- }
136
-
137
- if (config.series.some(item => item.name)) {
138
- const uniqueLabels = Array.from(new Set(config.series.map(d => d.name || d.dataKey))).map((val, i) => ({
139
- datum: val,
140
- index: i,
141
- text: val,
142
- value: colorScale(val)
143
- }))
144
- return reverseLabels(uniqueLabels)
145
- }
146
-
147
- if (
148
- (config.visualizationType === 'Bar' || config.visualizationType === 'Combo') &&
149
- config.visualizationSubType === 'regular' &&
150
- config.suppressedData
151
- ) {
152
- const lastIndex = defaultLabels.length - 1
153
- let newLabels = []
154
-
155
- config.suppressedData?.forEach(({ label, icon }, index) => {
156
- if (label && icon) {
157
- const newLabel = {
158
- datum: label,
159
- index: lastIndex + index,
160
- text: label,
161
- icon: <FaStar color='#000' size={15} />
162
- }
163
- newLabels.push(newLabel)
164
- }
165
- })
166
-
167
- return [...defaultLabels, ...newLabels]
168
- }
169
-
170
- return reverseLabels(defaultLabels)
171
- }
1
+ import {
2
+ colorPalettesChart as colorPalettes,
3
+ colorPalettesChartV2,
4
+ sequentialPalettes,
5
+ twoColorPalette
6
+ } from '@cdc/core/data/colorPalettes'
7
+ import { getCurrentPaletteName, getFallbackColorPalette, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
8
+ import { chartPaletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
9
+ import { getPaletteAccessor } from '@cdc/core/helpers/getPaletteAccessor'
10
+ import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
11
+ import { isV1Palette } from '@cdc/core/helpers/palettes/utils'
12
+ import { v2ColorDistribution } from '@cdc/core/helpers/palettes/colorDistributions'
13
+ import { updatePaletteNames } from '@cdc/core/helpers/updatePaletteNames'
14
+ import { buildForecastPaletteMappings } from '../../../helpers/buildForecastPaletteMappings'
15
+ import { FaStar } from 'react-icons/fa'
16
+ import { Label } from '../../../types/Label'
17
+ import { ColorScale, TransformedData } from '../../../types/ChartContext'
18
+ import { ChartConfig } from '../../../types/ChartConfig'
19
+ import _ from 'lodash'
20
+
21
+ export const createFormatLabels =
22
+ (config: ChartConfig, tableData: Object[], data: TransformedData[], colorScale: ColorScale) =>
23
+ (defaultLabels: Label[]): Label[] => {
24
+ const { visualizationType, visualizationSubType, series, runtime, legend } = config
25
+ const sortVertical = labels =>
26
+ legend.verticalSorted
27
+ ? _.sortBy(_.cloneDeep(labels), label => {
28
+ const match = label.datum?.match(/-?\d+(\.\d+)?/)
29
+ return match ? parseFloat(match[0]) : Number.MAX_SAFE_INTEGER
30
+ })
31
+ : labels
32
+ const reverseLabels = labels => {
33
+ if (config.series.some(series => series.dynamicCategory)) {
34
+ return orderDynamicLabels(labels)
35
+ }
36
+
37
+ return config.legend.reverseLabelOrder ? sortVertical(labels).reverse() : sortVertical(labels)
38
+ }
39
+
40
+ const orderDynamicLabels = labels => {
41
+ // Handle different ordering configurations
42
+ switch (config.legend.order) {
43
+ case 'dataColumn':
44
+ return labels
45
+ case 'asc':
46
+ case 'desc':
47
+ return labels.sort((a, b) => {
48
+ const valA = a.datum || a.text
49
+ const valB = b.datum || b.text
50
+ const numA = parseFloat(valA)
51
+ const numB = parseFloat(valB)
52
+ if (!isNaN(numA) && !isNaN(numB)) {
53
+ return config.legend.order === 'asc' ? numA - numB : numB - numA
54
+ } else {
55
+ return config.legend.order === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA)
56
+ }
57
+ })
58
+
59
+ default:
60
+ return labels // Default case to handle any unexpected config.legend.order values
61
+ }
62
+ }
63
+ const colorCode = config.legend?.colorCode
64
+ if (visualizationType === 'Deviation Bar') {
65
+ let versionName = isV1Palette(config) ? 'v1' : 'v2'
66
+ const [belowColor, aboveColor] = twoColorPalette?.[versionName]?.[config.twoColor.palette] || [
67
+ '#1D6ABF',
68
+ '#935586'
69
+ ]
70
+
71
+ const labelBelow = {
72
+ datum: 'X',
73
+ index: 0,
74
+ text: `Below ${config.xAxis.targetLabel}`,
75
+ value: belowColor
76
+ }
77
+ const labelAbove = {
78
+ datum: 'X',
79
+ index: 1,
80
+ text: `Above ${config.xAxis.targetLabel}`,
81
+ value: aboveColor
82
+ }
83
+
84
+ return reverseLabels([labelBelow, labelAbove])
85
+ }
86
+ if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
87
+ const currentPaletteName = getCurrentPaletteName(config) || getFallbackColorPalette(config)
88
+ const paletteName = migratePaletteWithMap(currentPaletteName, chartPaletteMigrationMap, true)
89
+ let palette = getPaletteAccessor(colorPalettes, config, paletteName)
90
+
91
+ const numberOfKeys = data.length
92
+
93
+ // Check if we should use v2 distribution logic for better contrast
94
+ const version = getColorPaletteVersion(config)
95
+ const isSequentialOrDivergent =
96
+ paletteName && (paletteName.includes('sequential') || paletteName.includes('divergent'))
97
+ const isPairedBarOrDeviation = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
98
+ const useV2Distribution =
99
+ version === 2 && isSequentialOrDivergent && palette.length === 9 && numberOfKeys <= 9 && !isPairedBarOrDeviation
100
+
101
+ if (useV2Distribution && v2ColorDistribution[numberOfKeys]) {
102
+ // Use strategic color distribution for v2 sequential palettes
103
+ const distributionIndices = v2ColorDistribution[numberOfKeys]
104
+ palette = distributionIndices.map(index => palette[index])
105
+ } else {
106
+ // Use existing logic for v1 palettes and other cases
107
+ while (tableData.length > palette?.length) {
108
+ palette = palette.concat(palette)
109
+ }
110
+ palette = palette?.slice(0, data.length)
111
+ }
112
+ //store unique values to Set by colorCode
113
+ const set = new Set()
114
+
115
+ tableData.forEach(d => set.add(d[colorCode]))
116
+
117
+ // create labels with unique values
118
+ const uniqueLabels = Array.from(set).map((val, i) => {
119
+ const newLabel = {
120
+ datum: val,
121
+ index: i,
122
+ text: val,
123
+ value: palette?.[i]
124
+ }
125
+ return newLabel
126
+ })
127
+
128
+ return reverseLabels(uniqueLabels)
129
+ }
130
+
131
+ // get forecasting items inside of combo
132
+ if (runtime?.forecastingSeriesKeys?.length > 0) {
133
+ let seriesLabels = []
134
+
135
+ // Create palette lookup map - use version-specific palettes
136
+ // Forecasting charts use sequentialPalettes for v1, sequential-only palettes for v2
137
+ const paletteVersion = getColorPaletteVersion(config)
138
+
139
+ let forecastPalettes
140
+ if (paletteVersion === 1) {
141
+ // V1: Use original sequential palettes
142
+ forecastPalettes = sequentialPalettes
143
+ } else {
144
+ // V2: Only use sequential palettes (filter out divergent and qualitative)
145
+ const allV2Palettes = colorPalettesChartV2
146
+ forecastPalettes = {}
147
+ Object.keys(allV2Palettes).forEach(key => {
148
+ if (key.startsWith('sequential')) {
149
+ forecastPalettes[key] = allV2Palettes[key]
150
+ }
151
+ })
152
+ }
153
+
154
+ const processedPalettes = updatePaletteNames(forecastPalettes)
155
+ const forecastingPalettes = buildForecastPaletteMappings(processedPalettes, paletteVersion)
156
+
157
+ //store unique values to Set by colorCode
158
+ // loop through each stage/group/area on the chart and create a label
159
+ config.runtime?.forecastingSeriesKeys?.map((outerGroup, index) => {
160
+ return outerGroup?.stages?.map((stage, index) => {
161
+ const palette = forecastingPalettes[stage.color] || false
162
+ let colorValue = palette?.[2] || '#ccc'
163
+
164
+ const newLabel = {
165
+ datum: stage.key,
166
+ index: index,
167
+ text: stage.key,
168
+ value: colorValue
169
+ }
170
+
171
+ seriesLabels.push(newLabel)
172
+ })
173
+ })
174
+
175
+ // loop through bars for now to meet requirements.
176
+ config.runtime.barSeriesKeys &&
177
+ config.runtime.barSeriesKeys.forEach((bar, index) => {
178
+ const currentPaletteName = getCurrentPaletteName(config) || getFallbackColorPalette(config)
179
+ const migratedPaletteName = migratePaletteWithMap(currentPaletteName, chartPaletteMigrationMap, true)
180
+ const palette = getPaletteAccessor(colorPalettes, config, migratedPaletteName)
181
+ let colorValue = palette?.[index] || '#ccc'
182
+
183
+ const newLabel = {
184
+ datum: bar,
185
+ index: index,
186
+ text: bar,
187
+ value: colorValue
188
+ }
189
+
190
+ seriesLabels.push(newLabel)
191
+ })
192
+
193
+ return reverseLabels(seriesLabels)
194
+ }
195
+
196
+ if (config.series.some(item => item.name)) {
197
+ const uniqueLabels = Array.from(new Set(config.series.map(d => d.name || d.dataKey))).map((val, i) => ({
198
+ datum: val,
199
+ index: i,
200
+ text: val,
201
+ value: colorScale(val)
202
+ }))
203
+ return reverseLabels(uniqueLabels)
204
+ }
205
+
206
+ if (
207
+ (config.visualizationType === 'Bar' || config.visualizationType === 'Combo') &&
208
+ config.visualizationSubType === 'regular' &&
209
+ config.suppressedData
210
+ ) {
211
+ const lastIndex = defaultLabels.length - 1
212
+ let newLabels = []
213
+
214
+ config.suppressedData?.forEach(({ label, icon }, index) => {
215
+ if (label && icon) {
216
+ const newLabel = {
217
+ datum: label,
218
+ index: lastIndex + index,
219
+ text: label,
220
+ icon: <FaStar color='#000' size={15} />
221
+ }
222
+ newLabels.push(newLabel)
223
+ }
224
+ })
225
+
226
+ return [...defaultLabels, ...newLabels]
227
+ }
228
+
229
+ return reverseLabels(defaultLabels)
230
+ }
@@ -22,7 +22,7 @@ const LegendWrapper: React.FC<LegendWrapperProps> = props => {
22
22
  return classes.join(' ')
23
23
  }
24
24
 
25
- return <div className={getLegendWrappingClasses()}>{...children}</div>
25
+ return <div className={getLegendWrappingClasses()}>{children}</div>
26
26
  }
27
27
 
28
28
  export default LegendWrapper
@@ -105,7 +105,7 @@ const LineChartCircle = (props: LineChartCircleProps) => {
105
105
  <g
106
106
  transform={transformShape(pointData[config.xAxis.dataKey], pointData[filtered?.dataKey])}
107
107
  className={`visx-glyph-group${displayArea ? '' : '-hidden'}`}
108
- data-seriesIndex={seriesIndex}
108
+ data-seriesindex={seriesIndex}
109
109
  >
110
110
  <Shape
111
111
  fillOpacity={mode === 'ALWAYS_SHOW_POINTS' ? 1 : 0}
@@ -142,7 +142,7 @@ const LineChartCircle = (props: LineChartCircleProps) => {
142
142
  <g
143
143
  transform={transformShape(pointData[config.xAxis?.dataKey], pointData[filtered?.dataKey])}
144
144
  className={`visx-glyph-group${displayArea ? '' : '-hidden'}`}
145
- data-seriesIndex={seriesIndex}
145
+ data-seriesindex={seriesIndex}
146
146
  >
147
147
  <Shape size={dotSize} stroke={color} fill={color} />
148
148
  </g>
@@ -228,10 +228,10 @@ const LineChart = (props: LineChartProps) => {
228
228
  />
229
229
 
230
230
  {suppressedSegments.map((segment, index) => {
231
- return Object.entries(segment.data).map(([key, value]) => {
231
+ return Object.entries(segment.data).map(([key, value], entryIndex) => {
232
232
  return (
233
233
  <LinePath
234
- key={index}
234
+ key={`${index}-${key}-${entryIndex}`}
235
235
  data={value}
236
236
  x={d => xPos(d)}
237
237
  y={d =>