@cdc/chart 4.26.1 → 4.26.3
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.local.md +79 -0
- package/LICENSE +201 -0
- package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
- package/dist/cdcchart.js +54742 -49796
- package/examples/data/data-with-metadata.json +10 -0
- package/examples/default.json +378 -0
- package/examples/feature/__data__/horizon-chart-data.json +373 -0
- package/examples/feature/annotations/index.json +3 -6
- package/examples/feature/horizon/horizon-chart.json +395 -0
- package/examples/feature/pie/planet-pie-example-config.json +2 -1
- package/examples/line-chart-states.json +1085 -0
- package/examples/metadata-variables.json +58 -0
- package/examples/private/123.json +694 -0
- package/examples/private/anchor-issue.json +4094 -0
- package/examples/private/backwards-slider.json +10430 -0
- package/examples/private/georgia.csv +160 -0
- package/examples/private/timeline-data.json +1 -0
- package/examples/private/timeline.json +389 -0
- package/examples/radar-chart-simple.json +133 -0
- package/examples/radar-chart.json +148 -0
- package/index.html +1 -31
- package/package.json +57 -59
- package/src/CdcChart.tsx +8 -4
- package/src/CdcChartComponent.tsx +398 -284
- package/src/_stories/Chart.Anchors.stories.tsx +10 -0
- package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
- package/src/_stories/Chart.CI.stories.tsx +13 -0
- package/src/_stories/Chart.Combo.stories.tsx +17 -0
- package/src/_stories/Chart.CustomColors.stories.tsx +78 -0
- package/src/_stories/Chart.Defaults.stories.tsx +95 -0
- package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
- package/src/_stories/Chart.Filters.stories.tsx +4 -0
- package/src/_stories/Chart.Forecast.stories.tsx +4 -0
- package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
- package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
- package/src/_stories/Chart.Patterns.stories.tsx +4 -0
- package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
- package/src/_stories/Chart.Regions.Categorical.stories.tsx +13 -0
- package/src/_stories/Chart.Regions.DateScale.stories.tsx +19 -0
- package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +25 -10
- package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
- package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
- package/src/_stories/Chart.SmallestLeftAxisMax.stories.tsx +64 -0
- package/src/_stories/Chart.stories.tsx +72 -1
- package/src/_stories/Chart.tooltip.stories.tsx +7 -0
- package/src/_stories/ChartAnnotation.stories.tsx +10 -0
- package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
- package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +97 -38
- package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
- package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
- package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
- package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
- package/src/_stories/ChartBrush.stories.tsx +7 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +1 -1
- package/src/_stories/ChartEditor.stories.tsx +7 -0
- package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
- package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
- package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
- package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
- package/src/_stories/TechAdoptionWithLinks.stories.tsx +7 -0
- package/src/_stories/_mock/brush_continuous.json +86 -0
- package/src/_stories/_mock/brush_date_large.json +176 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
- package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
- package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
- package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
- package/src/_stories/_mock/paired-bar-abbr.json +421 -0
- package/src/_stories/_mock/pie_custom_colors.json +268 -0
- package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +14 -20
- package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
- package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
- package/src/components/Annotations/components/AnnotationList.styles.css +12 -18
- package/src/components/Annotations/components/AnnotationList.tsx +5 -4
- package/src/components/Annotations/components/findNearestDatum.ts +75 -85
- package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
- package/src/components/Axis/BottomAxis.tsx +277 -0
- package/src/components/Axis/LeftAxis.tsx +404 -0
- package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
- package/src/components/Axis/PairedBarAxis.tsx +192 -0
- package/src/components/Axis/README.md +94 -0
- package/src/components/Axis/RightAxis.tsx +108 -0
- package/src/components/Axis/axis.constants.ts +21 -0
- package/src/components/Axis/index.ts +7 -0
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +12 -28
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +12 -30
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +12 -31
- package/src/components/BarChart/components/BarChart.Vertical.tsx +12 -28
- package/src/components/BarChart/components/BarChart.tsx +7 -1
- package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
- package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
- package/src/components/BarChart/helpers/useBarChart.ts +3 -0
- package/src/components/Brush/BrushSelector.tsx +155 -22
- package/src/components/Brush/MiniChartPreview.tsx +133 -21
- package/src/components/EditorPanel/EditorPanel.tsx +81 -54
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +67 -29
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +120 -2
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +25 -43
- package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +83 -3
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +66 -43
- package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
- package/src/components/EditorPanel/editor-panel.scss +1 -1
- package/src/components/EditorPanel/useEditorPermissions.ts +55 -26
- package/src/components/ForestPlot/ForestPlot.tsx +26 -22
- package/src/components/HorizonChart/HorizonChart.tsx +131 -0
- package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
- package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
- package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
- package/src/components/HorizonChart/index.tsx +3 -0
- package/src/components/Legend/Legend.Component.tsx +52 -4
- package/src/components/Legend/Legend.tsx +1 -1
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +4 -4
- package/src/components/Legend/LegendValueRange.tsx +77 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +16 -2
- package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
- package/src/components/LineChart/helpers/README.md +292 -0
- package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
- package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
- package/src/components/LineChart/index.tsx +44 -8
- package/src/components/LinearChart/README.md +109 -0
- package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
- package/src/components/LinearChart/linearChart.constants.ts +84 -0
- package/src/components/LinearChart/tests/LinearChart.test.tsx +278 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +131 -0
- package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
- package/src/components/LinearChart.tsx +268 -1057
- package/src/components/PieChart/PieChart.tsx +20 -5
- package/src/components/RadarChart/RadarAxis.tsx +78 -0
- package/src/components/RadarChart/RadarChart.tsx +298 -0
- package/src/components/RadarChart/RadarGrid.tsx +64 -0
- package/src/components/RadarChart/RadarPolygon.tsx +91 -0
- package/src/components/RadarChart/helpers.ts +83 -0
- package/src/components/RadarChart/index.tsx +3 -0
- package/src/components/Regions/components/Regions.tsx +6 -6
- package/src/components/Sankey/components/Sankey.tsx +3 -3
- package/src/components/Sankey/sankey.scss +1 -1
- package/src/components/SmallMultiples/SmallMultiples.css +5 -5
- package/src/components/Sparkline/index.scss +4 -2
- package/src/components/WarmingStripes/WarmingStripes.tsx +95 -25
- package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
- package/src/data/initial-state.js +37 -15
- package/src/data/legacy-defaults.ts +18 -0
- package/src/helpers/abbreviateNumber.ts +24 -17
- package/src/helpers/getChartPatternId.ts +17 -0
- package/src/helpers/getExcludedData.ts +4 -0
- package/src/helpers/getMinMax.ts +16 -2
- package/src/helpers/handleChartAriaLabels.ts +19 -19
- package/src/helpers/handleLineType.ts +22 -18
- package/src/helpers/seriesColumnSettings.ts +114 -0
- package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
- package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
- package/src/hooks/useProgrammaticTooltip.ts +23 -2
- package/src/hooks/useRightAxis.ts +14 -0
- package/src/hooks/useScales.ts +99 -56
- package/src/hooks/useTooltip.tsx +23 -3
- package/src/scss/main.scss +157 -79
- package/src/selectors/README.md +68 -0
- package/src/store/chart.reducer.ts +2 -0
- package/src/test/CdcChart.test.jsx +2 -2
- package/src/types/ChartConfig.ts +22 -0
- package/src/types/ChartContext.ts +1 -0
- package/src/types/Horizon.ts +64 -0
- package/tests/fixtures/chart-config-with-metadata.json +29 -0
- package/tests/fixtures/data-with-metadata.json +10 -0
- package/preview.html +0 -1616
- package/src/components/Annotations/components/helpers/index.tsx +0 -46
|
@@ -3,6 +3,7 @@ import { LinePath, AreaClosed, AreaStack } from '@visx/shape'
|
|
|
3
3
|
import * as allCurves from '@visx/curve'
|
|
4
4
|
import { handleLineType } from '../../helpers/handleLineType'
|
|
5
5
|
import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
|
|
6
|
+
import { getPatternUrl as getPatternUrlForBar } from '../BarChart/helpers/getPatternUrl'
|
|
6
7
|
|
|
7
8
|
interface MiniChartPreviewProps {
|
|
8
9
|
series: any[]
|
|
@@ -10,25 +11,41 @@ interface MiniChartPreviewProps {
|
|
|
10
11
|
dataKey: string
|
|
11
12
|
xScale: any
|
|
12
13
|
miniYScale: any
|
|
14
|
+
miniRightYScale?: any
|
|
13
15
|
colorScale: any
|
|
14
16
|
config: any
|
|
15
17
|
xMax: number
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
19
|
-
({ series, tableData, dataKey, xScale, miniYScale, colorScale, config }) => {
|
|
21
|
+
({ series, tableData, dataKey, xScale, miniYScale, miniRightYScale, colorScale, config }) => {
|
|
20
22
|
if (!series || !series.length || !tableData || !tableData.length || !xScale || !miniYScale) {
|
|
21
23
|
return null
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
const bandwidth = xScale.bandwidth?.() || 0
|
|
27
|
+
const isComboChart = config?.visualizationType === 'Combo'
|
|
25
28
|
const isBarChart = config?.visualizationType === 'Bar'
|
|
26
29
|
const isStacked = config?.visualizationSubType === 'stacked'
|
|
27
30
|
const isAreaChart = config?.visualizationType === 'Area Chart'
|
|
31
|
+
const barSeriesTypes = new Set(['Bar', 'Paired Bar', 'Deviation Bar', 'Combo'])
|
|
32
|
+
const barSeries = isComboChart ? series.filter(s => barSeriesTypes.has(s.type)) : series
|
|
33
|
+
const areaSeries = isComboChart ? series.filter(s => s.type === 'Area Chart') : series
|
|
34
|
+
const lineSeries = isComboChart
|
|
35
|
+
? series.filter(s => !barSeriesTypes.has(s.type) && s.type !== 'Area Chart')
|
|
36
|
+
: series
|
|
37
|
+
const patternSeriesKeys = Array.isArray(series) ? series.map(_series => _series.dataKey) : []
|
|
38
|
+
const allowGroupedNonSeriesFieldMatch = !config.series || config.series.length <= 1
|
|
39
|
+
|
|
40
|
+
let barElements: React.ReactElement[] = []
|
|
28
41
|
|
|
29
42
|
// For bar charts, render rectangles
|
|
30
|
-
if (isBarChart) {
|
|
43
|
+
if (isBarChart || (isComboChart && barSeries.length > 0)) {
|
|
31
44
|
const bars: React.ReactElement[] = []
|
|
45
|
+
const barSeriesToRender = isBarChart ? series : barSeries
|
|
46
|
+
|
|
47
|
+
const barStrokeColor = config?.barHasBorder === 'true' ? '#000' : 'transparent'
|
|
48
|
+
const barStrokeWidth = config?.barHasBorder === 'true' ? 1 : 0
|
|
32
49
|
|
|
33
50
|
tableData.forEach((d, i) => {
|
|
34
51
|
const xVal = xScale(d[dataKey])
|
|
@@ -47,11 +64,20 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
47
64
|
let cumulativeValue = 0
|
|
48
65
|
const zeroY = miniYScale(0)
|
|
49
66
|
|
|
50
|
-
|
|
67
|
+
barSeriesToRender.forEach((s, seriesIndex) => {
|
|
51
68
|
const value = parseFloat(d[s.dataKey])
|
|
52
69
|
if (isNaN(value) || value === 0) return
|
|
53
70
|
|
|
54
71
|
const seriesColor = colorScale?.(config.runtime.seriesLabels?.[s.dataKey] || s.dataKey) || '#666'
|
|
72
|
+
const patternUrl = getPatternUrlForBar({
|
|
73
|
+
patterns: config.legend?.patterns,
|
|
74
|
+
datum: d,
|
|
75
|
+
seriesKey: s.dataKey,
|
|
76
|
+
seriesValue: value,
|
|
77
|
+
seriesLabels: config.runtime?.seriesLabels,
|
|
78
|
+
seriesKeys: patternSeriesKeys,
|
|
79
|
+
allowNonSeriesFieldMatch: true
|
|
80
|
+
})
|
|
55
81
|
|
|
56
82
|
// Calculate the bottom and top of this segment
|
|
57
83
|
// For stacked bars, each segment sits on top of the previous one
|
|
@@ -72,22 +98,49 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
72
98
|
width={barWidth}
|
|
73
99
|
height={barHeight}
|
|
74
100
|
fill={seriesColor}
|
|
75
|
-
|
|
101
|
+
stroke={barStrokeColor}
|
|
102
|
+
strokeWidth={barStrokeWidth}
|
|
103
|
+
fillOpacity={1}
|
|
76
104
|
pointerEvents='none'
|
|
77
105
|
/>
|
|
78
106
|
)
|
|
107
|
+
if (patternUrl) {
|
|
108
|
+
bars.push(
|
|
109
|
+
<rect
|
|
110
|
+
key={`mini-bar-stacked-pattern-${i}-${seriesIndex}`}
|
|
111
|
+
x={x - barWidth / 2}
|
|
112
|
+
y={y}
|
|
113
|
+
width={barWidth}
|
|
114
|
+
height={barHeight}
|
|
115
|
+
fill={patternUrl}
|
|
116
|
+
stroke='transparent'
|
|
117
|
+
strokeWidth={0}
|
|
118
|
+
fillOpacity={1}
|
|
119
|
+
pointerEvents='none'
|
|
120
|
+
/>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
79
123
|
})
|
|
80
124
|
} else {
|
|
81
125
|
// For grouped bars, render bars side by side
|
|
82
|
-
const seriesCount =
|
|
126
|
+
const seriesCount = barSeriesToRender.length
|
|
83
127
|
const groupBarWidth = barWidth / seriesCount
|
|
84
128
|
const zeroY = miniYScale(0)
|
|
85
129
|
|
|
86
|
-
|
|
130
|
+
barSeriesToRender.forEach((s, seriesIndex) => {
|
|
87
131
|
const value = parseFloat(d[s.dataKey])
|
|
88
132
|
if (isNaN(value)) return
|
|
89
133
|
|
|
90
134
|
const seriesColor = colorScale?.(config.runtime.seriesLabels?.[s.dataKey] || s.dataKey) || '#666'
|
|
135
|
+
const patternUrl = getPatternUrlForBar({
|
|
136
|
+
patterns: config.legend?.patterns,
|
|
137
|
+
datum: d,
|
|
138
|
+
seriesKey: s.dataKey,
|
|
139
|
+
seriesValue: value,
|
|
140
|
+
seriesLabels: config.runtime?.seriesLabels,
|
|
141
|
+
seriesKeys: patternSeriesKeys,
|
|
142
|
+
allowNonSeriesFieldMatch: allowGroupedNonSeriesFieldMatch
|
|
143
|
+
})
|
|
91
144
|
|
|
92
145
|
// Calculate bar position and height
|
|
93
146
|
const valueY = miniYScale(value)
|
|
@@ -100,18 +153,39 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
100
153
|
key={`mini-bar-grouped-${i}-${seriesIndex}`}
|
|
101
154
|
x={barX}
|
|
102
155
|
y={y}
|
|
103
|
-
width={groupBarWidth
|
|
156
|
+
width={groupBarWidth}
|
|
104
157
|
height={barHeight}
|
|
105
158
|
fill={seriesColor}
|
|
106
|
-
|
|
159
|
+
stroke={barStrokeColor}
|
|
160
|
+
strokeWidth={barStrokeWidth}
|
|
161
|
+
fillOpacity={1}
|
|
107
162
|
pointerEvents='none'
|
|
108
163
|
/>
|
|
109
164
|
)
|
|
165
|
+
if (patternUrl) {
|
|
166
|
+
bars.push(
|
|
167
|
+
<rect
|
|
168
|
+
key={`mini-bar-grouped-pattern-${i}-${seriesIndex}`}
|
|
169
|
+
x={barX}
|
|
170
|
+
y={y}
|
|
171
|
+
width={groupBarWidth}
|
|
172
|
+
height={barHeight}
|
|
173
|
+
fill={patternUrl}
|
|
174
|
+
stroke='transparent'
|
|
175
|
+
strokeWidth={0}
|
|
176
|
+
fillOpacity={1}
|
|
177
|
+
pointerEvents='none'
|
|
178
|
+
/>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
110
181
|
})
|
|
111
182
|
}
|
|
112
183
|
})
|
|
113
184
|
|
|
114
|
-
|
|
185
|
+
barElements = bars
|
|
186
|
+
if (isBarChart && !isComboChart) {
|
|
187
|
+
return <>{barElements}</>
|
|
188
|
+
}
|
|
115
189
|
}
|
|
116
190
|
|
|
117
191
|
// For stacked area charts, use AreaStack
|
|
@@ -217,10 +291,10 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
217
291
|
)
|
|
218
292
|
}
|
|
219
293
|
|
|
220
|
-
// For line/area charts, render lines or areas
|
|
294
|
+
// For line/area charts (and combo line/area series), render lines or areas
|
|
221
295
|
return (
|
|
222
296
|
<>
|
|
223
|
-
{series.map((s, i) => {
|
|
297
|
+
{(isComboChart ? areaSeries : isAreaChart ? series : []).map((s, i) => {
|
|
224
298
|
const seriesKey = s.dataKey
|
|
225
299
|
const seriesColor = colorScale?.(config.runtime.seriesLabels?.[seriesKey] || seriesKey) || '#666'
|
|
226
300
|
|
|
@@ -231,25 +305,31 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
231
305
|
const curve = allCurves[seriesLineType] || allCurves.curveLinear
|
|
232
306
|
const strokeDasharray = handleLineType(seriesStyle)
|
|
233
307
|
|
|
234
|
-
//
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
308
|
+
// Use the right-axis scale when the series is on the right axis
|
|
309
|
+
const yScaleForSeries = s.axis === 'Right' && miniRightYScale ? miniRightYScale : miniYScale
|
|
310
|
+
|
|
311
|
+
// Filter to only valid data points, then sort by X position so the area
|
|
312
|
+
// renders left-to-right regardless of the original data order.
|
|
313
|
+
const validData = tableData
|
|
314
|
+
.filter(d => {
|
|
315
|
+
const xVal = xScale(d[dataKey])
|
|
316
|
+
const yVal = parseFloat(d[s.dataKey])
|
|
317
|
+
return xVal !== undefined && !isNaN(yVal)
|
|
318
|
+
})
|
|
319
|
+
.sort((a, b) => (xScale(a[dataKey]) ?? 0) - (xScale(b[dataKey]) ?? 0))
|
|
240
320
|
|
|
241
321
|
if (validData.length === 0) return null
|
|
242
322
|
|
|
243
323
|
const getX = d => xScale(d[dataKey]) + bandwidth / 2
|
|
244
|
-
const getY = d =>
|
|
324
|
+
const getY = d => yScaleForSeries(parseFloat(d[s.dataKey]))
|
|
245
325
|
|
|
246
|
-
return
|
|
326
|
+
return (
|
|
247
327
|
<AreaClosed
|
|
248
328
|
key={`mini-area-${seriesKey}-${i}`}
|
|
249
329
|
data={validData}
|
|
250
330
|
x={getX}
|
|
251
331
|
y={getY}
|
|
252
|
-
yScale={
|
|
332
|
+
yScale={yScaleForSeries}
|
|
253
333
|
fill={seriesColor}
|
|
254
334
|
fillOpacity={1}
|
|
255
335
|
stroke={seriesColor}
|
|
@@ -258,7 +338,39 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
258
338
|
curve={curve}
|
|
259
339
|
pointerEvents='none'
|
|
260
340
|
/>
|
|
261
|
-
)
|
|
341
|
+
)
|
|
342
|
+
})}
|
|
343
|
+
{barElements}
|
|
344
|
+
{(isAreaChart && !isComboChart ? [] : isComboChart ? lineSeries : series).map((s, i) => {
|
|
345
|
+
const seriesKey = s.dataKey
|
|
346
|
+
const seriesColor = colorScale?.(config.runtime.seriesLabels?.[seriesKey] || seriesKey) || '#666'
|
|
347
|
+
|
|
348
|
+
// Get series-specific styling
|
|
349
|
+
const seriesWeight = s.weight || 2
|
|
350
|
+
const seriesLineType = s.lineType || 'curveLinear'
|
|
351
|
+
const seriesStyle = s.type || 'solid'
|
|
352
|
+
const curve = allCurves[seriesLineType] || allCurves.curveLinear
|
|
353
|
+
const strokeDasharray = handleLineType(seriesStyle)
|
|
354
|
+
|
|
355
|
+
// Use the right-axis scale when the series is on the right axis
|
|
356
|
+
const yScaleForSeries = s.axis === 'Right' && miniRightYScale ? miniRightYScale : miniYScale
|
|
357
|
+
|
|
358
|
+
// Filter to only valid data points, then sort by X position so the line
|
|
359
|
+
// connects left-to-right regardless of the original data order.
|
|
360
|
+
const validData = tableData
|
|
361
|
+
.filter(d => {
|
|
362
|
+
const xVal = xScale(d[dataKey])
|
|
363
|
+
const yVal = parseFloat(d[s.dataKey])
|
|
364
|
+
return xVal !== undefined && !isNaN(yVal)
|
|
365
|
+
})
|
|
366
|
+
.sort((a, b) => (xScale(a[dataKey]) ?? 0) - (xScale(b[dataKey]) ?? 0))
|
|
367
|
+
|
|
368
|
+
if (validData.length === 0) return null
|
|
369
|
+
|
|
370
|
+
const getX = d => xScale(d[dataKey]) + bandwidth / 2
|
|
371
|
+
const getY = d => yScaleForSeries(parseFloat(d[s.dataKey]))
|
|
372
|
+
|
|
373
|
+
return (
|
|
262
374
|
<LinePath
|
|
263
375
|
key={`mini-line-${seriesKey}-${i}`}
|
|
264
376
|
data={validData}
|
|
@@ -45,7 +45,10 @@ import '@cdc/core/components/EditorPanel/EditorPanel.styles.css'
|
|
|
45
45
|
import './editor-panel.scss'
|
|
46
46
|
import { Anchor } from '@cdc/core/types/Axis'
|
|
47
47
|
import EditorPanelContext from './EditorPanelContext'
|
|
48
|
-
import
|
|
48
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
49
|
+
import flatMap from 'lodash/flatMap'
|
|
50
|
+
import keys from 'lodash/keys'
|
|
51
|
+
import uniq from 'lodash/uniq'
|
|
49
52
|
import { adjustedSymbols as symbolCodes } from '@cdc/core/helpers/footnoteSymbols'
|
|
50
53
|
import { updateFieldRankByValue } from './helpers/updateFieldRankByValue'
|
|
51
54
|
import cloneConfig from '@cdc/core/helpers/cloneConfig'
|
|
@@ -55,6 +58,7 @@ import { updateFieldFactory } from '@cdc/core/helpers/updateFieldFactory'
|
|
|
55
58
|
import { paletteMigrationMap, twoColorPaletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
|
|
56
59
|
import { isV1Palette, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
|
|
57
60
|
import { USE_V2_MIGRATION } from '@cdc/core/helpers/constants'
|
|
61
|
+
import { getSeriesOwnedColumnNames } from '../../helpers/seriesColumnSettings'
|
|
58
62
|
|
|
59
63
|
interface PreliminaryProps {
|
|
60
64
|
config: ChartConfig
|
|
@@ -70,7 +74,7 @@ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, dat
|
|
|
70
74
|
const hasComboBarSeries = isCombo && barSeriesExists
|
|
71
75
|
|
|
72
76
|
const getColumnOptions = () => {
|
|
73
|
-
return
|
|
77
|
+
return uniq(flatMap(data, keys))
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
const getTypeOptions = () => {
|
|
@@ -653,6 +657,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
653
657
|
visHasLegendAxisAlign,
|
|
654
658
|
visHasLegendColorCategory,
|
|
655
659
|
visHasSelectableLegendValues,
|
|
660
|
+
visSupportsClickingLegend,
|
|
656
661
|
visSupportsDateCategoryAxis,
|
|
657
662
|
visSupportsDateCategoryAxisLabel,
|
|
658
663
|
visSupportsDateCategoryAxisLine,
|
|
@@ -756,6 +761,10 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
756
761
|
if (isDateScale(updatedConfig.xAxis) && !updatedConfig.xAxis.padding) {
|
|
757
762
|
updatedConfig.xAxis.padding = 0
|
|
758
763
|
}
|
|
764
|
+
// Default Radar charts to a taller height
|
|
765
|
+
if (updatedConfig.visualizationType === 'Radar' && updatedConfig.heights?.vertical <= 400) {
|
|
766
|
+
updatedConfig.heights.vertical = 400
|
|
767
|
+
}
|
|
759
768
|
// DEV-8008 - Remove Bar styling when Line is converted to Bar
|
|
760
769
|
if (updatedConfig.visualizationType === 'Line') {
|
|
761
770
|
updatedConfig.visualizationSubType = 'regular'
|
|
@@ -930,6 +939,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
930
939
|
|
|
931
940
|
// Extract column names from data with memoization (replaces getColumns)
|
|
932
941
|
const allColumns = useDataColumns(dataSourceForColumns)
|
|
942
|
+
const seriesOwnedColumnNames = useMemo(() => getSeriesOwnedColumnNames(config.series), [config.series])
|
|
933
943
|
|
|
934
944
|
// Filter out series columns and confidence key columns (except lower and upper)
|
|
935
945
|
const filteredColumns = useMemo(() => {
|
|
@@ -1514,46 +1524,8 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
1514
1524
|
'Deviation Bar'
|
|
1515
1525
|
]
|
|
1516
1526
|
|
|
1517
|
-
const columnsOptions = [
|
|
1518
|
-
<option value='' key={'Select Option'}>
|
|
1519
|
-
- Select Option -
|
|
1520
|
-
</option>
|
|
1521
|
-
]
|
|
1522
|
-
|
|
1523
|
-
if (config.data && config.series) {
|
|
1524
|
-
Object.keys(config.data?.[0] || []).map(colName => {
|
|
1525
|
-
// OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
|
|
1526
|
-
const found = config?.series.some(series => series.dataKey === colName)
|
|
1527
|
-
if (colName !== config.xAxis.dataKey && !found) {
|
|
1528
|
-
// if not the index then add it
|
|
1529
|
-
return columnsOptions.push(
|
|
1530
|
-
<option value={colName} key={colName}>
|
|
1531
|
-
{colName}
|
|
1532
|
-
</option>
|
|
1533
|
-
)
|
|
1534
|
-
}
|
|
1535
|
-
})
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
// for pie charts
|
|
1539
|
-
if (!config.data && data) {
|
|
1540
|
-
if (!data[0]) return
|
|
1541
|
-
Object.keys(data[0]).map(colName => {
|
|
1542
|
-
// OMIT ANY COLUMNS THAT ARE IN DATA SERIES!
|
|
1543
|
-
const found = data.some(el => el.dataKey === colName)
|
|
1544
|
-
if (colName !== config.xAxis.dataKey && !found) {
|
|
1545
|
-
// if not the index then add it
|
|
1546
|
-
return columnsOptions.push(
|
|
1547
|
-
<option value={colName} key={colName}>
|
|
1548
|
-
{colName}
|
|
1549
|
-
</option>
|
|
1550
|
-
)
|
|
1551
|
-
}
|
|
1552
|
-
})
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
1527
|
const removeAdditionalColumn = columnName => {
|
|
1556
|
-
const newColumns =
|
|
1528
|
+
const newColumns = cloneDeep(config.columns)
|
|
1557
1529
|
|
|
1558
1530
|
delete newColumns[columnName]
|
|
1559
1531
|
|
|
@@ -1892,6 +1864,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
1892
1864
|
</AccordionItem>
|
|
1893
1865
|
)}
|
|
1894
1866
|
<Panels.BoxPlot name='Measures' />
|
|
1867
|
+
<Panels.Radar name='Radar Chart Settings' />
|
|
1895
1868
|
{/* Left Value Axis */}
|
|
1896
1869
|
{visSupportsLeftValueAxis() && (
|
|
1897
1870
|
<AccordionItem>
|
|
@@ -2481,6 +2454,28 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
2481
2454
|
<span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
|
|
2482
2455
|
</>
|
|
2483
2456
|
)}
|
|
2457
|
+
<TextField
|
|
2458
|
+
value={config.yAxis.smallestLeftAxisMax}
|
|
2459
|
+
section='yAxis'
|
|
2460
|
+
fieldName='smallestLeftAxisMax'
|
|
2461
|
+
type='number'
|
|
2462
|
+
label='Smallest Left Axis Maximum'
|
|
2463
|
+
placeholder='Auto'
|
|
2464
|
+
tooltip={
|
|
2465
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2466
|
+
<Tooltip.Target>
|
|
2467
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2468
|
+
</Tooltip.Target>
|
|
2469
|
+
<Tooltip.Content>
|
|
2470
|
+
<p>
|
|
2471
|
+
Example: If your data only goes up to 1, the axis might show 0, 0.2, 0.4, 0.6,
|
|
2472
|
+
0.8, 1. Setting this to 5 would make the axis show 0, 1, 2, 3, 4, 5 instead.
|
|
2473
|
+
</p>
|
|
2474
|
+
</Tooltip.Content>
|
|
2475
|
+
</Tooltip>
|
|
2476
|
+
}
|
|
2477
|
+
updateField={updateFieldDeprecated}
|
|
2478
|
+
/>
|
|
2484
2479
|
</>
|
|
2485
2480
|
)
|
|
2486
2481
|
)}
|
|
@@ -2880,7 +2875,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
2880
2875
|
/>
|
|
2881
2876
|
|
|
2882
2877
|
<TextField
|
|
2883
|
-
value={config.yAxis.
|
|
2878
|
+
value={config.yAxis.rightMax}
|
|
2884
2879
|
section='yAxis'
|
|
2885
2880
|
fieldName='rightMax'
|
|
2886
2881
|
type='number'
|
|
@@ -2890,7 +2885,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
2890
2885
|
/>
|
|
2891
2886
|
<span style={{ color: 'red', display: 'block' }}>{warningMsg.rightMaxMessage}</span>
|
|
2892
2887
|
<TextField
|
|
2893
|
-
value={config.yAxis.
|
|
2888
|
+
value={config.yAxis.rightMin}
|
|
2894
2889
|
section='yAxis'
|
|
2895
2890
|
fieldName='rightMin'
|
|
2896
2891
|
type='number'
|
|
@@ -2899,6 +2894,28 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
2899
2894
|
updateField={updateFieldDeprecated}
|
|
2900
2895
|
/>
|
|
2901
2896
|
<span style={{ color: 'red', display: 'block' }}>{warningMsg.minMsg}</span>
|
|
2897
|
+
<TextField
|
|
2898
|
+
value={config.yAxis.smallestRightAxisMax}
|
|
2899
|
+
section='yAxis'
|
|
2900
|
+
fieldName='smallestRightAxisMax'
|
|
2901
|
+
type='number'
|
|
2902
|
+
label='Smallest Right Axis Maximum'
|
|
2903
|
+
placeholder='Auto'
|
|
2904
|
+
tooltip={
|
|
2905
|
+
<Tooltip style={{ textTransform: 'none' }}>
|
|
2906
|
+
<Tooltip.Target>
|
|
2907
|
+
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
2908
|
+
</Tooltip.Target>
|
|
2909
|
+
<Tooltip.Content>
|
|
2910
|
+
<p>
|
|
2911
|
+
Example: If your data only goes up to 1, the axis might show 0, 0.2, 0.4, 0.6, 0.8, 1.
|
|
2912
|
+
Setting this to 5 would make the axis show 0, 1, 2, 3, 4, 5 instead.
|
|
2913
|
+
</p>
|
|
2914
|
+
</Tooltip.Content>
|
|
2915
|
+
</Tooltip>
|
|
2916
|
+
}
|
|
2917
|
+
updateField={updateFieldDeprecated}
|
|
2918
|
+
/>
|
|
2902
2919
|
</AccordionItemPanel>
|
|
2903
2920
|
</AccordionItem>
|
|
2904
2921
|
)}
|
|
@@ -4137,6 +4154,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
4137
4154
|
config={config}
|
|
4138
4155
|
updateField={updateFieldDeprecated}
|
|
4139
4156
|
deleteColumn={removeAdditionalColumn}
|
|
4157
|
+
hiddenColumnNames={seriesOwnedColumnNames}
|
|
4140
4158
|
/>{' '}
|
|
4141
4159
|
</AccordionItemPanel>
|
|
4142
4160
|
</AccordionItem>
|
|
@@ -4250,7 +4268,10 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
4250
4268
|
/>
|
|
4251
4269
|
|
|
4252
4270
|
<CheckBox
|
|
4253
|
-
display={
|
|
4271
|
+
display={
|
|
4272
|
+
config.visualizationType !== 'Radar' &&
|
|
4273
|
+
config.preliminaryData?.some(pd => pd.label && pd.type === 'suppression' && pd.value)
|
|
4274
|
+
}
|
|
4254
4275
|
value={config.legend.hideSuppressedLabels}
|
|
4255
4276
|
section='legend'
|
|
4256
4277
|
fieldName='hideSuppressedLabels'
|
|
@@ -4274,7 +4295,10 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
4274
4295
|
}
|
|
4275
4296
|
/>
|
|
4276
4297
|
<CheckBox
|
|
4277
|
-
display={
|
|
4298
|
+
display={
|
|
4299
|
+
config.visualizationType !== 'Radar' &&
|
|
4300
|
+
config.preliminaryData?.some(pd => pd.label && pd.type === 'suppression' && pd.value)
|
|
4301
|
+
}
|
|
4278
4302
|
value={config.legend.hideSuppressionLink}
|
|
4279
4303
|
section='legend'
|
|
4280
4304
|
fieldName='hideSuppressionLink'
|
|
@@ -4296,7 +4320,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
4296
4320
|
/>
|
|
4297
4321
|
|
|
4298
4322
|
<Select
|
|
4299
|
-
display={hasDynamicCategory || hasMultipleSeries}
|
|
4323
|
+
display={visSupportsClickingLegend() && (hasDynamicCategory || hasMultipleSeries)}
|
|
4300
4324
|
value={config.legend.behavior}
|
|
4301
4325
|
section='legend'
|
|
4302
4326
|
fieldName='behavior'
|
|
@@ -4580,14 +4604,17 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
|
|
|
4580
4604
|
)}
|
|
4581
4605
|
<Panels.Annotate name='Text Annotations' />
|
|
4582
4606
|
{/* {(config.visualizationType === 'Bar' || config.visualizationType === 'Line') && <Panels.DateHighlighting name='Date Highlighting' />} */}
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4607
|
+
{config.visualizationType !== 'Radar' && (
|
|
4608
|
+
<PanelMarkup
|
|
4609
|
+
name='Markup Variables'
|
|
4610
|
+
markupVariables={config.markupVariables || []}
|
|
4611
|
+
data={rawData}
|
|
4612
|
+
enableMarkupVariables={config.enableMarkupVariables || false}
|
|
4613
|
+
onMarkupVariablesChange={variables => updateField(null, null, 'markupVariables', variables)}
|
|
4614
|
+
onToggleEnable={enabled => updateField(null, null, 'enableMarkupVariables', enabled)}
|
|
4615
|
+
dataMetadata={config.dataMetadata}
|
|
4616
|
+
/>
|
|
4617
|
+
)}
|
|
4591
4618
|
<Panels.SmallMultiples name='Small Multiples' />
|
|
4592
4619
|
</Accordion>
|
|
4593
4620
|
{config.type !== 'Spark Line' && (
|