@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
|
@@ -3,6 +3,13 @@ import { describe, expect, it, vi } from 'vitest'
|
|
|
3
3
|
import { createFormatLabels } from './createFormatLabels'
|
|
4
4
|
import { ChartConfig } from '../../../types/ChartConfig'
|
|
5
5
|
import { Label } from '../../../types/Label'
|
|
6
|
+
import chroma from 'chroma-js'
|
|
7
|
+
import {
|
|
8
|
+
getForecastConfidenceIntervalGroups,
|
|
9
|
+
getForecastConfidenceIntervalKey,
|
|
10
|
+
getForecastLegendKey,
|
|
11
|
+
isForecastLegendSelectionActive
|
|
12
|
+
} from '../../../helpers/forecastLegend'
|
|
6
13
|
|
|
7
14
|
const defaultLabels: Label[] = [
|
|
8
15
|
{ datum: 'Series A', index: 0, text: 'Series A', value: '#aa0000' },
|
|
@@ -86,3 +93,222 @@ describe('createFormatLabels small multiples legend colors', () => {
|
|
|
86
93
|
expect(labels.map(label => label.value)).toEqual(['#aa0000', '#aa0000'])
|
|
87
94
|
})
|
|
88
95
|
})
|
|
96
|
+
describe('createFormatLabels category-colored bar legends', () => {
|
|
97
|
+
it('uses the same consecutive palette colors as category-colored bars', () => {
|
|
98
|
+
const config = buildConfig({
|
|
99
|
+
visualizationType: 'Bar',
|
|
100
|
+
visualizationSubType: 'regular',
|
|
101
|
+
general: {
|
|
102
|
+
palette: {
|
|
103
|
+
name: 'sequential_blue',
|
|
104
|
+
version: '2.0'
|
|
105
|
+
}
|
|
106
|
+
} as any,
|
|
107
|
+
legend: { colorCode: 'Group' } as any,
|
|
108
|
+
smallMultiples: undefined as any,
|
|
109
|
+
series: [{ dataKey: 'value', name: 'Value', type: 'Bar' }] as any
|
|
110
|
+
})
|
|
111
|
+
const tableData = [{ Group: 'Group A' }, { Group: 'Group B' }]
|
|
112
|
+
|
|
113
|
+
const labels = createFormatLabels(config, tableData, tableData, colorScale, vi.fn())(defaultLabels)
|
|
114
|
+
|
|
115
|
+
expect(labels.map(label => [label.text, label.value])).toEqual([
|
|
116
|
+
['Group A', '#DBE8F7'],
|
|
117
|
+
['Group B', '#BED5ED']
|
|
118
|
+
])
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const buildForecastConfig = (confidenceIntervals: any[]): ChartConfig =>
|
|
123
|
+
buildConfig({
|
|
124
|
+
visualizationType: 'Combo',
|
|
125
|
+
smallMultiples: undefined,
|
|
126
|
+
general: { palette: { name: 'qualitative_standard', version: '2.0' } } as any,
|
|
127
|
+
legend: { expandForecastConfidenceIntervals: true } as any,
|
|
128
|
+
series: [
|
|
129
|
+
{
|
|
130
|
+
dataKey: 'stage',
|
|
131
|
+
type: 'Forecasting',
|
|
132
|
+
confidenceIntervals,
|
|
133
|
+
stages: [
|
|
134
|
+
{ key: 'Observed', color: 'sequential-blue' },
|
|
135
|
+
{ key: 'Forecast', color: 'sequential-green' }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
] as any,
|
|
139
|
+
runtime: {
|
|
140
|
+
seriesKeys: ['Observed', 'Forecast'],
|
|
141
|
+
forecastingSeriesKeys: [
|
|
142
|
+
{
|
|
143
|
+
confidenceIntervals,
|
|
144
|
+
stages: [
|
|
145
|
+
{ key: 'Observed', color: 'sequential-blue' },
|
|
146
|
+
{ key: 'Forecast', color: 'sequential-green' }
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
} as any
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe('createFormatLabels forecast confidence interval legends', () => {
|
|
154
|
+
it('orders resolved percentages numerically and assigns an independent identity to each interval and stage', () => {
|
|
155
|
+
const labels = formatLabels(
|
|
156
|
+
buildForecastConfig([
|
|
157
|
+
{ low: 'Lower 90%', high: 'Upper 90%' },
|
|
158
|
+
{ low: 'Lower 50%', high: 'Upper 50%' }
|
|
159
|
+
])
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
expect(labels.map(label => [label.text, label.datum])).toEqual([
|
|
163
|
+
['50% CI Observed', getForecastLegendKey('Observed', 'percentage:50')],
|
|
164
|
+
['50% CI Forecast', getForecastLegendKey('Forecast', 'percentage:50')],
|
|
165
|
+
['90% CI Observed', getForecastLegendKey('Observed', 'percentage:90')],
|
|
166
|
+
['90% CI Forecast', getForecastLegendKey('Forecast', 'percentage:90')]
|
|
167
|
+
])
|
|
168
|
+
expect(chroma(labels[0].value).alpha()).toBeCloseTo(0.75)
|
|
169
|
+
expect(chroma(labels[2].value).alpha()).toBeCloseTo(0.5)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('collapses duplicate percentages while retaining their effective shade', () => {
|
|
173
|
+
const labels = formatLabels(
|
|
174
|
+
buildForecastConfig([
|
|
175
|
+
{ low: 'Lower 90%', high: 'Upper 90%' },
|
|
176
|
+
{ low: 'Low 90', high: 'High 90' }
|
|
177
|
+
])
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
expect(labels.map(label => label.text)).toEqual(['90% CI Observed', '90% CI Forecast'])
|
|
181
|
+
expect(chroma(labels[0].value).alpha()).toBeCloseTo(0.75)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('uses stable fallbacks and omits incomplete intervals', () => {
|
|
185
|
+
const labels = formatLabels(
|
|
186
|
+
buildForecastConfig([
|
|
187
|
+
{ low: 'lower', high: 'upper' },
|
|
188
|
+
{ low: '', high: 'Upper 80%' },
|
|
189
|
+
{ low: 'minimum', high: 'maximum' }
|
|
190
|
+
])
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
expect(labels.map(label => label.text)).toEqual([
|
|
194
|
+
'CI 1 Observed',
|
|
195
|
+
'CI 1 Forecast',
|
|
196
|
+
'CI 2 Observed',
|
|
197
|
+
'CI 2 Forecast'
|
|
198
|
+
])
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('falls back when bounds do not contain the same single percentage', () => {
|
|
202
|
+
const labels = formatLabels(
|
|
203
|
+
buildForecastConfig([
|
|
204
|
+
{ low: 'Lower 50%', high: 'Upper 95%' },
|
|
205
|
+
{ low: 'Lower 50 to 90%', high: 'Upper 50 to 90%' }
|
|
206
|
+
])
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
expect(labels.map(label => label.text)).toEqual([
|
|
210
|
+
'CI 1 Observed',
|
|
211
|
+
'CI 1 Forecast',
|
|
212
|
+
'CI 2 Observed',
|
|
213
|
+
'CI 2 Forecast'
|
|
214
|
+
])
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
it('keeps a single complete interval stage-only', () => {
|
|
218
|
+
const labels = formatLabels(
|
|
219
|
+
buildForecastConfig([
|
|
220
|
+
{ low: 'Lower 50%', high: 'Upper 50%' },
|
|
221
|
+
{ low: '', high: 'Upper 90%' }
|
|
222
|
+
])
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
expect(labels.map(label => [label.text, label.datum])).toEqual([
|
|
226
|
+
['Observed', 'Observed'],
|
|
227
|
+
['Forecast', 'Forecast']
|
|
228
|
+
])
|
|
229
|
+
expect(labels.every(label => chroma(label.value).alpha() === 1)).toBe(true)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it.each([undefined, false])('keeps multi-interval legends stage-only when expansion is %s', expansionValue => {
|
|
233
|
+
const config = buildForecastConfig([
|
|
234
|
+
{ low: 'Lower 50%', high: 'Upper 50%' },
|
|
235
|
+
{ low: 'Lower 90%', high: 'Upper 90%' }
|
|
236
|
+
])
|
|
237
|
+
config.legend.expandForecastConfidenceIntervals = expansionValue
|
|
238
|
+
|
|
239
|
+
expect(formatLabels(config).map(label => [label.text, label.datum])).toEqual([
|
|
240
|
+
['Observed', 'Observed'],
|
|
241
|
+
['Forecast', 'Forecast']
|
|
242
|
+
])
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
it('does not infer percentages outside the valid percentage range', () => {
|
|
246
|
+
const labels = formatLabels(
|
|
247
|
+
buildForecastConfig([
|
|
248
|
+
{ low: 'lower_2024', high: 'upper_2024' },
|
|
249
|
+
{ low: 'Lower 50%', high: 'Upper 50%' }
|
|
250
|
+
])
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
expect(labels.map(label => label.text)).toEqual([
|
|
254
|
+
'50% CI Observed',
|
|
255
|
+
'50% CI Forecast',
|
|
256
|
+
'CI 1 Observed',
|
|
257
|
+
'CI 1 Forecast'
|
|
258
|
+
])
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('preserves interval-major numeric ordering when legacy legend ordering options are enabled', () => {
|
|
262
|
+
const config = buildForecastConfig([
|
|
263
|
+
{ low: 'Lower 90%', high: 'Upper 90%' },
|
|
264
|
+
{ low: 'Lower 50%', high: 'Upper 50%' }
|
|
265
|
+
])
|
|
266
|
+
config.legend.reverseLabelOrder = true
|
|
267
|
+
config.legend.verticalSorted = true
|
|
268
|
+
|
|
269
|
+
expect(formatLabels(config).map(label => label.text)).toEqual([
|
|
270
|
+
'50% CI Observed',
|
|
271
|
+
'50% CI Forecast',
|
|
272
|
+
'90% CI Observed',
|
|
273
|
+
'90% CI Forecast'
|
|
274
|
+
])
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
describe('forecast confidence interval interaction identity', () => {
|
|
279
|
+
const confidenceIntervals = [
|
|
280
|
+
{ low: 'Lower 90%', high: 'Upper 90%' },
|
|
281
|
+
{ low: 'Lower 50%', high: 'Upper 50%' }
|
|
282
|
+
]
|
|
283
|
+
|
|
284
|
+
it('maps configured intervals to their normalized legend keys', () => {
|
|
285
|
+
expect(getForecastConfidenceIntervalKey(confidenceIntervals, 0)).toBe('percentage:90')
|
|
286
|
+
expect(getForecastConfidenceIntervalKey(confidenceIntervals, 1)).toBe('percentage:50')
|
|
287
|
+
expect(getForecastLegendKey('Forecast: west', 'percentage:50')).toBe(
|
|
288
|
+
'forecast-ci:Forecast%3A%20west:percentage%3A50'
|
|
289
|
+
)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('activates only a matching composite row while accepting legacy stage selections', () => {
|
|
293
|
+
const selectedKey = getForecastLegendKey('Forecast', 'percentage:50')
|
|
294
|
+
|
|
295
|
+
expect(isForecastLegendSelectionActive([selectedKey], 'Forecast', 'percentage:50')).toBe(true)
|
|
296
|
+
expect(isForecastLegendSelectionActive([selectedKey], 'Forecast', 'percentage:90')).toBe(false)
|
|
297
|
+
expect(isForecastLegendSelectionActive([selectedKey], 'Observed', 'percentage:50')).toBe(false)
|
|
298
|
+
expect(isForecastLegendSelectionActive(['Forecast'], 'Forecast', 'percentage:50')).toBe(true)
|
|
299
|
+
expect(isForecastLegendSelectionActive(['Forecast'], 'Forecast', 'percentage:90')).toBe(true)
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it('shares one composite identity across duplicate resolved percentages', () => {
|
|
303
|
+
const duplicateIntervals = [
|
|
304
|
+
{ low: 'Lower 90%', high: 'Upper 90%' },
|
|
305
|
+
{ low: 'Low 90', high: 'High 90' }
|
|
306
|
+
]
|
|
307
|
+
const groups = getForecastConfidenceIntervalGroups(duplicateIntervals)
|
|
308
|
+
|
|
309
|
+
expect(groups).toHaveLength(1)
|
|
310
|
+
expect(groups[0].intervalIndexes).toEqual([0, 1])
|
|
311
|
+
expect(getForecastConfidenceIntervalKey(duplicateIntervals, 0)).toBe('percentage:90')
|
|
312
|
+
expect(getForecastConfidenceIntervalKey(duplicateIntervals, 1)).toBe('percentage:90')
|
|
313
|
+
})
|
|
314
|
+
})
|
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
import { getCurrentPaletteName, getFallbackColorPalette, migratePaletteWithMap } from '@cdc/core/helpers/palettes/utils'
|
|
8
8
|
import { chartPaletteMigrationMap, paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
|
|
9
9
|
import { getPaletteAccessor } from '@cdc/core/helpers/getPaletteAccessor'
|
|
10
|
-
import {
|
|
10
|
+
import { getColorPaletteMajorVersion } from '@cdc/core/helpers/getColorPaletteMajorVersion'
|
|
11
11
|
import { isV1Palette } from '@cdc/core/helpers/palettes/utils'
|
|
12
|
-
import { v2ColorDistribution } from '@cdc/core/helpers/palettes/colorDistributions'
|
|
13
12
|
import { updatePaletteNames } from '@cdc/core/helpers/updatePaletteNames'
|
|
14
13
|
import { buildForecastPaletteMappings } from '../../../helpers/buildForecastPaletteMappings'
|
|
15
14
|
import { getFullColorPalette } from '../../../helpers/smallMultiplesHelpers'
|
|
16
15
|
import { hasSeriesColorAssignmentOverrides } from '../../../helpers/colorAssignmentHelpers'
|
|
16
|
+
import { getColorCodeCategoryColorMap } from '../../../helpers/getColorCodeCategoryColorMap'
|
|
17
17
|
import { FaStar } from 'react-icons/fa'
|
|
18
18
|
import { Label } from '../../../types/Label'
|
|
19
19
|
import { ColorScale, TransformedData } from '../../../types/ChartContext'
|
|
@@ -23,6 +23,8 @@ import sortBy from 'lodash/sortBy'
|
|
|
23
23
|
import { scaleSequential } from 'd3-scale'
|
|
24
24
|
import { interpolateRgbBasis } from 'd3-interpolate'
|
|
25
25
|
import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
|
|
26
|
+
import chroma from 'chroma-js'
|
|
27
|
+
import { getForecastConfidenceIntervalGroups, getForecastLegendKey } from '../../../helpers/forecastLegend'
|
|
26
28
|
|
|
27
29
|
export const createFormatLabels =
|
|
28
30
|
(
|
|
@@ -239,46 +241,13 @@ export const createFormatLabels =
|
|
|
239
241
|
return reverseLabels([labelBelow, labelAbove])
|
|
240
242
|
}
|
|
241
243
|
if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const version = getColorPaletteVersion(config)
|
|
250
|
-
const isSequentialOrDivergent =
|
|
251
|
-
paletteName && (paletteName.includes('sequential') || paletteName.includes('divergent'))
|
|
252
|
-
const isPairedBarOrDeviation = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
253
|
-
const useV2Distribution =
|
|
254
|
-
version === 2 && isSequentialOrDivergent && palette.length === 9 && numberOfKeys <= 9 && !isPairedBarOrDeviation
|
|
255
|
-
|
|
256
|
-
if (useV2Distribution && v2ColorDistribution[numberOfKeys]) {
|
|
257
|
-
// Use strategic color distribution for v2 sequential palettes
|
|
258
|
-
const distributionIndices = v2ColorDistribution[numberOfKeys]
|
|
259
|
-
palette = distributionIndices.map(index => palette[index])
|
|
260
|
-
} else {
|
|
261
|
-
// Use existing logic for v1 palettes and other cases
|
|
262
|
-
while (tableData.length > palette?.length) {
|
|
263
|
-
palette = palette.concat(palette)
|
|
264
|
-
}
|
|
265
|
-
palette = palette?.slice(0, data.length)
|
|
266
|
-
}
|
|
267
|
-
//store unique values to Set by colorCode
|
|
268
|
-
const set = new Set()
|
|
269
|
-
|
|
270
|
-
tableData.forEach(d => set.add(d[colorCode]))
|
|
271
|
-
|
|
272
|
-
// create labels with unique values
|
|
273
|
-
const uniqueLabels = Array.from(set).map((val, i) => {
|
|
274
|
-
const newLabel = {
|
|
275
|
-
datum: val,
|
|
276
|
-
index: i,
|
|
277
|
-
text: val,
|
|
278
|
-
value: palette?.[i]
|
|
279
|
-
}
|
|
280
|
-
return newLabel
|
|
281
|
-
})
|
|
244
|
+
const categoryColors = getColorCodeCategoryColorMap(config, tableData, filterChartColorPalettes(config))
|
|
245
|
+
const uniqueLabels = Array.from(categoryColors, ([category, color], index) => ({
|
|
246
|
+
datum: category,
|
|
247
|
+
index,
|
|
248
|
+
text: String(category),
|
|
249
|
+
value: color
|
|
250
|
+
}))
|
|
282
251
|
|
|
283
252
|
return reverseLabels(uniqueLabels)
|
|
284
253
|
}
|
|
@@ -286,10 +255,11 @@ export const createFormatLabels =
|
|
|
286
255
|
// get forecasting items inside of combo
|
|
287
256
|
if (runtime?.forecastingSeriesKeys?.length > 0) {
|
|
288
257
|
let seriesLabels = []
|
|
258
|
+
let hasExpandedForecastIntervals = false
|
|
289
259
|
|
|
290
260
|
// Create palette lookup map - use version-specific palettes
|
|
291
261
|
// Forecasting charts use sequentialPalettes for v1, sequential-only palettes for v2
|
|
292
|
-
const paletteVersion =
|
|
262
|
+
const paletteVersion = getColorPaletteMajorVersion(config)
|
|
293
263
|
|
|
294
264
|
let forecastPalettes
|
|
295
265
|
if (paletteVersion === 1) {
|
|
@@ -311,19 +281,43 @@ export const createFormatLabels =
|
|
|
311
281
|
|
|
312
282
|
//store unique values to Set by colorCode
|
|
313
283
|
// loop through each stage/group/area on the chart and create a label
|
|
314
|
-
config.runtime?.forecastingSeriesKeys?.map(
|
|
315
|
-
|
|
284
|
+
config.runtime?.forecastingSeriesKeys?.map(outerGroup => {
|
|
285
|
+
const confidenceIntervalLabels =
|
|
286
|
+
legend.expandForecastConfidenceIntervals === true
|
|
287
|
+
? getForecastConfidenceIntervalGroups(outerGroup?.confidenceIntervals)
|
|
288
|
+
: []
|
|
289
|
+
|
|
290
|
+
const stages = outerGroup?.stages || []
|
|
291
|
+
|
|
292
|
+
if (confidenceIntervalLabels.length) {
|
|
293
|
+
hasExpandedForecastIntervals = true
|
|
294
|
+
confidenceIntervalLabels.forEach(confidenceInterval => {
|
|
295
|
+
stages.forEach(stage => {
|
|
296
|
+
const palette = forecastingPalettes[stage.color] || false
|
|
297
|
+
const colorValue = palette?.[2] || '#ccc'
|
|
298
|
+
|
|
299
|
+
seriesLabels.push({
|
|
300
|
+
datum: getForecastLegendKey(stage.key, confidenceInterval.key),
|
|
301
|
+
index: seriesLabels.length,
|
|
302
|
+
text: `${confidenceInterval.label} ${stage.key}`,
|
|
303
|
+
value: chroma(colorValue).alpha(confidenceInterval.opacity).css()
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return stages.map(stage => {
|
|
316
312
|
const palette = forecastingPalettes[stage.color] || false
|
|
317
313
|
let colorValue = palette?.[2] || '#ccc'
|
|
318
314
|
|
|
319
|
-
|
|
315
|
+
seriesLabels.push({
|
|
320
316
|
datum: stage.key,
|
|
321
|
-
index:
|
|
317
|
+
index: seriesLabels.length,
|
|
322
318
|
text: stage.key,
|
|
323
319
|
value: colorValue
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
seriesLabels.push(newLabel)
|
|
320
|
+
})
|
|
327
321
|
})
|
|
328
322
|
})
|
|
329
323
|
|
|
@@ -345,7 +339,7 @@ export const createFormatLabels =
|
|
|
345
339
|
seriesLabels.push(newLabel)
|
|
346
340
|
})
|
|
347
341
|
|
|
348
|
-
return reverseLabels(seriesLabels)
|
|
342
|
+
return hasExpandedForecastIntervals ? seriesLabels : reverseLabels(seriesLabels)
|
|
349
343
|
}
|
|
350
344
|
|
|
351
345
|
// For Radar charts, use the entity names from runtime.seriesKeys (set from xAxis.dataKey values)
|
|
@@ -16,6 +16,7 @@ type LineChartCircleProps = {
|
|
|
16
16
|
colorScale: any
|
|
17
17
|
parseDate: any
|
|
18
18
|
seriesAxis: string
|
|
19
|
+
seriesColor: string
|
|
19
20
|
dataIndex: number
|
|
20
21
|
seriesIndex: number
|
|
21
22
|
mode: 'ISOLATED_POINTS' | 'HOVER_POINTS' | 'ALWAYS_SHOW_POINTS' | 'TOOLTIP_POINTS'
|
|
@@ -49,6 +50,7 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
49
50
|
d: pointData,
|
|
50
51
|
displayArea,
|
|
51
52
|
seriesKey,
|
|
53
|
+
seriesColor,
|
|
52
54
|
xScale,
|
|
53
55
|
yScale,
|
|
54
56
|
colorScale,
|
|
@@ -136,15 +138,13 @@ const LineChartCircle = (props: LineChartCircleProps) => {
|
|
|
136
138
|
: dataIndex
|
|
137
139
|
|
|
138
140
|
if (drawIsolatedPoints(_dataIndex, seriesKey)) {
|
|
139
|
-
const color = colorScale(config.runtime.seriesLabelsAll[seriesIndex])
|
|
140
|
-
|
|
141
141
|
return (
|
|
142
142
|
<g
|
|
143
143
|
transform={transformShape(pointData[config.xAxis?.dataKey], pointData[filtered?.dataKey])}
|
|
144
144
|
className={`visx-glyph-group${displayArea ? '' : '-hidden'}`}
|
|
145
145
|
data-seriesindex={seriesIndex}
|
|
146
146
|
>
|
|
147
|
-
<Shape size={dotSize} stroke={
|
|
147
|
+
<Shape size={dotSize} stroke={seriesColor} fill={seriesColor} />
|
|
148
148
|
</g>
|
|
149
149
|
)
|
|
150
150
|
}
|