@cdc/chart 4.25.10 → 4.26.1

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 (135) hide show
  1. package/dist/{cdcchart-1a1724a1.es.js → cdcchart-dgT_1dIT.es.js} +136 -151
  2. package/dist/cdcchart.js +44003 -43518
  3. package/examples/feature/__data__/planet-example-data.json +1 -1
  4. package/examples/feature/boxplot/valid-boxplot.csv +38 -17
  5. package/examples/feature/pie/planet-pie-example-config.json +48 -2
  6. package/examples/private/DEV-11825.json +573 -0
  7. package/examples/private/DEV-12100.json +1303 -0
  8. package/examples/private/cat-y.json +1235 -0
  9. package/examples/private/data-points.json +228 -0
  10. package/examples/private/height.json +3915 -0
  11. package/examples/private/links.json +569 -0
  12. package/examples/private/na.json +913 -0
  13. package/examples/private/quadrant.txt +30 -0
  14. package/examples/private/test-data.csv +28 -0
  15. package/examples/private/test-forecast.json +5510 -0
  16. package/examples/private/warming-stripe-test.json +2578 -0
  17. package/examples/private/warming-stripes.json +4763 -0
  18. package/examples/tech-adoption-with-links.json +560 -0
  19. package/index.html +16 -140
  20. package/package.json +6 -5
  21. package/preview.html +1616 -0
  22. package/src/CdcChart.tsx +8 -11
  23. package/src/CdcChartComponent.tsx +329 -124
  24. package/src/_stories/Chart.Combo.stories.tsx +18 -0
  25. package/src/_stories/Chart.Forecast.stories.tsx +36 -0
  26. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +520 -0
  27. package/src/_stories/Chart.Patterns.stories.tsx +2 -1
  28. package/src/_stories/Chart.PreserveDecimals.stories.tsx +220 -0
  29. package/src/_stories/Chart.Regions.Categorical.stories.tsx +148 -0
  30. package/src/_stories/Chart.Regions.DateScale.stories.tsx +197 -0
  31. package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +297 -0
  32. package/src/_stories/Chart.SmallMultiples.stories.tsx +47 -0
  33. package/src/_stories/Chart.stories.tsx +8 -0
  34. package/src/_stories/ChartAnnotation.stories.tsx +6 -3
  35. package/src/_stories/ChartBar.Editor.stories.tsx +3585 -0
  36. package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
  37. package/src/_stories/ChartBrush.stories.tsx +50 -0
  38. package/src/_stories/ChartEditor.Editor.stories.tsx +656 -0
  39. package/src/_stories/ChartEditor.stories.tsx +1 -2
  40. package/src/_stories/TechAdoptionWithLinks.stories.tsx +27 -0
  41. package/src/_stories/_mock/brush_enabled.json +326 -0
  42. package/src/_stories/_mock/brush_mock.json +2 -69
  43. package/src/_stories/_mock/combo.json +451 -0
  44. package/src/_stories/_mock/editor-test-configs.json +376 -0
  45. package/src/_stories/_mock/editor-test-datasets.json +477 -0
  46. package/src/_stories/_mock/editor-tests/bar-chart-editor-test.json +255 -0
  47. package/src/_stories/_mock/editor-tests/bar-chart-general-test.json +267 -0
  48. package/src/_stories/_mock/editor-tests/bar-chart-test.json +237 -0
  49. package/src/_stories/_mock/forecast_combo_with_gaps.json +913 -0
  50. package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
  51. package/src/_stories/_mock/pie_config.json +257 -62
  52. package/src/_stories/_mock/small_multiples/small_multiples_bars.json +1944 -0
  53. package/src/_stories/_mock/small_multiples/small_multiples_big_data_bars.json +1114 -0
  54. package/src/_stories/_mock/small_multiples/small_multiples_lines.json +2646 -0
  55. package/src/_stories/_mock/small_multiples/small_multiples_lines_colors.json +1305 -0
  56. package/src/_stories/_mock/small_multiples/small_multiples_stacked_bars.json +1936 -0
  57. package/src/components/Annotations/components/findNearestDatum.ts +6 -41
  58. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -7
  59. package/src/components/AreaChart/index.tsx +1 -2
  60. package/src/components/Axis/Categorical.Axis.tsx +6 -7
  61. package/src/components/BarChart/components/BarChart.Horizontal.tsx +181 -27
  62. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
  63. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
  64. package/src/components/BarChart/components/BarChart.Vertical.tsx +8 -9
  65. package/src/components/BarChart/components/context.tsx +1 -0
  66. package/src/components/BarChart/helpers/useBarChart.ts +14 -2
  67. package/src/components/BoxPlot/helpers/index.ts +3 -3
  68. package/src/components/Brush/BrushSelector.tsx +1258 -0
  69. package/src/components/Brush/MiniChartPreview.tsx +283 -0
  70. package/src/components/DeviationBar.jsx +9 -7
  71. package/src/components/EditorPanel/EditorPanel.tsx +2720 -2586
  72. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +96 -111
  73. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
  74. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +76 -31
  75. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +104 -55
  76. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +54 -49
  77. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +427 -0
  78. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +96 -48
  79. package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
  80. package/src/components/EditorPanel/editor-panel.scss +0 -20
  81. package/src/components/EditorPanel/useEditorPermissions.ts +36 -31
  82. package/src/components/Forecasting/Forecasting.tsx +139 -21
  83. package/src/components/Legend/Legend.Component.tsx +16 -9
  84. package/src/components/Legend/Legend.tsx +3 -2
  85. package/src/components/Legend/helpers/createFormatLabels.tsx +325 -176
  86. package/src/components/Legend/helpers/getLegendClasses.ts +0 -1
  87. package/src/components/Legend/helpers/index.ts +10 -6
  88. package/src/components/LineChart/LineChartProps.ts +0 -3
  89. package/src/components/LineChart/helpers.ts +1 -1
  90. package/src/components/LineChart/index.tsx +36 -13
  91. package/src/components/LinearChart.tsx +559 -499
  92. package/src/components/PairedBarChart.jsx +20 -3
  93. package/src/components/Regions/components/Regions.tsx +366 -144
  94. package/src/components/Sankey/types/index.ts +1 -1
  95. package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
  96. package/src/components/SmallMultiples/SmallMultipleTile.tsx +202 -0
  97. package/src/components/SmallMultiples/SmallMultiples.css +32 -0
  98. package/src/components/SmallMultiples/SmallMultiples.tsx +271 -0
  99. package/src/components/SmallMultiples/index.ts +2 -0
  100. package/src/components/WarmingStripes/WarmingStripes.tsx +160 -0
  101. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
  102. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
  103. package/src/components/WarmingStripes/index.tsx +3 -0
  104. package/src/data/initial-state.js +16 -2
  105. package/src/helpers/buildForecastPaletteOptions.ts +0 -38
  106. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
  107. package/src/helpers/getColorScale.ts +10 -0
  108. package/src/{hooks/useMinMax.ts → helpers/getMinMax.ts} +26 -14
  109. package/src/helpers/getYAxisAutoPadding.ts +53 -0
  110. package/src/helpers/sizeHelpers.ts +0 -20
  111. package/src/helpers/smallMultiplesHelpers.ts +529 -0
  112. package/src/hooks/useChartHoverAnalytics.tsx +10 -9
  113. package/src/hooks/useProgrammaticTooltip.ts +96 -0
  114. package/src/hooks/useScales.ts +98 -34
  115. package/src/hooks/useSmallMultipleSynchronization.ts +59 -0
  116. package/src/hooks/useTooltip.tsx +91 -25
  117. package/src/scss/DataTable.scss +0 -4
  118. package/src/scss/main.scss +18 -83
  119. package/src/store/chart.actions.ts +2 -0
  120. package/src/store/chart.reducer.ts +4 -0
  121. package/src/test/CdcChart.test.jsx +1 -1
  122. package/src/types/ChartConfig.ts +27 -6
  123. package/src/types/ChartContext.ts +3 -0
  124. package/src/types/Label.ts +1 -0
  125. package/src/utils/analyticsTracking.ts +19 -0
  126. package/LICENSE +0 -201
  127. package/src/_stories/_mock/pie_data.json +0 -218
  128. package/src/components/AreaChart/components/AreaChart.jsx +0 -109
  129. package/src/components/Brush/BrushChart.tsx +0 -128
  130. package/src/components/Brush/BrushController.tsx +0 -71
  131. package/src/components/Brush/types.tsx +0 -8
  132. package/src/components/BrushChart.tsx +0 -223
  133. package/src/helpers/sort.ts +0 -7
  134. package/src/hooks/useActiveElement.js +0 -19
  135. package/src/hooks/useChartClasses.js +0 -41
@@ -5,226 +5,375 @@ import {
5
5
  twoColorPalette
6
6
  } from '@cdc/core/data/colorPalettes'
7
7
  import { getCurrentPaletteName, getFallbackColorPalette, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
8
- import { chartPaletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
8
+ import { chartPaletteMigrationMap, paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
9
9
  import { getPaletteAccessor } from '@cdc/core/helpers/getPaletteAccessor'
10
10
  import { getColorPaletteVersion } from '@cdc/core/helpers/getColorPaletteVersion'
11
11
  import { isV1Palette } from '@cdc/core/helpers/palettes/utils'
12
12
  import { v2ColorDistribution } from '@cdc/core/helpers/palettes/colorDistributions'
13
13
  import { updatePaletteNames } from '@cdc/core/helpers/updatePaletteNames'
14
14
  import { buildForecastPaletteMappings } from '../../../helpers/buildForecastPaletteMappings'
15
+ import { getFullColorPalette } from '../../../helpers/smallMultiplesHelpers'
15
16
  import { FaStar } from 'react-icons/fa'
16
17
  import { Label } from '../../../types/Label'
17
18
  import { ColorScale, TransformedData } from '../../../types/ChartContext'
18
19
  import { ChartConfig } from '../../../types/ChartConfig'
19
20
  import _ from 'lodash'
21
+ import { scaleSequential } from 'd3-scale'
22
+ import { interpolateRgbBasis } from 'd3-interpolate'
23
+ import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
20
24
 
21
25
  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
- }
26
+ (
27
+ config: ChartConfig,
28
+ tableData: Object[],
29
+ data: TransformedData[],
30
+ colorScale: ColorScale,
31
+ formatNumber: (value: any, axis: string) => string
32
+ ) =>
33
+ (defaultLabels: Label[]): Label[] => {
34
+ const { visualizationType, visualizationSubType, series, runtime, legend } = config
36
35
 
37
- return config.legend.reverseLabelOrder ? sortVertical(labels).reverse() : sortVertical(labels)
38
- }
36
+ // Handle small multiples legend adjustments
37
+ // by-series + same: all tiles use same color, legend should show one color
38
+ if (config.smallMultiples?.mode === 'by-series' && config.smallMultiples?.colorMode === 'same') {
39
+ const baseColor = colorScale?.range()?.[0]
40
+ return defaultLabels.map((label, i) => ({
41
+ ...label,
42
+ value: baseColor
43
+ }))
44
+ }
39
45
 
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
- }
46
+ // by-column + different: each tile gets different color, legend should show tile values with their colors
47
+ if (config.smallMultiples?.mode === 'by-column' && config.smallMultiples?.colorMode === 'different') {
48
+ const tileColumn = config.smallMultiples.tileColumn
49
+ const tileValues = Array.from(new Set(data.map(d => d[tileColumn])))
50
+ .filter(Boolean)
51
+ .sort()
52
+ const tilePalette = getFullColorPalette(config, tileValues.length)
53
+
54
+ return tileValues.map((value, index) => ({
55
+ datum: value,
56
+ index,
57
+ text: config.smallMultiples.tileTitles?.[value] || String(value),
58
+ value: tilePalette[index]
59
+ }))
60
+ }
61
+
62
+ // Handle Warming Stripes legend
63
+ if (visualizationType === 'Warming Stripes') {
64
+ const valueKey = runtime.seriesKeys?.[0]
65
+ if (!valueKey || !data || data.length === 0) {
66
+ return []
62
67
  }
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'
68
+
69
+ // Calculate min and max values
70
+ const values = data.map(d => Number(d[valueKey])).filter(v => !isNaN(v))
71
+ const minValue = Math.min(...values)
72
+ const maxValue = Math.max(...values)
73
+
74
+ // Get the color palette from config (same logic as WarmingStripes component)
75
+ const colorPalettesFiltered = filterChartColorPalettes(config)
76
+ const configPalette = config.general?.palette?.name
77
+ const migratedPaletteName = configPalette ? configPalette : getFallbackColorPalette(config)
78
+
79
+ const isReversedPalette = migratedPaletteName?.endsWith('reverse')
80
+ const basePaletteName = isReversedPalette ? migratedPaletteName.slice(0, -7) : migratedPaletteName
81
+
82
+ let palette =
83
+ colorPalettesFiltered[migratePaletteWithMap(basePaletteName, paletteMigrationMap, false)] ||
84
+ colorPalettesFiltered[basePaletteName] ||
85
+ colorPalettesFiltered[configPalette]
86
+
87
+ if (!palette || palette.length < 2) {
88
+ palette = [
89
+ '#053061',
90
+ '#2166ac',
91
+ '#4393c3',
92
+ '#92c5de',
93
+ '#d1e5f0',
94
+ '#f7f7f7',
95
+ '#fddbc7',
96
+ '#f4a582',
97
+ '#d6604d',
98
+ '#b2182b',
99
+ '#67001f'
69
100
  ]
101
+ }
70
102
 
71
- const labelBelow = {
72
- datum: 'X',
73
- index: 0,
74
- text: `Below ${config.xAxis.targetLabel}`,
75
- value: belowColor
103
+ const shouldReverse = config.general?.palette?.isReversed || isReversedPalette
104
+ const finalPalette = shouldReverse ? [...palette].reverse() : palette
105
+ const warmingColorScale = scaleSequential(interpolateRgbBasis(finalPalette)).domain([minValue, maxValue])
106
+
107
+ // For gradient style, create smooth gradient with min/max labels only
108
+ if (legend?.style === 'gradient') {
109
+ // Create many color stops for smooth gradient (these are used for the gradient fill)
110
+ const numColorStops = 20
111
+ const colorStops = []
112
+ for (let i = 0; i < numColorStops; i++) {
113
+ const t = i / (numColorStops - 1)
114
+ const value = minValue + t * (maxValue - minValue)
115
+ colorStops.push(warmingColorScale(value))
76
116
  }
77
- const labelAbove = {
78
- datum: 'X',
79
- index: 1,
80
- text: `Above ${config.xAxis.targetLabel}`,
81
- value: aboveColor
117
+
118
+ // Create multiple stops for proper spacing, but only show labels at first and last
119
+ // This ensures the first label appears at the left edge and last at the right edge
120
+ const numPositions = 5 // Number of tick positions for spacing
121
+ const labels = []
122
+ for (let i = 0; i < numPositions; i++) {
123
+ const t = i / (numPositions - 1)
124
+ const value = minValue + t * (maxValue - minValue)
125
+ const isFirstOrLast = i === 0 || i === numPositions - 1
126
+
127
+ labels.push({
128
+ datum: String(value),
129
+ index: i,
130
+ text: isFirstOrLast ? formatNumber(value, 'left') : '',
131
+ value: colorStops[Math.floor(t * (numColorStops - 1))],
132
+ colors: colorStops
133
+ })
82
134
  }
83
135
 
84
- return reverseLabels([labelBelow, labelAbove])
136
+ return labels
85
137
  }
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
138
 
115
- tableData.forEach(d => set.add(d[colorCode]))
139
+ // For interval style, create ranges
140
+ const numIntervals = legend?.warmingStripesIntervals || 5
141
+ const range = maxValue - minValue
142
+ const intervalSize = range / numIntervals
116
143
 
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
144
+ const intervalLabels = []
145
+
146
+ for (let i = 0; i < numIntervals; i++) {
147
+ // Calculate interval boundaries
148
+ // Each interval after the first starts at the exact boundary point
149
+ const start = minValue + i * intervalSize
150
+ const end = i === numIntervals - 1 ? maxValue : minValue + (i + 1) * intervalSize
151
+ const midPoint = (start + end) / 2
152
+
153
+ // For display, show the actual start for first interval
154
+ // For subsequent intervals, format the boundary point
155
+ const displayStart = start
156
+ const displayEnd = i === numIntervals - 1 ? end : end
157
+
158
+ intervalLabels.push({
159
+ datum: String(midPoint),
160
+ index: i,
161
+ text:
162
+ i === 0
163
+ ? `${formatNumber(displayStart, 'left')} - < ${formatNumber(displayEnd, 'left')}`
164
+ : i === numIntervals - 1
165
+ ? `${formatNumber(displayStart, 'left')} - ${formatNumber(displayEnd, 'left')}`
166
+ : `${formatNumber(displayStart, 'left')} - < ${formatNumber(displayEnd, 'left')}`,
167
+ value: warmingColorScale(midPoint)
126
168
  })
169
+ }
127
170
 
128
- return reverseLabels(uniqueLabels)
171
+ return intervalLabels
172
+ }
173
+
174
+ const sortVertical = labels =>
175
+ legend.verticalSorted
176
+ ? _.sortBy(_.cloneDeep(labels), label => {
177
+ const match = label.datum?.match(/-?\d+(\.\d+)?/)
178
+ return match ? parseFloat(match[0]) : Number.MAX_SAFE_INTEGER
179
+ })
180
+ : labels
181
+ const reverseLabels = labels => {
182
+ if (config.series.some(series => series.dynamicCategory)) {
183
+ return orderDynamicLabels(labels)
129
184
  }
130
185
 
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]
186
+ return config.legend.reverseLabelOrder ? sortVertical(labels).reverse() : sortVertical(labels)
187
+ }
188
+
189
+ const orderDynamicLabels = labels => {
190
+ // Handle different ordering configurations
191
+ switch (config.legend.order) {
192
+ case 'dataColumn':
193
+ return labels
194
+ case 'asc':
195
+ case 'desc':
196
+ return labels.sort((a, b) => {
197
+ const valA = a.datum || a.text
198
+ const valB = b.datum || b.text
199
+ const numA = parseFloat(valA)
200
+ const numB = parseFloat(valB)
201
+ if (!isNaN(numA) && !isNaN(numB)) {
202
+ return config.legend.order === 'asc' ? numA - numB : numB - numA
203
+ } else {
204
+ return config.legend.order === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA)
150
205
  }
151
206
  })
152
- }
153
207
 
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
- }
208
+ default:
209
+ return labels // Default case to handle any unexpected config.legend.order values
210
+ }
211
+ }
212
+ const colorCode = config.legend?.colorCode
213
+ if (visualizationType === 'Deviation Bar') {
214
+ let versionName = isV1Palette(config) ? 'v1' : 'v2'
215
+ const [belowColor, aboveColor] = twoColorPalette?.[versionName]?.[config.twoColor.palette] || [
216
+ '#1D6ABF',
217
+ '#935586'
218
+ ]
170
219
 
171
- seriesLabels.push(newLabel)
172
- })
173
- })
220
+ const labelBelow = {
221
+ datum: 'X',
222
+ index: 0,
223
+ text: `Below ${config.xAxis.targetLabel}`,
224
+ value: belowColor
225
+ }
226
+ const labelAbove = {
227
+ datum: 'X',
228
+ index: 1,
229
+ text: `Above ${config.xAxis.targetLabel}`,
230
+ value: aboveColor
231
+ }
174
232
 
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
- }
233
+ return reverseLabels([labelBelow, labelAbove])
234
+ }
235
+ if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
236
+ const currentPaletteName = getCurrentPaletteName(config) || getFallbackColorPalette(config)
237
+ const paletteName = migratePaletteWithMap(currentPaletteName, chartPaletteMigrationMap, true)
238
+ let palette = getPaletteAccessor(colorPalettes, config, paletteName)
189
239
 
190
- seriesLabels.push(newLabel)
191
- })
240
+ const numberOfKeys = data.length
192
241
 
193
- return reverseLabels(seriesLabels)
242
+ // Check if we should use v2 distribution logic for better contrast
243
+ const version = getColorPaletteVersion(config)
244
+ const isSequentialOrDivergent =
245
+ paletteName && (paletteName.includes('sequential') || paletteName.includes('divergent'))
246
+ const isPairedBarOrDeviation = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
247
+ const useV2Distribution =
248
+ version === 2 && isSequentialOrDivergent && palette.length === 9 && numberOfKeys <= 9 && !isPairedBarOrDeviation
249
+
250
+ if (useV2Distribution && v2ColorDistribution[numberOfKeys]) {
251
+ // Use strategic color distribution for v2 sequential palettes
252
+ const distributionIndices = v2ColorDistribution[numberOfKeys]
253
+ palette = distributionIndices.map(index => palette[index])
254
+ } else {
255
+ // Use existing logic for v1 palettes and other cases
256
+ while (tableData.length > palette?.length) {
257
+ palette = palette.concat(palette)
258
+ }
259
+ palette = palette?.slice(0, data.length)
194
260
  }
261
+ //store unique values to Set by colorCode
262
+ const set = new Set()
195
263
 
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) => ({
264
+ tableData.forEach(d => set.add(d[colorCode]))
265
+
266
+ // create labels with unique values
267
+ const uniqueLabels = Array.from(set).map((val, i) => {
268
+ const newLabel = {
198
269
  datum: val,
199
270
  index: i,
200
271
  text: val,
201
- value: colorScale(val)
202
- }))
203
- return reverseLabels(uniqueLabels)
272
+ value: palette?.[i]
273
+ }
274
+ return newLabel
275
+ })
276
+
277
+ return reverseLabels(uniqueLabels)
278
+ }
279
+
280
+ // get forecasting items inside of combo
281
+ if (runtime?.forecastingSeriesKeys?.length > 0) {
282
+ let seriesLabels = []
283
+
284
+ // Create palette lookup map - use version-specific palettes
285
+ // Forecasting charts use sequentialPalettes for v1, sequential-only palettes for v2
286
+ const paletteVersion = getColorPaletteVersion(config)
287
+
288
+ let forecastPalettes
289
+ if (paletteVersion === 1) {
290
+ // V1: Use original sequential palettes
291
+ forecastPalettes = sequentialPalettes
292
+ } else {
293
+ // V2: Only use sequential palettes (filter out divergent and qualitative)
294
+ const allV2Palettes = colorPalettesChartV2
295
+ forecastPalettes = {}
296
+ Object.keys(allV2Palettes).forEach(key => {
297
+ if (key.startsWith('sequential')) {
298
+ forecastPalettes[key] = allV2Palettes[key]
299
+ }
300
+ })
204
301
  }
205
302
 
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)
303
+ const processedPalettes = updatePaletteNames(forecastPalettes)
304
+ const forecastingPalettes = buildForecastPaletteMappings(processedPalettes, paletteVersion)
305
+
306
+ //store unique values to Set by colorCode
307
+ // loop through each stage/group/area on the chart and create a label
308
+ config.runtime?.forecastingSeriesKeys?.map((outerGroup, index) => {
309
+ return outerGroup?.stages?.map((stage, index) => {
310
+ const palette = forecastingPalettes[stage.color] || false
311
+ let colorValue = palette?.[2] || '#ccc'
312
+
313
+ const newLabel = {
314
+ datum: stage.key,
315
+ index: index,
316
+ text: stage.key,
317
+ value: colorValue
223
318
  }
319
+
320
+ seriesLabels.push(newLabel)
224
321
  })
322
+ })
225
323
 
226
- return [...defaultLabels, ...newLabels]
227
- }
324
+ // loop through bars for now to meet requirements.
325
+ config.runtime.barSeriesKeys &&
326
+ config.runtime.barSeriesKeys.forEach((bar, index) => {
327
+ const currentPaletteName = getCurrentPaletteName(config) || getFallbackColorPalette(config)
328
+ const migratedPaletteName = migratePaletteWithMap(currentPaletteName, chartPaletteMigrationMap, true)
329
+ const palette = getPaletteAccessor(colorPalettes, config, migratedPaletteName)
330
+ let colorValue = palette?.[index] || '#ccc'
331
+
332
+ const newLabel = {
333
+ datum: bar,
334
+ index: index,
335
+ text: bar,
336
+ value: colorValue
337
+ }
338
+
339
+ seriesLabels.push(newLabel)
340
+ })
228
341
 
229
- return reverseLabels(defaultLabels)
342
+ return reverseLabels(seriesLabels)
230
343
  }
344
+
345
+ if (config.series.some(item => item.name)) {
346
+ const uniqueLabels = Array.from(new Set(config.series.map(d => d.name || d.dataKey))).map((val, i) => ({
347
+ datum: val,
348
+ index: i,
349
+ text: val,
350
+ value: colorScale(val)
351
+ }))
352
+ return reverseLabels(uniqueLabels)
353
+ }
354
+
355
+ if (
356
+ (config.visualizationType === 'Bar' || config.visualizationType === 'Combo') &&
357
+ config.visualizationSubType === 'regular' &&
358
+ config.suppressedData
359
+ ) {
360
+ const lastIndex = defaultLabels.length - 1
361
+ let newLabels = []
362
+
363
+ config.suppressedData?.forEach(({ label, icon }, index) => {
364
+ if (label && icon) {
365
+ const newLabel = {
366
+ datum: label,
367
+ index: lastIndex + index,
368
+ text: label,
369
+ icon: <FaStar color='#000' size={15} />
370
+ }
371
+ newLabels.push(newLabel)
372
+ }
373
+ })
374
+
375
+ return [...defaultLabels, ...newLabels]
376
+ }
377
+
378
+ return reverseLabels(defaultLabels)
379
+ }
@@ -38,4 +38,3 @@ export const getLegendClasses = (config: ChartConfig) => {
38
38
  innerClasses
39
39
  }
40
40
  }
41
- export default getLegendClasses
@@ -2,6 +2,15 @@ export const getGradientConfig = (config, formatLabels, colorScale) => {
2
2
  const defaultValue = [{ datum: '', index: 0, text: '', value: '' }]
3
3
 
4
4
  const formatted = formatLabels(defaultValue)
5
+
6
+ // For Warming Stripes, always use the formatted labels which contain the gradient stops
7
+ if (config.visualizationType === 'Warming Stripes' && config.legend.style === 'gradient') {
8
+ // Extract the colors array from the first item (all items have the same color stops)
9
+ const colors = formatted[0]?.colors || formatted.map(label => label?.value).filter(Boolean)
10
+ const labels = formatted.map(label => label?.text || label?.datum)
11
+ return { colors, labels }
12
+ }
13
+
5
14
  const colors = config.legend.colorCode ? formatted.map(label => label?.value) : colorScale?.range() ?? []
6
15
  const labels = config.legend.colorCode
7
16
  ? formatted.map(label => label?.text || label?.datum)
@@ -19,12 +28,7 @@ export const getMarginTop = (isLegendBottom, config) => {
19
28
  if (!isLegendBottom) {
20
29
  return '0px'
21
30
  }
22
- if (isLegendBottom && config.xAxis.brushActive && !config.legend.hide) {
23
- const additiolMargin = 25
24
- return `${DEFAULT_MARGIN_TOP + config.brush?.height + additiolMargin}px`
25
- } else {
26
- return `${DEFAULT_MARGIN_TOP}px`
27
- }
31
+ return `${DEFAULT_MARGIN_TOP}px`
28
32
  }
29
33
  export const getMarginBottom = (isLegendBottom, config) => {
30
34
  const isLegendTop = config.legend?.position === 'top' && !config.legend.hide
@@ -21,9 +21,6 @@ export interface DataItem {
21
21
  [key: string]: any
22
22
  }
23
23
 
24
- export interface Config {
25
- preliminaryData: PreliminaryDataItem[] | []
26
- }
27
24
  export interface StyleProps {
28
25
  data: DataItem[]
29
26
  handleLineType: Function
@@ -210,7 +210,7 @@ const handleLastIndex = ({
210
210
  return result
211
211
  }
212
212
 
213
- export const handleMiddleIndices = ({
213
+ const handleMiddleIndices = ({
214
214
  data,
215
215
  seriesKey,
216
216
  preliminaryData,