@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.
- package/.claude/settings.local.json +9 -0
- package/dist/cdcchart.js +37524 -35243
- package/examples/feature/__data__/planet-example-data.json +0 -30
- package/examples/grouped-bar-test.json +400 -0
- package/examples/private/d.json +382 -0
- package/examples/private/example-2.json +49784 -0
- package/examples/private/f2.json +1 -0
- package/examples/private/f4.json +1577 -0
- package/examples/private/forecast.json +1180 -0
- package/examples/private/lollipop.json +468 -0
- package/examples/private/new.json +48756 -0
- package/examples/private/pie-chart-legend.json +904 -0
- package/examples/suppressed_tooltip.json +480 -0
- package/index.html +10 -22
- package/package.json +25 -7
- package/src/CdcChart.tsx +1 -2
- package/src/CdcChartComponent.tsx +174 -32
- package/src/_stories/Chart.Anchors.stories.tsx +2 -2
- package/src/_stories/Chart.BoxPlot.stories.tsx +1 -1
- package/src/_stories/Chart.CI.stories.tsx +1 -1
- package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
- package/src/_stories/Chart.DynamicSeries.stories.tsx +2 -2
- package/src/_stories/Chart.Filters.stories.tsx +2 -2
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
- package/src/_stories/Chart.Patterns.stories.tsx +19 -0
- package/src/_stories/Chart.ScatterPlot.stories.tsx +1 -1
- package/src/_stories/Chart.stories.tsx +8 -5
- package/src/_stories/Chart.tooltip.stories.tsx +1 -1
- package/src/_stories/ChartAnnotation.stories.tsx +1 -1
- package/src/_stories/ChartAxisLabels.stories.tsx +2 -2
- package/src/_stories/ChartAxisTitles.stories.tsx +2 -2
- package/src/_stories/ChartEditor.stories.tsx +60 -60
- package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
- package/src/_stories/ChartLine.Symbols.stories.tsx +1 -1
- package/src/_stories/ChartPrefixSuffix.stories.tsx +2 -2
- package/src/_stories/_mock/stacked-pattern-test.json +520 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -0
- package/src/components/Annotations/components/AnnotationDropdown.tsx +1 -1
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +159 -20
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +138 -5
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +215 -73
- package/src/components/BarChart/components/BarChart.Vertical.tsx +153 -21
- package/src/components/BarChart/helpers/index.ts +43 -4
- package/src/components/BarChart/helpers/lollipopColors.ts +27 -0
- package/src/components/BarChart/helpers/useBarChart.ts +25 -3
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +2 -1
- package/src/components/DeviationBar.jsx +9 -6
- package/src/components/EditorPanel/EditorPanel.tsx +364 -39
- package/src/components/EditorPanel/EditorPanelContext.ts +3 -0
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +414 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +28 -20
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +115 -120
- package/src/components/EditorPanel/components/Panels/index.tsx +3 -1
- package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +0 -8
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +49 -48
- package/src/components/Forecasting/Forecasting.tsx +36 -6
- package/src/components/ForestPlot/ForestPlot.tsx +11 -7
- package/src/components/ForestPlot/ForestPlotProps.ts +1 -1
- package/src/components/Legend/Legend.Component.tsx +106 -13
- package/src/components/Legend/helpers/createFormatLabels.tsx +230 -171
- package/src/components/LegendWrapper.tsx +1 -1
- package/src/components/LineChart/components/LineChart.Circle.tsx +2 -2
- package/src/components/LineChart/index.tsx +2 -2
- package/src/components/LinearChart.tsx +22 -5
- package/src/components/PairedBarChart.jsx +6 -4
- package/src/components/PieChart/PieChart.tsx +170 -54
- package/src/components/Sankey/components/Sankey.tsx +7 -1
- package/src/components/ScatterPlot/ScatterPlot.jsx +32 -4
- package/src/data/initial-state.js +315 -293
- package/src/helpers/buildForecastPaletteMappings.ts +112 -0
- package/src/helpers/buildForecastPaletteOptions.ts +109 -0
- package/src/helpers/getColorScale.ts +72 -8
- package/src/helpers/getNewRuntime.ts +1 -1
- package/src/helpers/getTransformedData.ts +1 -1
- package/src/hooks/useChartHoverAnalytics.tsx +44 -0
- package/src/hooks/useReduceData.ts +105 -70
- package/src/hooks/useTooltip.tsx +57 -15
- package/src/index.jsx +0 -2
- package/src/scss/main.scss +12 -0
- package/src/store/chart.reducer.ts +1 -1
- package/src/test/CdcChart.test.jsx +8 -3
- package/src/types/ChartConfig.ts +30 -6
- package/src/types/ChartContext.ts +1 -0
- package/vite.config.js +1 -1
- package/vitest.config.ts +16 -0
- package/src/coreStyles_chart.scss +0 -3
- package/src/helpers/configHelpers.ts +0 -28
- package/src/helpers/generateColorsArray.ts +0 -8
- package/src/hooks/useColorPalette.js +0 -76
|
@@ -10,6 +10,7 @@ interface BarConfigProps {
|
|
|
10
10
|
barWidth: number
|
|
11
11
|
isVertical: boolean
|
|
12
12
|
yAxisValue: number
|
|
13
|
+
labelFontSize: number
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
// Function to create bar width based on suppression status and missing data label
|
|
@@ -20,7 +21,8 @@ export const getBarConfig = ({
|
|
|
20
21
|
config,
|
|
21
22
|
barWidth,
|
|
22
23
|
isVertical,
|
|
23
|
-
yAxisValue
|
|
24
|
+
yAxisValue,
|
|
25
|
+
labelFontSize
|
|
24
26
|
}: BarConfigProps) => {
|
|
25
27
|
const heightMini = 3 /// height of small bars aka suppressed/NA/Zero valued
|
|
26
28
|
let barHeight = defaultBarHeight
|
|
@@ -50,7 +52,7 @@ export const getBarConfig = ({
|
|
|
50
52
|
|
|
51
53
|
// Handle undefined, null, or non-calculable bar.value
|
|
52
54
|
if (!isSuppressed && !isNumber(bar.value) && config.general.showMissingDataLabel) {
|
|
53
|
-
const labelWidth = getTextWidth(barLabel, `normal ${
|
|
55
|
+
const labelWidth = getTextWidth(barLabel, `normal ${labelFontSize}px sans-serif`)
|
|
54
56
|
const labelFits = Number(labelWidth) < barWidth && barWidth > 10
|
|
55
57
|
showMissingDataLabel = true
|
|
56
58
|
barHeight = labelFits ? heightMini : 0
|
|
@@ -59,7 +61,7 @@ export const getBarConfig = ({
|
|
|
59
61
|
// Handle undefined, null, or non-calculable bar.value
|
|
60
62
|
|
|
61
63
|
if (!isSuppressed && bar.value === '0' && config.general.showZeroValueData) {
|
|
62
|
-
const labelWidth = getTextWidth('0', `normal ${
|
|
64
|
+
const labelWidth = getTextWidth('0', `normal ${labelFontSize}px sans-serif`)
|
|
63
65
|
const labelFits = Number(labelWidth) < barWidth && barWidth > 10
|
|
64
66
|
showZeroValueData = true
|
|
65
67
|
barHeight = labelFits ? heightMini : 0
|
|
@@ -91,7 +93,7 @@ export const getBarConfig = ({
|
|
|
91
93
|
if (showZeroValueData) label = '0'
|
|
92
94
|
|
|
93
95
|
// determine label width in pixels & check if it fits to the bar width
|
|
94
|
-
const labelWidth = getTextWidth(barLabel, `normal ${
|
|
96
|
+
const labelWidth = getTextWidth(barLabel, `normal ${labelFontSize}px sans-serif`)
|
|
95
97
|
const labelFits = Number(labelWidth) < barWidth && barWidth > 10
|
|
96
98
|
if (config.isLollipopChart) {
|
|
97
99
|
return label
|
|
@@ -110,3 +112,40 @@ export const testZeroValue = value => {
|
|
|
110
112
|
const regex = /^0(\.0)?$/
|
|
111
113
|
return regex.test(value.toString())
|
|
112
114
|
}
|
|
115
|
+
|
|
116
|
+
export const addMinimumBarHeights = barStacks => {
|
|
117
|
+
const MIN_BAR_HEIGHT = 3
|
|
118
|
+
|
|
119
|
+
barStacks[0].bars.forEach((_, i) => {
|
|
120
|
+
let segments = barStacks
|
|
121
|
+
.map(bs => bs.bars[i])
|
|
122
|
+
.filter(s => s.bar.data[s.key])
|
|
123
|
+
.reverse()
|
|
124
|
+
|
|
125
|
+
const segmentsNeedingAdjustment = segments.filter(segment => segment.height > 0 && segment.height < MIN_BAR_HEIGHT)
|
|
126
|
+
const segmentsToShrink = segments.filter(segment => segment.height > MIN_BAR_HEIGHT)
|
|
127
|
+
|
|
128
|
+
if (segmentsNeedingAdjustment.length > 0 && segmentsToShrink.length > 0) {
|
|
129
|
+
segmentsNeedingAdjustment.forEach(smallSegment => {
|
|
130
|
+
const heightToAdd = MIN_BAR_HEIGHT - smallSegment.height
|
|
131
|
+
const heightToTakePerSegment = heightToAdd / segmentsToShrink.length
|
|
132
|
+
|
|
133
|
+
segmentsToShrink.forEach(largeSegment => {
|
|
134
|
+
largeSegment.height -= heightToTakePerSegment
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
smallSegment.height = MIN_BAR_HEIGHT
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
let cumulativeY = segments[0].y
|
|
141
|
+
segments.forEach(segment => {
|
|
142
|
+
segment.y = cumulativeY
|
|
143
|
+
cumulativeY += segment.height
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return barStacks
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { getLollipopStemColor, getLollipopHeadColor } from './lollipopColors'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import chroma from 'chroma-js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adjusts color for lollipop stem (lighter than original for contrast)
|
|
5
|
+
*/
|
|
6
|
+
export const getLollipopStemColor = (baseColor: string): string => {
|
|
7
|
+
const lightness = chroma(baseColor).get('hsl.l')
|
|
8
|
+
|
|
9
|
+
if (lightness > 0.7) {
|
|
10
|
+
// Very light colors: darken slightly to ensure visibility
|
|
11
|
+
return chroma(baseColor).darken(0.1).hex()
|
|
12
|
+
} else if (lightness > 0.4) {
|
|
13
|
+
// Medium colors: lighten moderately
|
|
14
|
+
return chroma(baseColor).brighten(0.5).hex()
|
|
15
|
+
} else {
|
|
16
|
+
// Dark colors: lighten significantly but keep visible
|
|
17
|
+
return chroma(baseColor).brighten(1.0).hex()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Adjusts color for lollipop head (darker than original for contrast)
|
|
23
|
+
*/
|
|
24
|
+
export const getLollipopHeadColor = (baseColor: string): string => {
|
|
25
|
+
const lightness = chroma(baseColor).get('hsl.l')
|
|
26
|
+
return lightness > 0.6 ? chroma(baseColor).darken(1.5).hex() : baseColor
|
|
27
|
+
}
|
|
@@ -2,9 +2,13 @@ import React, { useContext, useEffect, useState } from 'react'
|
|
|
2
2
|
import { ChartDispatchContext } from '../../../ConfigContext'
|
|
3
3
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
4
4
|
import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
|
|
5
|
+
import { getPaletteColors } from '@cdc/core/helpers/palettes/utils'
|
|
6
|
+
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
7
|
+
import { getVizSubType, getVizTitle } from '@cdc/core/helpers/metrics/utils'
|
|
5
8
|
|
|
6
9
|
export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, configContext) => {
|
|
7
|
-
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, seriesHighlight } =
|
|
10
|
+
const { config, colorPalettes, tableData, updateConfig, parseDate, formatDate, seriesHighlight, interactionLabel } =
|
|
11
|
+
configContext
|
|
8
12
|
const { orientation } = config
|
|
9
13
|
const dispatch = useContext(ChartDispatchContext)
|
|
10
14
|
const [hoveredBar, setHoveredBar] = useState(null)
|
|
@@ -98,7 +102,7 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
98
102
|
if (!config.legend.colorCode && config.series.length > 1) {
|
|
99
103
|
return currentBarColor
|
|
100
104
|
}
|
|
101
|
-
const palettesArr = config
|
|
105
|
+
const palettesArr = getPaletteColors(config, colorPalettes)
|
|
102
106
|
const values = tableData.map(d => {
|
|
103
107
|
return d[config.legend.colorCode]
|
|
104
108
|
})
|
|
@@ -189,12 +193,30 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
189
193
|
return additionalTooltipItems
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
const onMouseOverBar = (categoryValue, barKey, event, data) => {
|
|
196
|
+
const onMouseOverBar = (categoryValue, barKey, event, data, barValue) => {
|
|
193
197
|
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey) {
|
|
194
198
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [barKey] })
|
|
195
199
|
}
|
|
196
200
|
handleTooltipMouseOver(event, data)
|
|
197
201
|
setHoveredBar(categoryValue)
|
|
202
|
+
|
|
203
|
+
if (config.tooltips.singleSeries) {
|
|
204
|
+
const numericValue = barValue || 'none'
|
|
205
|
+
publishAnalyticsEvent({
|
|
206
|
+
vizType: config.type,
|
|
207
|
+
vizSubType: getVizSubType(config),
|
|
208
|
+
eventType: `chart_hover`,
|
|
209
|
+
eventAction: 'hover',
|
|
210
|
+
eventLabel: interactionLabel,
|
|
211
|
+
vizTitle: getVizTitle(config),
|
|
212
|
+
series: barKey || 'none',
|
|
213
|
+
specifics: `series: ${barKey || 'none'}, yValue: ${
|
|
214
|
+
orientation === 'horizontal' ? categoryValue : numericValue
|
|
215
|
+
}, xValue: ${orientation === 'horizontal' ? numericValue : categoryValue}, orientation: ${
|
|
216
|
+
orientation || 'none'
|
|
217
|
+
}`
|
|
218
|
+
})
|
|
219
|
+
}
|
|
198
220
|
}
|
|
199
221
|
const onMouseLeaveBar = () => {
|
|
200
222
|
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight') {
|
|
@@ -4,6 +4,7 @@ import { Group } from '@visx/group'
|
|
|
4
4
|
import ConfigContext from '../../ConfigContext'
|
|
5
5
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
6
6
|
import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
|
|
7
|
+
import { getCurrentPaletteName } from '@cdc/core/helpers/palettes/utils'
|
|
7
8
|
import { handleTooltip, createPlots } from './helpers/index'
|
|
8
9
|
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
9
10
|
import _ from 'lodash'
|
|
@@ -18,7 +19,7 @@ const BoxPlotVertical = ({ xScale, yScale, seriesScale }) => {
|
|
|
18
19
|
const bodyStyles = getComputedStyle(document.body)
|
|
19
20
|
const defaultColor = APP_FONT_COLOR
|
|
20
21
|
const constrainedWidth = Math.min(40, boxWidth)
|
|
21
|
-
const color_0 = _.get(colorPalettesChart, [config
|
|
22
|
+
const color_0 = _.get(colorPalettesChart, [getCurrentPaletteName(config), 0], '#000')
|
|
22
23
|
const plots = createPlots(data, config)
|
|
23
24
|
return (
|
|
24
25
|
<ErrorBoundary component='BoxPlot Vertical'>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Line } from '@visx/shape'
|
|
2
2
|
import { Group } from '@visx/group'
|
|
3
|
-
import { useContext, useEffect, useRef, useState } from 'react'
|
|
3
|
+
import React, { useContext, useEffect, useRef, useState } from 'react'
|
|
4
4
|
import ConfigContext from '../ConfigContext'
|
|
5
5
|
import { Text } from '@visx/text'
|
|
6
6
|
import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
@@ -8,6 +8,7 @@ import useIntersectionObserver from '../hooks/useIntersectionObserver'
|
|
|
8
8
|
import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
|
|
9
9
|
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
10
10
|
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
11
|
+
import { isV1Palette } from '@cdc/core/helpers/palettes/utils'
|
|
11
12
|
|
|
12
13
|
export default function DeviationBar({ height, xScale }) {
|
|
13
14
|
const {
|
|
@@ -26,10 +27,10 @@ export default function DeviationBar({ height, xScale }) {
|
|
|
26
27
|
roundingStyle === 'standard'
|
|
27
28
|
? '8px'
|
|
28
29
|
: roundingStyle === 'shallow'
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
? '5px'
|
|
31
|
+
: roundingStyle === 'finger'
|
|
32
|
+
? '15px'
|
|
33
|
+
: '0px'
|
|
33
34
|
const isRounded = config.barStyle === 'rounded'
|
|
34
35
|
const target = Number(config.xAxis.target)
|
|
35
36
|
const seriesKey = config.series[0].dataKey
|
|
@@ -176,7 +177,8 @@ export default function DeviationBar({ height, xScale }) {
|
|
|
176
177
|
const squareY = barY - barHeight / 2
|
|
177
178
|
const borderRadius = applyRadius(barPosition)
|
|
178
179
|
// colors
|
|
179
|
-
|
|
180
|
+
let versionName = isV1Palette(config) ? 'v1' : 'v2'
|
|
181
|
+
const [leftColor, rightColor] = twoColorPalette?.[versionName]?.[twoColor.palette] || ['#1D6ABF', '#935586']
|
|
180
182
|
const barColor = { left: leftColor, right: rightColor }
|
|
181
183
|
const fill = getContrastColor(APP_FONT_COLOR, barColor[barPosition])
|
|
182
184
|
|
|
@@ -205,6 +207,7 @@ export default function DeviationBar({ height, xScale }) {
|
|
|
205
207
|
y={barY}
|
|
206
208
|
width={barWidth}
|
|
207
209
|
height={barHeight}
|
|
210
|
+
onMouseEnter={() => {}}
|
|
208
211
|
data-tooltip-html={tooltip}
|
|
209
212
|
data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
|
|
210
213
|
tabIndex={-1}
|