@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.
- package/dist/cdcchart.js +38898 -40013
- package/examples/feature/pie/planet-pie-example-config.json +48 -2
- package/examples/private/DEV-12100.json +1303 -0
- package/examples/private/cat-y.json +1235 -0
- package/examples/private/data-points.json +228 -0
- package/examples/private/height.json +3915 -0
- package/examples/private/links.json +569 -0
- package/examples/private/quadrant.txt +30 -0
- package/examples/private/test-forecast.json +5510 -0
- package/examples/private/warming-stripe-test.json +2578 -0
- package/examples/private/warming-stripes.json +4763 -0
- package/examples/tech-adoption-with-links.json +560 -0
- package/index.html +15 -20
- package/package.json +5 -4
- package/preview.html +1616 -0
- package/src/CdcChartComponent.tsx +111 -75
- package/src/_stories/Chart.Regions.Categorical.stories.tsx +148 -0
- package/src/_stories/Chart.Regions.DateScale.stories.tsx +197 -0
- package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +297 -0
- package/src/_stories/Chart.stories.tsx +8 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +11 -6
- package/src/_stories/ChartBrush.Editor.stories.tsx +295 -0
- package/src/_stories/ChartBrush.stories.tsx +50 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +3 -5
- package/src/_stories/TechAdoptionWithLinks.stories.tsx +27 -0
- package/src/_stories/_mock/brush_enabled.json +326 -0
- package/src/_stories/_mock/brush_mock.json +2 -69
- package/src/_stories/_mock/horizontal-bars-dynamic-y-axis.json +413 -0
- package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -2
- package/src/components/Axis/Categorical.Axis.tsx +6 -7
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +178 -24
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +3 -1
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +1 -0
- package/src/components/BarChart/components/BarChart.Vertical.tsx +6 -8
- package/src/components/BarChart/components/context.tsx +1 -0
- package/src/components/BarChart/helpers/useBarChart.ts +14 -2
- package/src/components/Brush/BrushSelector.tsx +1258 -0
- package/src/components/Brush/MiniChartPreview.tsx +283 -0
- package/src/components/DeviationBar.jsx +9 -7
- package/src/components/EditorPanel/EditorPanel.tsx +2711 -2586
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +56 -34
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +57 -30
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +2 -0
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +30 -25
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +21 -27
- package/src/components/EditorPanel/useEditorPermissions.ts +31 -18
- package/src/components/Legend/Legend.tsx +3 -2
- package/src/components/Legend/helpers/createFormatLabels.tsx +151 -2
- package/src/components/Legend/helpers/index.ts +10 -6
- package/src/components/LinearChart.tsx +495 -430
- package/src/components/PairedBarChart.jsx +20 -3
- package/src/components/Regions/components/Regions.tsx +365 -122
- package/src/components/ScatterPlot/ScatterPlot.jsx +2 -2
- package/src/components/SmallMultiples/SmallMultipleTile.tsx +5 -1
- package/src/components/WarmingStripes/WarmingStripes.tsx +160 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +35 -0
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +104 -0
- package/src/components/WarmingStripes/index.tsx +3 -0
- package/src/data/initial-state.js +3 -1
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +57 -0
- package/src/helpers/getMinMax.ts +12 -7
- package/src/helpers/sizeHelpers.ts +0 -20
- package/src/helpers/smallMultiplesHelpers.ts +1 -1
- package/src/hooks/useChartHoverAnalytics.tsx +10 -9
- package/src/hooks/useScales.ts +11 -1
- package/src/hooks/useTooltip.tsx +31 -10
- package/src/scss/DataTable.scss +0 -4
- package/src/scss/main.scss +17 -3
- package/src/test/CdcChart.test.jsx +1 -1
- package/src/types/ChartConfig.ts +3 -0
- package/src/types/Label.ts +1 -0
- package/src/utils/analyticsTracking.ts +19 -0
- package/LICENSE +0 -201
- package/src/components/Brush/BrushChart.tsx +0 -128
- package/src/components/Brush/BrushController.tsx +0 -71
- package/src/components/Brush/types.tsx +0 -8
- 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
|
-
(
|
|
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
|
-
|
|
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
|