@cdc/chart 4.26.6 → 4.26.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/CONFIG.md +504 -368
- package/dist/cdcchart-CYc-Kayd.es.js +22488 -0
- package/dist/cdcchart-DCKn8NmI.es.js +20695 -0
- package/dist/cdcchart-Dyui9nk_.es.js +45828 -0
- package/dist/cdcchart.js +4 -96498
- package/examples/__data__/vega-remote-data.json +6 -0
- package/examples/dendrogram.json +18 -0
- package/examples/feature/bar/bar-chart-race-annual-change.json +68 -0
- package/examples/feature/heatmap/average-age-categorical.json +2 -2
- package/examples/feature/heatmap/calendar-heatmap.json +2 -2
- package/examples/feature/heatmap/cell-values-bottom-axis.json +2 -2
- package/examples/feature/heatmap/sparse-aggregation.json +2 -2
- package/examples/feature/line/line-chart-race.json +50 -0
- package/examples/feature/sankey/sankey-example-data.json +40 -1441
- package/examples/feature/sankey/sankey_chart_data.csv +9 -20
- package/examples/network.json +31 -0
- package/examples/private/box-plot.json +573 -0
- package/examples/private/custom-color-issue.json +625 -0
- package/examples/private/custom-colors.json +522 -0
- package/examples/private/cutoff.json +10750 -0
- package/examples/private/flueview.json +22051 -0
- package/examples/private/overlay-legend.json +485 -0
- package/examples/private/pie-test.json +469 -0
- package/examples/vega-remote.json +22 -0
- package/package.json +11 -13
- package/src/CdcChart.tsx +2 -2
- package/src/CdcChartComponent.tsx +444 -217
- package/src/_stories/Chart.BarRace.stories.tsx +443 -0
- package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
- package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
- package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
- package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
- package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
- package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
- package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
- package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
- package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
- package/src/_stories/Chart.LineRace.stories.tsx +149 -0
- package/src/_stories/Chart.Network.stories.tsx +432 -0
- package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
- package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
- package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
- package/src/_stories/Chart.PieLabels.stories.tsx +49 -0
- package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
- package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
- package/src/_stories/Chart.Sankey.stories.tsx +417 -0
- package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
- package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
- package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
- package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
- package/src/_stories/Chart.smoke.stories.tsx +14 -1
- package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
- package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
- package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
- package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
- package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
- package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
- package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
- package/src/_stories/ChartPrefixSuffix.smoke.stories.tsx +28 -1
- package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
- package/src/_stories/_mock/editor-test-configs.json +20 -10
- package/src/_stories/_mock/editor-test-datasets.json +38 -42
- package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
- package/src/_stories/_mock/heatmap.json +2 -2
- package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
- package/src/_stories/_mock/sankey_mock.json +23 -27
- package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
- package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
- package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
- package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
- package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
- package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
- package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
- package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
- package/src/components/Axis/BottomAxis.tsx +1 -1
- package/src/components/Axis/Categorical.Axis.tsx +3 -3
- package/src/components/Axis/RightAxis.tsx +21 -13
- package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -8
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +13 -4
- package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +30 -26
- package/src/components/BarChart/components/BarChart.Vertical.tsx +38 -28
- package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
- package/src/components/BarChart/components/context.tsx +6 -0
- package/src/components/BarChart/helpers/getBarData.ts +28 -28
- package/src/components/BarChart/helpers/getBarHeights.ts +2 -2
- package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
- package/src/components/BarChart/helpers/index.ts +8 -0
- package/src/components/BarChart/helpers/portionPattern.ts +243 -0
- package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
- package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
- package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
- package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +202 -0
- package/src/components/BarChart/helpers/useBarChart.ts +60 -60
- package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
- package/src/components/BarChartRace/BarChartRace.tsx +230 -0
- package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
- package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
- package/src/components/BarChartRace/bar-chart-race.css +160 -0
- package/src/components/BarChartRace/helpers.test.ts +114 -0
- package/src/components/BarChartRace/helpers.ts +185 -0
- package/src/components/BarChartRace/index.ts +3 -0
- package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +7 -6
- package/src/components/BoxPlot/BoxPlot.Vertical.tsx +9 -7
- package/src/components/BoxPlot/helpers/index.ts +13 -83
- package/src/components/Brush/BrushSelector.tsx +10 -24
- package/src/components/Brush/MiniChartPreview.tsx +33 -1
- package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
- package/src/components/Dendrogram/dendrogram.css +113 -0
- package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
- package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
- package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
- package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
- package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
- package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
- package/src/components/Dendrogram/index.tsx +1 -0
- package/src/components/Dendrogram/types/index.ts +66 -0
- package/src/components/EditorPanel/EditorPanel.tsx +1111 -805
- package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
- package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
- package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
- package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
- package/src/components/EditorPanel/components/Panels/Panel.General.tsx +104 -61
- package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
- package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
- package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +236 -36
- package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
- package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
- package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +21 -6
- package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
- package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
- package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
- package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
- package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
- package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
- package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
- package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
- package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +5 -4
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +94 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +75 -25
- package/src/components/Forecasting/Forecasting.tsx +16 -7
- package/src/components/HeatMap/components/HeatMap.tsx +511 -178
- package/src/components/HeatMap/heatmap.constants.ts +7 -3
- package/src/components/HeatMap/heatmap.css +101 -2
- package/src/components/HeatMap/helpers/layout.ts +80 -0
- package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
- package/src/components/HeatMap/tests/helpers.test.ts +79 -0
- package/src/components/Legend/Legend.Component.tsx +163 -148
- package/src/components/Legend/Legend.Suppression.tsx +3 -3
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -35
- package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
- package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
- package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
- package/src/components/LineChart/helpers.test.ts +126 -38
- package/src/components/LineChart/helpers.ts +9 -6
- package/src/components/LineChart/index.tsx +71 -27
- package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
- package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
- package/src/components/LineChartRace/LineChartRace.tsx +134 -0
- package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
- package/src/components/LineChartRace/helpers.test.ts +88 -0
- package/src/components/LineChartRace/helpers.ts +157 -0
- package/src/components/LineChartRace/index.ts +3 -0
- package/src/components/LineChartRace/line-chart-race.css +16 -0
- package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
- package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
- package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
- package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
- package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
- package/src/components/LinearChart.tsx +85 -54
- package/src/components/Network/components/Network.tsx +372 -0
- package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
- package/src/components/Network/helpers/layoutNetwork.ts +87 -0
- package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
- package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
- package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
- package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
- package/src/components/Network/index.tsx +3 -0
- package/src/components/Network/network.css +107 -0
- package/src/components/Network/types/index.ts +56 -0
- package/src/components/PairedBarChart.jsx +5 -10
- package/src/components/PairedBarChart.test.tsx +71 -0
- package/src/components/PieChart/PieChart.tsx +121 -13
- package/src/components/PieChart/helpers/labelPlacement.test.ts +88 -0
- package/src/components/PieChart/helpers/labelPlacement.ts +95 -0
- package/src/components/RacingChartRenderer.tsx +44 -0
- package/src/components/Regions/components/Regions.test.tsx +179 -0
- package/src/components/Regions/components/Regions.tsx +104 -69
- package/src/components/Regions/index.tsx +2 -3
- package/src/components/Sankey/components/Sankey.tsx +264 -437
- package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
- package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
- package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
- package/src/components/Sankey/sankey.scss +57 -116
- package/src/components/Sankey/types/index.ts +41 -4
- package/src/components/ScatterPlot/ScatterPlot.jsx +28 -12
- package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
- package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
- package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
- package/src/components/raceTiming.ts +18 -0
- package/src/data/initial-state.js +79 -31
- package/src/helpers/applyChartTypeContract.ts +71 -0
- package/src/helpers/axisLabelFontSize.ts +2 -2
- package/src/helpers/boxPlotStats.ts +60 -0
- package/src/helpers/bumpChartTooltip.ts +2 -1
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
- package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
- package/src/helpers/calculateRightYAxisWidth.ts +60 -0
- package/src/helpers/categoryOrder.ts +60 -0
- package/src/helpers/colorAssignmentHelpers.ts +2 -2
- package/src/helpers/createNewChartConfig.ts +56 -0
- package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
- package/src/helpers/forecastLegend.ts +88 -0
- package/src/helpers/getBoxPlotConfig.ts +26 -38
- package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
- package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
- package/src/helpers/getColorScale.ts +31 -22
- package/src/helpers/getComboChartConfig.ts +0 -1
- package/src/helpers/getDynamicYAxisCategories.ts +69 -0
- package/src/helpers/getExcludedData.ts +0 -1
- package/src/helpers/getHasBoundarySuppression.ts +4 -3
- package/src/helpers/getMinMax.ts +11 -8
- package/src/helpers/getNewRuntime.ts +4 -3
- package/src/helpers/getPiePercent.ts +3 -3
- package/src/helpers/getStackedSeriesMax.ts +51 -0
- package/src/helpers/getV21ChartDistributionColors.ts +57 -0
- package/src/helpers/handleRankByValue.ts +15 -15
- package/src/helpers/isConvertLineToBarGraph.ts +2 -2
- package/src/helpers/seriesColumnSettings.ts +31 -3
- package/src/helpers/sizeHelpers.ts +4 -5
- package/src/helpers/smallMultiplesHelpers.ts +10 -13
- package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
- package/src/helpers/tests/boxPlotStats.test.ts +31 -0
- package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
- package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
- package/src/helpers/tests/categoryOrder.test.ts +57 -0
- package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
- package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
- package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
- package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
- package/src/helpers/tests/getColorScale.test.ts +226 -1
- package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
- package/src/helpers/tests/getMinMax.test.ts +41 -0
- package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
- package/src/helpers/tests/handleRankByValue.test.ts +55 -37
- package/src/helpers/tests/seriesColumnSettings.test.ts +83 -0
- package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
- package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
- package/src/helpers/tooltipHelpers.ts +16 -0
- package/src/hooks/tests/useRightAxis.test.tsx +143 -2
- package/src/hooks/tests/useScales.test.tsx +3 -6
- package/src/hooks/tests/useTooltip.test.tsx +484 -0
- package/src/hooks/usePrefersReducedMotion.ts +22 -0
- package/src/hooks/useReduceData.ts +4 -36
- package/src/hooks/useRightAxis.ts +19 -26
- package/src/hooks/useScales.ts +15 -0
- package/src/hooks/useTooltip.tsx +106 -71
- package/src/scss/main.scss +74 -2
- package/src/store/chart.reducer.ts +4 -4
- package/src/test/CdcChart.dataTable.test.tsx +582 -14
- package/src/test/CdcChart.dendrogram.test.tsx +277 -0
- package/src/test/CdcChart.finalization.test.tsx +143 -0
- package/src/test/CdcChart.network.test.tsx +271 -0
- package/src/types/ChartConfig.ts +91 -54
- package/src/types/ChartContext.ts +5 -0
- package/src/types/Horizon.ts +1 -1
- package/LICENSE +0 -201
- package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
- package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
- package/src/components/Sankey/components/ColumnList.tsx +0 -19
- package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
- package/src/components/Sankey/useSankeyAlert.tsx +0 -60
- package/src/data/legacy-defaults.ts +0 -18
- package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
- package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +0 -30
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export type ForecastConfidenceInterval = {
|
|
2
|
+
high?: string | number
|
|
3
|
+
low?: string | number
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type ForecastConfidenceIntervalGroup = {
|
|
7
|
+
intervalIndexes: number[]
|
|
8
|
+
key: string
|
|
9
|
+
label: string
|
|
10
|
+
opacity: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const FORECAST_CI_KEY_PREFIX = 'forecast-ci:'
|
|
14
|
+
|
|
15
|
+
const getSharedPercentage = ({ high, low }: ForecastConfidenceInterval): number | undefined => {
|
|
16
|
+
const highMatches = String(high).match(/\d+(?:\.\d+)?/g)
|
|
17
|
+
const lowMatches = String(low).match(/\d+(?:\.\d+)?/g)
|
|
18
|
+
|
|
19
|
+
if (highMatches?.length !== 1 || lowMatches?.length !== 1) return
|
|
20
|
+
|
|
21
|
+
const highPercentage = Number(highMatches[0])
|
|
22
|
+
const lowPercentage = Number(lowMatches[0])
|
|
23
|
+
|
|
24
|
+
return highPercentage === lowPercentage && highPercentage >= 0 && highPercentage <= 100 ? highPercentage : undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const getForecastConfidenceIntervalGroups = (
|
|
28
|
+
confidenceIntervals: ForecastConfidenceInterval[] = []
|
|
29
|
+
): ForecastConfidenceIntervalGroup[] => {
|
|
30
|
+
const completeIntervals = confidenceIntervals
|
|
31
|
+
.map((interval, configIndex) => ({ interval, configIndex, percentage: getSharedPercentage(interval) }))
|
|
32
|
+
.filter(({ interval }) => String(interval.high ?? '').trim() && String(interval.low ?? '').trim())
|
|
33
|
+
|
|
34
|
+
if (completeIntervals.length <= 1) return []
|
|
35
|
+
|
|
36
|
+
const orderedIntervals = completeIntervals.sort((a, b) => {
|
|
37
|
+
if (a.percentage !== undefined && b.percentage !== undefined) return a.percentage - b.percentage
|
|
38
|
+
if (a.percentage !== undefined) return -1
|
|
39
|
+
if (b.percentage !== undefined) return 1
|
|
40
|
+
return a.configIndex - b.configIndex
|
|
41
|
+
})
|
|
42
|
+
const groups: ForecastConfidenceIntervalGroup[] = []
|
|
43
|
+
const percentageGroups = new Map<number, ForecastConfidenceIntervalGroup>()
|
|
44
|
+
let fallbackIndex = 0
|
|
45
|
+
|
|
46
|
+
orderedIntervals.forEach((interval, displayIndex) => {
|
|
47
|
+
if (interval.percentage !== undefined) {
|
|
48
|
+
const existingGroup = percentageGroups.get(interval.percentage)
|
|
49
|
+
if (existingGroup) {
|
|
50
|
+
existingGroup.intervalIndexes.push(interval.configIndex)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const fallbackNumber = interval.percentage === undefined ? ++fallbackIndex : undefined
|
|
56
|
+
const group = {
|
|
57
|
+
intervalIndexes: [interval.configIndex],
|
|
58
|
+
key: interval.percentage !== undefined ? `percentage:${interval.percentage}` : `interval:${interval.configIndex}`,
|
|
59
|
+
label: interval.percentage !== undefined ? `${interval.percentage}% CI` : `CI ${fallbackNumber}`,
|
|
60
|
+
opacity: 1 - Math.pow(1 - 0.5, completeIntervals.length - displayIndex)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
groups.push(group)
|
|
64
|
+
if (interval.percentage !== undefined) percentageGroups.set(interval.percentage, group)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return groups
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const getForecastLegendKey = (stageKey: string, intervalKey: string): string =>
|
|
71
|
+
`${FORECAST_CI_KEY_PREFIX}${encodeURIComponent(stageKey)}:${encodeURIComponent(intervalKey)}`
|
|
72
|
+
|
|
73
|
+
export const getForecastConfidenceIntervalKey = (
|
|
74
|
+
confidenceIntervals: ForecastConfidenceInterval[] = [],
|
|
75
|
+
intervalIndex: number
|
|
76
|
+
): string | undefined =>
|
|
77
|
+
getForecastConfidenceIntervalGroups(confidenceIntervals).find(group => group.intervalIndexes.includes(intervalIndex))
|
|
78
|
+
?.key
|
|
79
|
+
|
|
80
|
+
export const isForecastLegendSelectionActive = (
|
|
81
|
+
seriesHighlight: string[],
|
|
82
|
+
stageKey: string,
|
|
83
|
+
intervalKey?: string
|
|
84
|
+
): boolean => {
|
|
85
|
+
if (!seriesHighlight.length) return true
|
|
86
|
+
if (seriesHighlight.includes(stageKey)) return true
|
|
87
|
+
return intervalKey ? seriesHighlight.includes(getForecastLegendKey(stageKey, intervalKey)) : false
|
|
88
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import capitalize from 'lodash/capitalize'
|
|
2
|
+
import flatMap from 'lodash/flatMap'
|
|
3
|
+
import map from 'lodash/map'
|
|
4
|
+
import round from 'lodash/round'
|
|
5
|
+
import uniq from 'lodash/uniq'
|
|
6
|
+
import { calculateBoxPlotStats } from './boxPlotStats'
|
|
2
7
|
import { ChartConfig } from '../types/ChartConfig'
|
|
3
|
-
import * as d3 from 'd3-array'
|
|
4
8
|
|
|
5
9
|
export const getBoxPlotConfig = (newConfig: ChartConfig, data: object[]) => {
|
|
6
10
|
const combinedData = data
|
|
7
|
-
const groups =
|
|
8
|
-
const seriesKeys =
|
|
11
|
+
const groups = uniq(map(combinedData, newConfig.xAxis.dataKey))
|
|
12
|
+
const seriesKeys = map(newConfig.series, 'dataKey')
|
|
9
13
|
const plots: any[] = []
|
|
10
14
|
|
|
11
15
|
groups.forEach(g => {
|
|
@@ -13,44 +17,28 @@ export const getBoxPlotConfig = (newConfig: ChartConfig, data: object[]) => {
|
|
|
13
17
|
try {
|
|
14
18
|
if (!g) throw new Error('No groups resolved in box plots')
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
// Filter by xAxis data key
|
|
19
|
-
.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
20
|
-
// perform multiple operations on the filtered data
|
|
21
|
-
.thru(filteredData => ({
|
|
22
|
-
count: filteredData.length,
|
|
23
|
-
sortedData: _.map(filteredData, item => Number(item[seriesKey])).sort()
|
|
24
|
-
}))
|
|
25
|
-
// get the results from the chain
|
|
26
|
-
.value()
|
|
20
|
+
const filteredData = combinedData.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
21
|
+
const stats = calculateBoxPlotStats(map(filteredData, item => item[seriesKey]))
|
|
27
22
|
|
|
28
|
-
if (!
|
|
23
|
+
if (!stats) throw new Error('boxplots dont have data yet')
|
|
29
24
|
if (!plots) throw new Error('boxplots dont have plots yet')
|
|
30
25
|
|
|
31
|
-
const q1 = d3.quantile(sortedData, 0.25)
|
|
32
|
-
const q3 = d3.quantile(sortedData, 0.75)
|
|
33
|
-
|
|
34
|
-
const iqr = q3 - q1
|
|
35
|
-
const lowerBounds = q1 - 1.5 * iqr
|
|
36
|
-
const upperBounds = q3 + 1.5 * iqr
|
|
37
|
-
const nonOutliers = sortedData.filter(value => value >= lowerBounds && value <= upperBounds)
|
|
38
26
|
plots.push({
|
|
39
27
|
columnCategory: g,
|
|
40
|
-
columnMax:
|
|
41
|
-
columnThirdQuartile:
|
|
42
|
-
columnMedian: Number(
|
|
43
|
-
columnFirstQuartile:
|
|
44
|
-
columnMin:
|
|
45
|
-
columnCount:
|
|
46
|
-
columnSd: Number(
|
|
47
|
-
columnMean: Number(
|
|
48
|
-
columnIqr:
|
|
49
|
-
values:
|
|
50
|
-
columnLowerBounds:
|
|
51
|
-
columnUpperBounds:
|
|
52
|
-
columnOutliers:
|
|
53
|
-
columnNonOutliers:
|
|
28
|
+
columnMax: stats.whiskerMax,
|
|
29
|
+
columnThirdQuartile: round(stats.q3, newConfig.dataFormat.roundTo),
|
|
30
|
+
columnMedian: Number(stats.median).toFixed(newConfig.dataFormat.roundTo),
|
|
31
|
+
columnFirstQuartile: round(stats.q1, newConfig.dataFormat.roundTo),
|
|
32
|
+
columnMin: stats.whiskerMin,
|
|
33
|
+
columnCount: stats.values.length,
|
|
34
|
+
columnSd: Number(stats.deviation).toFixed(newConfig.dataFormat.roundTo),
|
|
35
|
+
columnMean: Number(stats.mean).toFixed(newConfig.dataFormat.roundTo),
|
|
36
|
+
columnIqr: round(stats.iqr, newConfig.dataFormat.roundTo),
|
|
37
|
+
values: stats.values,
|
|
38
|
+
columnLowerBounds: stats.lowerBound,
|
|
39
|
+
columnUpperBounds: stats.upperBound,
|
|
40
|
+
columnOutliers: stats.outliers,
|
|
41
|
+
columnNonOutliers: stats.nonOutliers
|
|
54
42
|
})
|
|
55
43
|
} catch (e) {
|
|
56
44
|
console.error('COVE: ', e.message) // eslint-disable-line
|
|
@@ -61,7 +49,7 @@ export const getBoxPlotConfig = (newConfig: ChartConfig, data: object[]) => {
|
|
|
61
49
|
// Generate a flat list of categories based on seriesKeys and groups
|
|
62
50
|
const categories =
|
|
63
51
|
seriesKeys.length > 1
|
|
64
|
-
?
|
|
52
|
+
? flatMap(groups, value => map(seriesKeys, key => `${capitalize(key)} - ${capitalize(value)}`))
|
|
65
53
|
: groups
|
|
66
54
|
|
|
67
55
|
return [plots, categories]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Palette } from '@cdc/core/types/Palette'
|
|
2
|
+
|
|
3
|
+
const paletteDefaultsByVisualizationType: Record<string, Palette> = {
|
|
4
|
+
Line: {
|
|
5
|
+
isReversed: false,
|
|
6
|
+
version: '2.1',
|
|
7
|
+
name: 'qualitative_standard'
|
|
8
|
+
},
|
|
9
|
+
'Horizon Chart': {
|
|
10
|
+
isReversed: false,
|
|
11
|
+
version: '2.1',
|
|
12
|
+
name: 'sequential_blue'
|
|
13
|
+
},
|
|
14
|
+
HeatMap: {
|
|
15
|
+
isReversed: false,
|
|
16
|
+
version: '2.1',
|
|
17
|
+
name: 'sequential_blue'
|
|
18
|
+
},
|
|
19
|
+
Sankey: {
|
|
20
|
+
isReversed: true,
|
|
21
|
+
version: '2.1',
|
|
22
|
+
name: 'sequential_bluereverse'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const getChartTypeDefaultPalette = (visualizationType?: string): Palette | undefined => {
|
|
27
|
+
const palette = visualizationType ? paletteDefaultsByVisualizationType[visualizationType] : undefined
|
|
28
|
+
return palette ? { ...palette } : undefined
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getPaletteColors } from '@cdc/core/helpers/palettes/utils'
|
|
2
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
3
|
+
import { getV21ChartDistributionColors } from './getV21ChartDistributionColors'
|
|
4
|
+
|
|
5
|
+
export const getColorCodeCategoryColorMap = (
|
|
6
|
+
config: ChartConfig,
|
|
7
|
+
tableData: Record<string, unknown>[],
|
|
8
|
+
colorPalettes: Record<string, string[]>
|
|
9
|
+
): Map<unknown, string> => {
|
|
10
|
+
const colorCode = config.legend?.colorCode
|
|
11
|
+
if (!colorCode || !Array.isArray(tableData)) return new Map()
|
|
12
|
+
|
|
13
|
+
let palette = getPaletteColors(config, colorPalettes)
|
|
14
|
+
if (!palette.length) return new Map()
|
|
15
|
+
|
|
16
|
+
const categoryColors = new Map<unknown, string>()
|
|
17
|
+
|
|
18
|
+
const categories = Array.from(new Set(tableData.map(row => row[colorCode])))
|
|
19
|
+
palette = getV21ChartDistributionColors(config, palette, categories.length) ?? palette
|
|
20
|
+
|
|
21
|
+
categories.forEach((category, index) => {
|
|
22
|
+
categoryColors.set(category, palette[index % palette.length])
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return categoryColors
|
|
26
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { twoColorPalette } from '@cdc/core/data/colorPalettes'
|
|
2
2
|
import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
|
|
3
|
-
import {
|
|
3
|
+
import { getColorPaletteMajorVersion } from '@cdc/core/helpers/getColorPaletteMajorVersion'
|
|
4
4
|
import { scaleOrdinal } from '@visx/scale'
|
|
5
5
|
import { ChartConfig } from '../types/ChartConfig'
|
|
6
6
|
import { paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
|
|
@@ -10,7 +10,17 @@ import {
|
|
|
10
10
|
divergentColorDistribution,
|
|
11
11
|
colorblindColorDistribution
|
|
12
12
|
} from '@cdc/core/helpers/palettes/colorDistributions'
|
|
13
|
+
import { isValidPaletteColor } from '@cdc/core/helpers/palettes/colorValidation'
|
|
13
14
|
import { applySeriesColorAssignmentsToRange } from './colorAssignmentHelpers'
|
|
15
|
+
import { getV21ChartDistributionColors } from './getV21ChartDistributionColors'
|
|
16
|
+
|
|
17
|
+
const INVALID_CUSTOM_COLOR_FALLBACK = '#000000'
|
|
18
|
+
|
|
19
|
+
const normalizeCustomColors = (colors: unknown): string[] => {
|
|
20
|
+
if (!Array.isArray(colors) || colors.length === 0) return []
|
|
21
|
+
|
|
22
|
+
return colors.map(color => (isValidPaletteColor(color) ? color.trim() : INVALID_CUSTOM_COLOR_FALLBACK))
|
|
23
|
+
}
|
|
14
24
|
|
|
15
25
|
export const getColorScale = (config: ChartConfig): ((value: string) => string) => {
|
|
16
26
|
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
@@ -19,7 +29,7 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
19
29
|
const colorPalettes = filterChartColorPalettes(config)
|
|
20
30
|
|
|
21
31
|
// Get the correct version of two-color palettes
|
|
22
|
-
const version =
|
|
32
|
+
const version = getColorPaletteMajorVersion(config)
|
|
23
33
|
const versionKey = `v${version}`
|
|
24
34
|
const versionedTwoColorPalette = twoColorPalette[versionKey] || twoColorPalette.v2
|
|
25
35
|
|
|
@@ -28,17 +38,24 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
28
38
|
? versionedTwoColorPalette
|
|
29
39
|
: colorPalettes
|
|
30
40
|
|
|
31
|
-
const allPalettes: Record<string, string[]> = { ...versionedTwoColorPalette, ...colorPalettes }
|
|
32
|
-
|
|
33
41
|
// Migrate old palette name if needed
|
|
34
42
|
const migratedPaletteName = configPalette ? configPalette : getFallbackColorPalette(config)
|
|
43
|
+
let namedPalette =
|
|
44
|
+
palettesSource[migratePaletteWithMap(migratedPaletteName, paletteMigrationMap, false)] ||
|
|
45
|
+
palettesSource[configPalette]
|
|
46
|
+
|
|
47
|
+
// Use a visible sentinel when saved palette config cannot resolve.
|
|
48
|
+
if (!namedPalette) {
|
|
49
|
+
console.warn(`Palette "${configPalette}" not found, falling back to black`)
|
|
50
|
+
namedPalette = [INVALID_CUSTOM_COLOR_FALLBACK]
|
|
51
|
+
}
|
|
35
52
|
|
|
36
53
|
const domain = config.runtime.seriesLabelsAll
|
|
37
54
|
|
|
38
|
-
const customColorsOrdered = config.general?.palette?.customColorsOrdered
|
|
55
|
+
const customColorsOrdered = normalizeCustomColors(config.general?.palette?.customColorsOrdered)
|
|
39
56
|
|
|
40
57
|
// Check for customColorsOrdered first (direct 1-to-1 mapping, no distribution needed)
|
|
41
|
-
if (
|
|
58
|
+
if (customColorsOrdered.length > 0) {
|
|
42
59
|
const range = applySeriesColorAssignmentsToRange(config, domain, customColorsOrdered)
|
|
43
60
|
return scaleOrdinal({
|
|
44
61
|
domain,
|
|
@@ -47,26 +64,19 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
47
64
|
})
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
const customColors = config.general?.palette?.customColors
|
|
51
|
-
const isUsingCustomColors =
|
|
52
|
-
let palette =
|
|
53
|
-
(isUsingCustomColors ? customColors : undefined) ||
|
|
54
|
-
palettesSource[migratePaletteWithMap(migratedPaletteName, paletteMigrationMap, false)] ||
|
|
55
|
-
palettesSource[configPalette]
|
|
56
|
-
|
|
57
|
-
// Fallback to a default palette if none found
|
|
58
|
-
if (!palette) {
|
|
59
|
-
console.warn(`Palette "${configPalette}" not found, falling back to default`)
|
|
60
|
-
palette = Object.values(allPalettes)[0] || ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
|
|
61
|
-
}
|
|
67
|
+
const customColors = normalizeCustomColors(config.general?.palette?.customColors)
|
|
68
|
+
const isUsingCustomColors = customColors.length > 0
|
|
69
|
+
let palette = isUsingCustomColors ? customColors : namedPalette
|
|
62
70
|
|
|
63
71
|
let numberOfKeys = config.runtime.seriesKeys.length
|
|
64
72
|
|
|
65
73
|
// Apply enhanced color distribution (same logic as pie charts)
|
|
66
|
-
const paletteVersion =
|
|
74
|
+
const paletteVersion = getColorPaletteMajorVersion(config)
|
|
75
|
+
const v21DistributionColors = getV21ChartDistributionColors(config, palette, numberOfKeys)
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
if (v21DistributionColors) {
|
|
78
|
+
palette = v21DistributionColors
|
|
79
|
+
} else if (isUsingCustomColors || paletteVersion !== 2 || numberOfKeys > 9 || palette.length !== 9) {
|
|
70
80
|
// Use existing logic for v1 palettes and other cases
|
|
71
81
|
while (numberOfKeys > palette.length) {
|
|
72
82
|
palette = palette.concat(palette)
|
|
@@ -78,7 +88,6 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
78
88
|
const isDivergent = configPalette && configPalette.includes('divergent')
|
|
79
89
|
const isColorblindSafe =
|
|
80
90
|
configPalette && (configPalette.includes('colorblindsafe') || configPalette.includes('qualitative_standard'))
|
|
81
|
-
|
|
82
91
|
// Determine which distribution to use based on palette type
|
|
83
92
|
let distributionMap = null
|
|
84
93
|
if (isDivergent) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Axis } from '@cdc/core/types/Axis'
|
|
2
|
+
|
|
3
|
+
export type DynamicYAxisCategory = {
|
|
4
|
+
label: string
|
|
5
|
+
upperBoundKey?: string
|
|
6
|
+
color?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type DynamicYAxisCategoriesConfig = {
|
|
10
|
+
categories: DynamicYAxisCategory[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type DynamicYAxisCategoriesParams = {
|
|
14
|
+
config?: DynamicYAxisCategoriesConfig
|
|
15
|
+
data?: Record<string, any>[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type DynamicYAxisCategoriesResult = {
|
|
19
|
+
categories: NonNullable<Axis['categories']>
|
|
20
|
+
axisMax: number | null
|
|
21
|
+
} | null
|
|
22
|
+
|
|
23
|
+
const toFiniteNumber = value => {
|
|
24
|
+
const number = Number(value)
|
|
25
|
+
return Number.isFinite(number) ? number : null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const getDynamicYAxisCategories = ({
|
|
29
|
+
config,
|
|
30
|
+
data = []
|
|
31
|
+
}: DynamicYAxisCategoriesParams): DynamicYAxisCategoriesResult => {
|
|
32
|
+
if (!config || !data.length || !config.categories?.length) return null
|
|
33
|
+
|
|
34
|
+
const currentRow = data.find(row =>
|
|
35
|
+
config.categories.every(
|
|
36
|
+
(category, index) =>
|
|
37
|
+
(index === config.categories.length - 1 && !category.upperBoundKey) ||
|
|
38
|
+
toFiniteNumber(row?.[category.upperBoundKey]) !== null
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
if (!currentRow) return null
|
|
42
|
+
|
|
43
|
+
const upperBounds = config.categories.map(category =>
|
|
44
|
+
category.upperBoundKey ? toFiniteNumber(currentRow[category.upperBoundKey]) : null
|
|
45
|
+
)
|
|
46
|
+
const lastCategory = config.categories[config.categories.length - 1]
|
|
47
|
+
const hasOpenEndedFinalCategory = !lastCategory.upperBoundKey
|
|
48
|
+
const axisMax = hasOpenEndedFinalCategory ? null : upperBounds[upperBounds.length - 1]
|
|
49
|
+
const resolvedUpperBounds = hasOpenEndedFinalCategory ? upperBounds.slice(0, -1) : upperBounds
|
|
50
|
+
|
|
51
|
+
if (resolvedUpperBounds.some(bound => bound === null)) return null
|
|
52
|
+
if (!hasOpenEndedFinalCategory && (axisMax === null || axisMax <= 0)) return null
|
|
53
|
+
|
|
54
|
+
const numericUpperBounds = resolvedUpperBounds as number[]
|
|
55
|
+
if (numericUpperBounds.some((bound, index) => index > 0 && bound <= numericUpperBounds[index - 1])) {
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const categories = config.categories.map((category, index) => ({
|
|
60
|
+
label: category.label,
|
|
61
|
+
height:
|
|
62
|
+
hasOpenEndedFinalCategory && index === config.categories.length - 1
|
|
63
|
+
? ''
|
|
64
|
+
: String(numericUpperBounds[index] - (numericUpperBounds[index - 1] || 0)),
|
|
65
|
+
color: category.color || '#f1f1f1'
|
|
66
|
+
}))
|
|
67
|
+
|
|
68
|
+
return { categories, axisMax }
|
|
69
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pick from 'lodash/pick'
|
|
2
|
+
import values from 'lodash/values'
|
|
2
3
|
import { ChartConfig } from '../types/ChartConfig'
|
|
3
4
|
|
|
4
5
|
type GetHasBoundarySuppressionProps = {
|
|
@@ -15,9 +16,9 @@ export const getHasBoundarySuppression = ({ rows, config }: GetHasBoundarySuppre
|
|
|
15
16
|
|
|
16
17
|
return boundaryRows.some(item =>
|
|
17
18
|
suppressionRules.some(({ column, value }) => {
|
|
18
|
-
const
|
|
19
|
+
const seriesValues = values(pick(item, config.runtime?.seriesKeys))
|
|
19
20
|
const dynamicCategory = config.series[0]?.dynamicCategory
|
|
20
|
-
const match = column ? item[column] === value :
|
|
21
|
+
const match = column ? item[column] === value : seriesValues.includes(value)
|
|
21
22
|
const dynamic = dynamicCategory && (item[dynamicCategory] === column || !column)
|
|
22
23
|
|
|
23
24
|
return Boolean(match || dynamic)
|
package/src/helpers/getMinMax.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ChartConfig } from '../types/ChartConfig'
|
|
2
2
|
import { getCleanTopTickMax } from './getCleanTopTickMax'
|
|
3
3
|
import { getAxisMaxOverride } from './getAxisMaxOverride'
|
|
4
|
+
import getStackedSeriesMax from './getStackedSeriesMax'
|
|
4
5
|
|
|
5
6
|
type GetMinMaxProps = {
|
|
6
7
|
/** config - standard chart config */
|
|
@@ -120,24 +121,25 @@ const getMinMax = ({
|
|
|
120
121
|
let rightAxisSeriesItems = series.filter(s => s.axis === 'Right')
|
|
121
122
|
|
|
122
123
|
const findMaxFromSeriesKeys = (data, seriesData, max, axis = 'left') => {
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
const stackedBarSeries =
|
|
125
|
+
config.visualizationSubType === 'stacked' && axis === 'left' ? seriesData.filter(s => s.type === 'Bar') : []
|
|
126
|
+
const stackedBarSeriesKeys = new Set(stackedBarSeries.map(s => s.dataKey))
|
|
127
|
+
let axisSeriesKeys = seriesData.map(i => i.dataKey).filter(key => !stackedBarSeriesKeys.has(key)) || []
|
|
125
128
|
|
|
126
129
|
axisSeriesKeys.forEach(key => {
|
|
127
|
-
let _seriesData = seriesData.find(s => s.dataKey === key)
|
|
128
130
|
let _data = data.map(d => d[key])
|
|
129
131
|
let seriesMax = Math.max.apply(null, _data)
|
|
130
|
-
if (config.visualizationSubType === 'stacked' && axis === 'left' && _seriesData.type === 'Bar') {
|
|
131
|
-
stackedBarMax += seriesMax
|
|
132
|
-
}
|
|
133
132
|
if (seriesMax > max) {
|
|
134
133
|
max = seriesMax
|
|
135
134
|
}
|
|
135
|
+
})
|
|
136
136
|
|
|
137
|
+
if (stackedBarSeries.length) {
|
|
138
|
+
const stackedBarMax = getStackedSeriesMax(data, stackedBarSeries)
|
|
137
139
|
if (max < stackedBarMax) {
|
|
138
140
|
max = stackedBarMax
|
|
139
141
|
}
|
|
140
|
-
}
|
|
142
|
+
}
|
|
141
143
|
return max
|
|
142
144
|
}
|
|
143
145
|
leftMax = findMaxFromSeriesKeys(dataForMinMax, leftAxisSeriesItems, leftMax, 'left')
|
|
@@ -190,7 +192,8 @@ const getMinMax = ({
|
|
|
190
192
|
|
|
191
193
|
const isCategorical = config.yAxis.type === 'categorical'
|
|
192
194
|
|
|
193
|
-
min =
|
|
195
|
+
min =
|
|
196
|
+
enteredMinValue !== '' && isMinValid ? numEnteredMin : hasBoundarySuppression ? 0 : isCategorical ? 0 : minValue
|
|
194
197
|
}
|
|
195
198
|
|
|
196
199
|
//If data value max wasn't provided, calculate it
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getSeriesName } from '@cdc/core/helpers/getSeriesName'
|
|
2
2
|
|
|
3
3
|
export const getNewRuntime = (visualizationConfig, newFilteredData) => {
|
|
4
4
|
const runtime = visualizationConfig.runtime ? { ...visualizationConfig.runtime } : {}
|
|
@@ -25,8 +25,9 @@ export const getNewRuntime = (visualizationConfig, newFilteredData) => {
|
|
|
25
25
|
|
|
26
26
|
runtime.seriesKeys = runtime.series
|
|
27
27
|
? runtime.series.map(series => {
|
|
28
|
-
|
|
29
|
-
runtime.
|
|
28
|
+
const seriesName = getSeriesName(series.dataKey, { series: [series] })
|
|
29
|
+
runtime.seriesLabels[series.dataKey] = seriesName
|
|
30
|
+
runtime.seriesLabelsAll.push(seriesName)
|
|
30
31
|
return series.dataKey
|
|
31
32
|
})
|
|
32
33
|
: []
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import toNumber from 'lodash/toNumber'
|
|
2
2
|
|
|
3
3
|
export const getPiePercent = (data: Record<string, any>[], seriesKey: string): Record<string, any>[] => {
|
|
4
4
|
// getonly the numeric values for each seriesKey
|
|
5
|
-
const numericValues = data.map(row =>
|
|
5
|
+
const numericValues = data.map(row => toNumber(row[seriesKey])).filter(v => !Number.isNaN(v))
|
|
6
6
|
|
|
7
7
|
const total = numericValues.reduce((sum, v) => sum + v, 0)
|
|
8
8
|
|
|
9
9
|
return data.map(row => {
|
|
10
|
-
const raw =
|
|
10
|
+
const raw = toNumber(row[seriesKey])
|
|
11
11
|
if (Number.isNaN(raw)) {
|
|
12
12
|
// skip non-numeric / undefined
|
|
13
13
|
return row
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import isNumber from '@cdc/core/helpers/isNumber'
|
|
2
|
+
|
|
3
|
+
type SeriesKeyOrItem =
|
|
4
|
+
| string
|
|
5
|
+
| {
|
|
6
|
+
dataKey?: string
|
|
7
|
+
originalDataKey?: string
|
|
8
|
+
dynamicCategory?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const cleanValue = value => {
|
|
12
|
+
if (value === null || value === '') return ''
|
|
13
|
+
return typeof value === 'string' ? value.replace(/[,$]/g, '') : value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const getSeriesDataKey = (seriesKeyOrItem: SeriesKeyOrItem) => {
|
|
17
|
+
if (typeof seriesKeyOrItem === 'string') return seriesKeyOrItem
|
|
18
|
+
return seriesKeyOrItem.dynamicCategory && seriesKeyOrItem.originalDataKey
|
|
19
|
+
? seriesKeyOrItem.originalDataKey
|
|
20
|
+
: seriesKeyOrItem.dataKey
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getStackedSeriesMax = (data: Object[] = [], seriesKeysOrSeriesItems: SeriesKeyOrItem[] = []) => {
|
|
24
|
+
let maxValue = Number.NEGATIVE_INFINITY
|
|
25
|
+
let hasValue = false
|
|
26
|
+
|
|
27
|
+
for (const row of data) {
|
|
28
|
+
let rowSum = 0
|
|
29
|
+
let hasRowValue = false
|
|
30
|
+
|
|
31
|
+
for (const seriesKeyOrItem of seriesKeysOrSeriesItems) {
|
|
32
|
+
const seriesKey = getSeriesDataKey(seriesKeyOrItem)
|
|
33
|
+
if (!seriesKey) continue
|
|
34
|
+
|
|
35
|
+
const value = cleanValue(row[seriesKey])
|
|
36
|
+
if (isNumber(value)) {
|
|
37
|
+
rowSum += Number(value)
|
|
38
|
+
hasRowValue = true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (hasRowValue) {
|
|
43
|
+
maxValue = Math.max(maxValue, rowSum)
|
|
44
|
+
hasValue = true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return hasValue ? maxValue : 0
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default getStackedSeriesMax
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ChartConfig, type VisualizationType } from '../types/ChartConfig'
|
|
2
|
+
import {
|
|
3
|
+
chartV21ColorDistribution,
|
|
4
|
+
divergentColorDistribution,
|
|
5
|
+
qualitativeStandardColorDistribution
|
|
6
|
+
} from '@cdc/core/helpers/palettes/colorDistributions'
|
|
7
|
+
|
|
8
|
+
const V21_COLOR_DISTRIBUTION_CHART_TYPES = new Set<VisualizationType>([
|
|
9
|
+
'Area Chart',
|
|
10
|
+
'Bar',
|
|
11
|
+
'Box Plot',
|
|
12
|
+
'Bump Chart',
|
|
13
|
+
'Combo',
|
|
14
|
+
'Line',
|
|
15
|
+
'Pie',
|
|
16
|
+
'Radar',
|
|
17
|
+
'Scatter Plot',
|
|
18
|
+
'Spark Line'
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
export const getV21ChartDistributionColors = (
|
|
22
|
+
config: ChartConfig,
|
|
23
|
+
palette: string[],
|
|
24
|
+
itemCount: number
|
|
25
|
+
): string[] | undefined => {
|
|
26
|
+
const paletteName = config.general?.palette?.name || config.palette || ''
|
|
27
|
+
const hasCustomColors =
|
|
28
|
+
Boolean(config.general?.palette?.customColors?.length) ||
|
|
29
|
+
Boolean(config.general?.palette?.customColorsOrdered?.length)
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
config.general?.palette?.version !== '2.1' ||
|
|
33
|
+
!V21_COLOR_DISTRIBUTION_CHART_TYPES.has(config.visualizationType) ||
|
|
34
|
+
hasCustomColors ||
|
|
35
|
+
palette.length !== 9
|
|
36
|
+
) {
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let distributionMap: Record<number, number[]> | undefined
|
|
41
|
+
if (paletteName.includes('sequential')) {
|
|
42
|
+
distributionMap = chartV21ColorDistribution
|
|
43
|
+
} else if (paletteName.includes('divergent')) {
|
|
44
|
+
distributionMap = divergentColorDistribution
|
|
45
|
+
} else if (paletteName.includes('qualitative_standard')) {
|
|
46
|
+
distributionMap = qualitativeStandardColorDistribution
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const distributionIndices = distributionMap?.[itemCount]
|
|
50
|
+
if (!distributionIndices) return undefined
|
|
51
|
+
|
|
52
|
+
const orientedIndices = paletteName.endsWith('reverse')
|
|
53
|
+
? [...distributionIndices].reverse().map(index => palette.length - 1 - index)
|
|
54
|
+
: distributionIndices
|
|
55
|
+
|
|
56
|
+
return orientedIndices.map(index => palette[index])
|
|
57
|
+
}
|