@cdc/chart 4.25.11 → 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 (77) hide show
  1. package/dist/cdcchart.js +38898 -40013
  2. package/examples/feature/pie/planet-pie-example-config.json +48 -2
  3. package/examples/private/DEV-12100.json +1303 -0
  4. package/examples/private/cat-y.json +1235 -0
  5. package/examples/private/data-points.json +228 -0
  6. package/examples/private/height.json +3915 -0
  7. package/examples/private/links.json +569 -0
  8. package/examples/private/quadrant.txt +30 -0
  9. package/examples/private/test-forecast.json +5510 -0
  10. package/examples/private/warming-stripe-test.json +2578 -0
  11. package/examples/private/warming-stripes.json +4763 -0
  12. package/examples/tech-adoption-with-links.json +560 -0
  13. package/index.html +15 -20
  14. package/package.json +5 -4
  15. package/preview.html +1616 -0
  16. package/src/CdcChartComponent.tsx +111 -75
  17. package/src/_stories/Chart.Regions.Categorical.stories.tsx +148 -0
  18. package/src/_stories/Chart.Regions.DateScale.stories.tsx +197 -0
  19. package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +297 -0
  20. package/src/_stories/Chart.stories.tsx +8 -0
  21. package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
  22. package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
  23. package/src/_stories/ChartBrush.stories.tsx +50 -0
  24. package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
  25. package/src/_stories/TechAdoptionWithLinks.stories.tsx +27 -0
  26. package/src/_stories/_mock/brush_enabled.json +326 -0
  27. package/src/_stories/_mock/brush_mock.json +2 -69
  28. package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
  29. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
  30. package/src/components/Axis/Categorical.Axis.tsx +6 -7
  31. package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
  32. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
  33. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
  34. package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
  35. package/src/components/BarChart/components/context.tsx +1 -0
  36. package/src/components/BarChart/helpers/useBarChart.ts +14 -2
  37. package/src/components/Brush/BrushSelector.tsx +1258 -0
  38. package/src/components/Brush/MiniChartPreview.tsx +283 -0
  39. package/src/components/DeviationBar.jsx +9 -7
  40. package/src/components/EditorPanel/EditorPanel.tsx +2711 -2586
  41. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
  42. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +57 -30
  43. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
  44. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
  45. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +21 -27
  46. package/src/components/EditorPanel/useEditorPermissions.ts +31 -18
  47. package/src/components/Legend/Legend.tsx +3 -2
  48. package/src/components/Legend/helpers/createFormatLabels.tsx +151 -2
  49. package/src/components/Legend/helpers/index.ts +10 -6
  50. package/src/components/LinearChart.tsx +495 -430
  51. package/src/components/PairedBarChart.jsx +20 -3
  52. package/src/components/Regions/components/Regions.tsx +365 -122
  53. package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
  54. package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
  55. package/src/components/WarmingStripes/WarmingStripes.tsx +160 -0
  56. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
  57. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
  58. package/src/components/WarmingStripes/index.tsx +3 -0
  59. package/src/data/initial-state.js +3 -1
  60. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
  61. package/src/helpers/getMinMax.ts +12 -7
  62. package/src/helpers/sizeHelpers.ts +0 -20
  63. package/src/helpers/smallMultiplesHelpers.ts +1 -1
  64. package/src/hooks/useChartHoverAnalytics.tsx +10 -9
  65. package/src/hooks/useScales.ts +11 -1
  66. package/src/hooks/useTooltip.tsx +31 -10
  67. package/src/scss/DataTable.scss +0 -4
  68. package/src/scss/main.scss +17 -3
  69. package/src/test/CdcChart.test.jsx +1 -1
  70. package/src/types/ChartConfig.ts +3 -0
  71. package/src/types/Label.ts +1 -0
  72. package/src/utils/analyticsTracking.ts +19 -0
  73. package/LICENSE +0 -201
  74. package/src/components/Brush/BrushChart.tsx +0 -128
  75. package/src/components/Brush/BrushController.tsx +0 -71
  76. package/src/components/Brush/types.tsx +0 -8
  77. package/src/components/BrushChart.tsx +0 -223
@@ -5,23 +5,172 @@ 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) =>
26
+ (
27
+ config: ChartConfig,
28
+ tableData: Object[],
29
+ data: TransformedData[],
30
+ colorScale: ColorScale,
31
+ formatNumber: (value: any, axis: string) => string
32
+ ) =>
23
33
  (defaultLabels: Label[]): Label[] => {
24
34
  const { visualizationType, visualizationSubType, series, runtime, legend } = config
35
+
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
+ }
45
+
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 []
67
+ }
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'
100
+ ]
101
+ }
102
+
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))
116
+ }
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
+ })
134
+ }
135
+
136
+ return labels
137
+ }
138
+
139
+ // For interval style, create ranges
140
+ const numIntervals = legend?.warmingStripesIntervals || 5
141
+ const range = maxValue - minValue
142
+ const intervalSize = range / numIntervals
143
+
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)
168
+ })
169
+ }
170
+
171
+ return intervalLabels
172
+ }
173
+
25
174
  const sortVertical = labels =>
26
175
  legend.verticalSorted
27
176
  ? _.sortBy(_.cloneDeep(labels), label => {
@@ -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