@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
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_RACE_SECONDS_PER_FRAME } from '../components/raceTiming'
|
|
2
2
|
|
|
3
|
-
// Dynamic initial state based on migration flag
|
|
4
3
|
const createInitialState = () => {
|
|
5
|
-
const paletteDefaults =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
: {
|
|
12
|
-
isReversed: true,
|
|
13
|
-
version: '1.0',
|
|
14
|
-
name: 'qualitative_bold'
|
|
15
|
-
}
|
|
4
|
+
const paletteDefaults = {
|
|
5
|
+
isReversed: false,
|
|
6
|
+
version: '2.1',
|
|
7
|
+
name: 'sequential_blue'
|
|
8
|
+
}
|
|
16
9
|
|
|
17
10
|
return {
|
|
18
11
|
annotations: [],
|
|
@@ -26,11 +19,19 @@ const createInitialState = () => {
|
|
|
26
19
|
titleStyle: 'small',
|
|
27
20
|
showTitle: true,
|
|
28
21
|
showDownloadMediaButton: false,
|
|
22
|
+
enableTooltips: false,
|
|
29
23
|
theme: 'theme-blue',
|
|
30
24
|
animate: false,
|
|
31
25
|
lineDatapointStyle: 'hover',
|
|
32
26
|
lineDatapointColor: 'Same as Line',
|
|
33
27
|
barHasBorder: 'true',
|
|
28
|
+
barRace: {
|
|
29
|
+
maxBars: 10,
|
|
30
|
+
secondsPerFrame: DEFAULT_RACE_SECONDS_PER_FRAME
|
|
31
|
+
},
|
|
32
|
+
lineRace: {
|
|
33
|
+
secondsPerFrame: DEFAULT_RACE_SECONDS_PER_FRAME
|
|
34
|
+
},
|
|
34
35
|
isLollipopChart: false,
|
|
35
36
|
lollipopShape: 'circle',
|
|
36
37
|
lollipopColorStyle: 'two-tone',
|
|
@@ -58,6 +59,7 @@ const createInitialState = () => {
|
|
|
58
59
|
displayNumbersOnBar: false,
|
|
59
60
|
hideLabel: false,
|
|
60
61
|
hideTicks: true,
|
|
62
|
+
labelPlacement: 'On Date/Category Axis',
|
|
61
63
|
size: 50,
|
|
62
64
|
gridLines: true,
|
|
63
65
|
enablePadding: false,
|
|
@@ -70,6 +72,7 @@ const createInitialState = () => {
|
|
|
70
72
|
rightAxisSize: 0,
|
|
71
73
|
rightLabel: '',
|
|
72
74
|
rightLabelOffsetSize: 0,
|
|
75
|
+
rightTitlePlacement: 'top',
|
|
73
76
|
rightAxisLabelColor: '#1c1d1f',
|
|
74
77
|
rightAxisTickLabelColor: '#1c1d1f',
|
|
75
78
|
rightAxisTickColor: '#1c1d1f',
|
|
@@ -80,6 +83,7 @@ const createInitialState = () => {
|
|
|
80
83
|
tickRotation: 0,
|
|
81
84
|
autoMaxStrategy: 'clean-top-tick',
|
|
82
85
|
anchors: [],
|
|
86
|
+
rightAnchors: [],
|
|
83
87
|
shoMissingDataLabel: true,
|
|
84
88
|
showMissingDataLine: true,
|
|
85
89
|
categories: []
|
|
@@ -112,8 +116,8 @@ const createInitialState = () => {
|
|
|
112
116
|
},
|
|
113
117
|
|
|
114
118
|
isLegendValue: false,
|
|
115
|
-
barThickness: 0.35,
|
|
116
119
|
barHeight: 25,
|
|
120
|
+
barThickness: 0.8,
|
|
117
121
|
barSpace: 15,
|
|
118
122
|
heights: {
|
|
119
123
|
vertical: 300,
|
|
@@ -144,9 +148,12 @@ const createInitialState = () => {
|
|
|
144
148
|
padding: 5,
|
|
145
149
|
showYearsOnce: false,
|
|
146
150
|
sortByRecentDate: false,
|
|
151
|
+
categoryOrder: [],
|
|
152
|
+
categoryOrderType: 'data',
|
|
147
153
|
brushActive: false,
|
|
148
154
|
brushDefaultRecentDateCount: undefined,
|
|
149
155
|
brushDynamicYAxis: false,
|
|
156
|
+
brushHideHatching: false,
|
|
150
157
|
viewportNumTicks: {
|
|
151
158
|
xs: 4,
|
|
152
159
|
xxs: 4
|
|
@@ -160,10 +167,11 @@ const createInitialState = () => {
|
|
|
160
167
|
caption: '',
|
|
161
168
|
showDownloadUrl: false,
|
|
162
169
|
downloadUrlLabel: '',
|
|
163
|
-
showDataTableLink:
|
|
170
|
+
showDataTableLink: false,
|
|
164
171
|
showDownloadLinkBelow: true,
|
|
165
172
|
search: false,
|
|
166
173
|
searchPlaceholder: '',
|
|
174
|
+
stickyFirstColumn: false,
|
|
167
175
|
indexLabel: '',
|
|
168
176
|
download: false,
|
|
169
177
|
showVertical: true,
|
|
@@ -173,7 +181,6 @@ const createInitialState = () => {
|
|
|
173
181
|
collapsible: true
|
|
174
182
|
},
|
|
175
183
|
orientation: 'vertical',
|
|
176
|
-
color: 'qualitative-bold',
|
|
177
184
|
columns: {
|
|
178
185
|
// start with a blank list
|
|
179
186
|
},
|
|
@@ -313,10 +320,12 @@ const createInitialState = () => {
|
|
|
313
320
|
},
|
|
314
321
|
heatmap: {
|
|
315
322
|
cellPadding: 1,
|
|
316
|
-
rowLabelGap:
|
|
317
|
-
columnLabelGap:
|
|
323
|
+
rowLabelGap: 0,
|
|
324
|
+
columnLabelGap: 20,
|
|
318
325
|
xAxisPosition: 'top',
|
|
319
|
-
showCellValues: false
|
|
326
|
+
showCellValues: false,
|
|
327
|
+
horizontalScroll: false,
|
|
328
|
+
minColumnWidth: 44
|
|
320
329
|
},
|
|
321
330
|
area: {
|
|
322
331
|
isStacked: false
|
|
@@ -334,22 +343,20 @@ const createInitialState = () => {
|
|
|
334
343
|
axisLabelOffset: 15
|
|
335
344
|
},
|
|
336
345
|
sankey: {
|
|
337
|
-
title: {
|
|
338
|
-
defaultColor: 'black'
|
|
339
|
-
},
|
|
340
346
|
iterations: 1,
|
|
341
347
|
rxValue: 0.9,
|
|
342
|
-
|
|
343
|
-
width: 900,
|
|
344
|
-
height: 700
|
|
345
|
-
},
|
|
348
|
+
horizontalScrollWidth: 900,
|
|
346
349
|
margin: {
|
|
347
350
|
margin_y: 25,
|
|
348
351
|
margin_x: 0
|
|
349
352
|
},
|
|
353
|
+
columns: {
|
|
354
|
+
source: 'source',
|
|
355
|
+
target: 'target',
|
|
356
|
+
value: 'value'
|
|
357
|
+
},
|
|
350
358
|
nodeSize: {
|
|
351
|
-
nodeWidth: 26
|
|
352
|
-
nodeHeight: 40
|
|
359
|
+
nodeWidth: 26
|
|
353
360
|
},
|
|
354
361
|
nodePadding: 55,
|
|
355
362
|
nodeFontColor: 'black',
|
|
@@ -367,13 +374,54 @@ const createInitialState = () => {
|
|
|
367
374
|
LinkOpacityDefault: 1.0,
|
|
368
375
|
LinkOpacityInactive: 0.1
|
|
369
376
|
},
|
|
370
|
-
storyNodeFontColor: '#006778',
|
|
371
|
-
storyNodeText: [],
|
|
372
377
|
nodeValueStyle: {
|
|
373
378
|
textBefore: '(',
|
|
374
379
|
textAfter: ')'
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
network: {
|
|
383
|
+
columns: {
|
|
384
|
+
source: 'source',
|
|
385
|
+
target: 'target',
|
|
386
|
+
weight: '',
|
|
387
|
+
style: '',
|
|
388
|
+
nodeColor: ''
|
|
389
|
+
},
|
|
390
|
+
directed: false,
|
|
391
|
+
height: 500,
|
|
392
|
+
nodeRadius: 8,
|
|
393
|
+
nodeColor: '#005eaa',
|
|
394
|
+
linkColor: '#333333',
|
|
395
|
+
linkStyleLabels: {
|
|
396
|
+
solid: 'Solid',
|
|
397
|
+
dashed: 'Dashed'
|
|
398
|
+
},
|
|
399
|
+
linkDistance: 90,
|
|
400
|
+
chargeStrength: -240,
|
|
401
|
+
rotation: 0,
|
|
402
|
+
scale: 1,
|
|
403
|
+
offsetX: 0,
|
|
404
|
+
offsetY: 0
|
|
405
|
+
},
|
|
406
|
+
dendrogram: {
|
|
407
|
+
columns: {
|
|
408
|
+
node: 'node',
|
|
409
|
+
parent: 'parent',
|
|
410
|
+
style: 'linkStyle',
|
|
411
|
+
nodeColor: 'nodeColor'
|
|
412
|
+
},
|
|
413
|
+
connectionType: 'curve',
|
|
414
|
+
orientation: 'horizontal',
|
|
415
|
+
height: 500,
|
|
416
|
+
nodeRadius: 6,
|
|
417
|
+
nodeColor: '#005eaa',
|
|
418
|
+
linkColor: '#333333',
|
|
419
|
+
linkStyleLabels: {
|
|
420
|
+
solid: 'Solid',
|
|
421
|
+
dashed: 'Dashed'
|
|
375
422
|
},
|
|
376
|
-
|
|
423
|
+
leafSpacing: 40,
|
|
424
|
+
depthSpacing: 140
|
|
377
425
|
},
|
|
378
426
|
markupVariables: [],
|
|
379
427
|
enableMarkupVariables: false
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ChartConfig } from '../types/ChartConfig'
|
|
2
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
3
|
+
|
|
4
|
+
/** Applies deterministic chart-type fields owned by current authoring paths. */
|
|
5
|
+
export const applyChartTypeContract = <T extends ChartConfig>(config: T): T => {
|
|
6
|
+
const nextConfig = cloneDeep(config)
|
|
7
|
+
|
|
8
|
+
if (Array.isArray(nextConfig.series)) {
|
|
9
|
+
nextConfig.series = nextConfig.series.map(series => ({ tooltip: true, axis: 'Left', ...series }))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
switch (nextConfig.visualizationType) {
|
|
13
|
+
case 'Area Chart':
|
|
14
|
+
nextConfig.visualizationSubType = 'stacked'
|
|
15
|
+
break
|
|
16
|
+
case 'Forecasting':
|
|
17
|
+
if (!nextConfig.xAxis.type || nextConfig.xAxis.type === 'categorical') {
|
|
18
|
+
nextConfig.xAxis.type = 'date'
|
|
19
|
+
if (!nextConfig.xAxis.dateParseFormat) nextConfig.xAxis.dateParseFormat = '%Y-%m-%d'
|
|
20
|
+
if (!nextConfig.xAxis.dateDisplayFormat) nextConfig.xAxis.dateDisplayFormat = '%Y-%m-%d'
|
|
21
|
+
}
|
|
22
|
+
break
|
|
23
|
+
case 'HeatMap':
|
|
24
|
+
nextConfig.yAxis.type = 'categorical'
|
|
25
|
+
nextConfig.yAxis.titlePlacement = nextConfig.yAxis.titlePlacement || 'side'
|
|
26
|
+
nextConfig.legend.position = nextConfig.legend.position || 'top'
|
|
27
|
+
nextConfig.legend.style = nextConfig.legend.style || 'gradient'
|
|
28
|
+
nextConfig.legend.subStyle =
|
|
29
|
+
nextConfig.legend.subStyle === 'smooth' ? 'linear blocks' : nextConfig.legend.subStyle || 'linear blocks'
|
|
30
|
+
break
|
|
31
|
+
case 'Horizon Chart': {
|
|
32
|
+
nextConfig.horizon = {
|
|
33
|
+
numLayers: 4,
|
|
34
|
+
mode: 'offset',
|
|
35
|
+
bandGap: 15,
|
|
36
|
+
bottomPadding: 15,
|
|
37
|
+
...nextConfig.horizon
|
|
38
|
+
}
|
|
39
|
+
if (!nextConfig.xAxis.type) nextConfig.xAxis.type = 'categorical'
|
|
40
|
+
const layerCount = nextConfig.horizon.numLayers ?? 4
|
|
41
|
+
const paletteCount = nextConfig.general.paletteColorCount ?? 4
|
|
42
|
+
nextConfig.general.paletteColorCount = Math.max(paletteCount, layerCount)
|
|
43
|
+
break
|
|
44
|
+
}
|
|
45
|
+
case 'Box Plot':
|
|
46
|
+
nextConfig.yAxis.labelPlacement = 'On Date/Category Axis'
|
|
47
|
+
break
|
|
48
|
+
case 'Paired Bar':
|
|
49
|
+
case 'Deviation Bar':
|
|
50
|
+
nextConfig.orientation = 'horizontal'
|
|
51
|
+
break
|
|
52
|
+
case 'Bump Chart':
|
|
53
|
+
nextConfig.xAxis.type = 'date-time'
|
|
54
|
+
break
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (
|
|
58
|
+
['date', 'date-time'].includes(nextConfig.xAxis?.type) &&
|
|
59
|
+
nextConfig.xAxis?.dataKey &&
|
|
60
|
+
!nextConfig.table?.defaultSort?.column
|
|
61
|
+
) {
|
|
62
|
+
nextConfig.table = {
|
|
63
|
+
...nextConfig.table,
|
|
64
|
+
defaultSort: { column: nextConfig.xAxis.dataKey, sortDirection: 'desc' }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return nextConfig
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default applyChartTypeContract
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isMobileFontViewport } from '@cdc/core/helpers/viewports'
|
|
2
|
-
import {
|
|
2
|
+
import type { ViewPort } from '@cdc/core/types/ViewPort'
|
|
3
3
|
|
|
4
4
|
export const AXIS_LABEL_FONT_SIZE = 18
|
|
5
5
|
export const AXIS_LABEL_FONT_SIZE_SMALL = 14
|
|
6
6
|
|
|
7
|
-
export const getAxisLabelFontSize = (vizViewport:
|
|
7
|
+
export const getAxisLabelFontSize = (vizViewport: ViewPort) =>
|
|
8
8
|
isMobileFontViewport(vizViewport) ? AXIS_LABEL_FONT_SIZE_SMALL : AXIS_LABEL_FONT_SIZE
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as d3 from 'd3-array'
|
|
2
|
+
import { sortByNumber, toSortableNumber } from '@cdc/core/helpers/sorting'
|
|
3
|
+
|
|
4
|
+
export type BoxPlotStats = {
|
|
5
|
+
deviation: number | undefined
|
|
6
|
+
iqr: number
|
|
7
|
+
lowerBound: number
|
|
8
|
+
mean: number | undefined
|
|
9
|
+
median: number | undefined
|
|
10
|
+
nonOutliers: number[]
|
|
11
|
+
outliers: number[]
|
|
12
|
+
q1: number
|
|
13
|
+
q3: number
|
|
14
|
+
upperBound: number
|
|
15
|
+
values: number[]
|
|
16
|
+
whiskerMax: number
|
|
17
|
+
whiskerMin: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const getSortedBoxPlotValues = (values: unknown[] = []): number[] => {
|
|
21
|
+
const numericValues = values.reduce<number[]>((result, value) => {
|
|
22
|
+
if (value === null || value === undefined || value === '') return result
|
|
23
|
+
|
|
24
|
+
const numericValue = toSortableNumber(value)
|
|
25
|
+
if (Number.isFinite(numericValue)) result.push(numericValue)
|
|
26
|
+
|
|
27
|
+
return result
|
|
28
|
+
}, [])
|
|
29
|
+
|
|
30
|
+
return sortByNumber(numericValues)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const calculateBoxPlotStats = (values: unknown[] = []): BoxPlotStats | null => {
|
|
34
|
+
const sortedValues = getSortedBoxPlotValues(values)
|
|
35
|
+
if (!sortedValues.length) return null
|
|
36
|
+
|
|
37
|
+
const q1 = d3.quantile(sortedValues, 0.25) ?? 0
|
|
38
|
+
const q3 = d3.quantile(sortedValues, 0.75) ?? 0
|
|
39
|
+
const iqr = q3 - q1
|
|
40
|
+
const lowerBound = q1 - 1.5 * iqr
|
|
41
|
+
const upperBound = q3 + 1.5 * iqr
|
|
42
|
+
const nonOutliers = sortedValues.filter(value => value >= lowerBound && value <= upperBound)
|
|
43
|
+
const outliers = sortedValues.filter(value => value < lowerBound || value > upperBound)
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
deviation: d3.deviation(sortedValues),
|
|
47
|
+
iqr,
|
|
48
|
+
lowerBound,
|
|
49
|
+
mean: d3.mean(sortedValues),
|
|
50
|
+
median: d3.median(sortedValues),
|
|
51
|
+
nonOutliers,
|
|
52
|
+
outliers,
|
|
53
|
+
q1,
|
|
54
|
+
q3,
|
|
55
|
+
upperBound,
|
|
56
|
+
values: sortedValues,
|
|
57
|
+
whiskerMax: nonOutliers[nonOutliers.length - 1] ?? q3,
|
|
58
|
+
whiskerMin: nonOutliers[0] ?? q1
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -2,6 +2,7 @@ import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
|
2
2
|
import { buildTooltipListHtml, getTooltipSeriesMarker } from './tooltipHelpers'
|
|
3
3
|
import { type ChartConfig } from '../types/ChartConfig'
|
|
4
4
|
import { type ColorScale } from '../types/ChartContext'
|
|
5
|
+
import { getSeriesValueLabel } from '@cdc/core/helpers/getSeriesName'
|
|
5
6
|
|
|
6
7
|
type TooltipDateHelpers = {
|
|
7
8
|
formatDate: (date: Date) => string
|
|
@@ -35,7 +36,7 @@ export const buildBumpChartTooltipHtml = ({ config, colorScale, dataRow, series,
|
|
|
35
36
|
const xAxisLabel = config.runtime?.xAxis?.label || config.xAxis.dataKey
|
|
36
37
|
const heading = `${xAxisLabel ? `${xAxisLabel}: ` : ''}${formattedXAxisValue ?? ''}`
|
|
37
38
|
|
|
38
|
-
const seriesName = series.
|
|
39
|
+
const seriesName = getSeriesValueLabel(series.dataKey, { ...config, series: [series] })
|
|
39
40
|
const marker = getTooltipSeriesMarker(config, colorScale, series.dataKey)
|
|
40
41
|
const bodyRows = [
|
|
41
42
|
{
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { calculateHorizontalBarCategoryLabelWidth } from './calculateHorizontalBarCategoryLabelWidth'
|
|
4
|
+
|
|
5
|
+
vi.mock('@cdc/core/helpers/getTextWidth', () => ({
|
|
6
|
+
getTextWidth: vi.fn()
|
|
7
|
+
}))
|
|
8
|
+
|
|
9
|
+
const mockedGetTextWidth = vi.mocked(getTextWidth)
|
|
10
|
+
|
|
11
|
+
const calculateWidth = (categoryValues: string[], chartWidth = 500) =>
|
|
12
|
+
calculateHorizontalBarCategoryLabelWidth({
|
|
13
|
+
yScale: { domain: () => categoryValues },
|
|
14
|
+
chartWidth,
|
|
15
|
+
formatDate: vi.fn(),
|
|
16
|
+
parseDate: vi.fn(),
|
|
17
|
+
tickLabelFont: 'normal 16px Nunito, sans-serif',
|
|
18
|
+
labelPlacement: 'On Date/Category Axis'
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('calculateHorizontalBarCategoryLabelWidth', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
mockedGetTextWidth.mockImplementation(label => label.length * 10)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('keeps at least ten pixels between short labels and the chart area', () => {
|
|
27
|
+
expect(calculateWidth(['A', 'B'])).toBe(20)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('retains proportional padding for longer labels', () => {
|
|
31
|
+
expect(calculateWidth(['abcdefghij'])).toBe(115)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('still caps category label space at thirty percent of the chart width', () => {
|
|
35
|
+
expect(calculateWidth(['abcdefghij'], 300)).toBe(90)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -47,8 +47,9 @@ export const calculateHorizontalBarCategoryLabelWidth = ({
|
|
|
47
47
|
const labelWidths = formattedLabels.map(label => getTextWidth(label, tickLabelFont))
|
|
48
48
|
const maxLabelWidth = Math.max(...labelWidths)
|
|
49
49
|
|
|
50
|
-
// We need some extra padding or visx will wrap labels too early
|
|
51
|
-
|
|
50
|
+
// We need some extra padding or visx will wrap labels too early. Keep a
|
|
51
|
+
// minimum gap so short labels do not sit too close to the chart area.
|
|
52
|
+
const paddedWidth = maxLabelWidth + Math.max(10, Math.ceil(maxLabelWidth * 0.15))
|
|
52
53
|
|
|
53
54
|
// Allocate at most 30% of chart width to category labels
|
|
54
55
|
const maxAllowedWidth = chartWidth * 0.3
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
|
|
2
|
+
import { DEFAULT_TICK_LENGTH, TICK_LABEL_MARGIN_RIGHT } from '../components/Axis/axis.constants'
|
|
3
|
+
|
|
4
|
+
type CalculateRightYAxisWidthProps = {
|
|
5
|
+
axisLabelFontSize: number
|
|
6
|
+
config: any
|
|
7
|
+
formatNumber: (value: any, axis?: string) => string
|
|
8
|
+
tickLabelFont: string
|
|
9
|
+
tickValues?: number[]
|
|
10
|
+
yScaleRight: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const AXIS_LABEL_PADDING = 5
|
|
14
|
+
|
|
15
|
+
const getRightTickValues = ({ config, tickValues, yScaleRight }) => {
|
|
16
|
+
if (tickValues) return tickValues
|
|
17
|
+
|
|
18
|
+
if (typeof yScaleRight?.ticks === 'function') {
|
|
19
|
+
return yScaleRight.ticks(config.runtime?.yAxis?.rightNumTicks || undefined)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof yScaleRight?.domain === 'function') {
|
|
23
|
+
return yScaleRight.domain()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return []
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const measureWidestLabel = (labels: string[], tickLabelFont: string) => {
|
|
30
|
+
return labels.length ? Math.max(...labels.map(label => getTextWidth(label, tickLabelFont))) : 0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getSideTitleSpace = ({ config, axisLabelFontSize, tickContentSpace }) => {
|
|
34
|
+
const rightTitle = config.runtime?.yAxis?.rightLabel ?? config.yAxis?.rightLabel
|
|
35
|
+
|
|
36
|
+
if (config.yAxis?.rightTitlePlacement === 'top' || !rightTitle) return 0
|
|
37
|
+
|
|
38
|
+
return tickContentSpace + axisLabelFontSize + AXIS_LABEL_PADDING
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const calculateRightYAxisWidth = ({
|
|
42
|
+
axisLabelFontSize,
|
|
43
|
+
config,
|
|
44
|
+
formatNumber,
|
|
45
|
+
tickLabelFont,
|
|
46
|
+
tickValues,
|
|
47
|
+
yScaleRight
|
|
48
|
+
}: CalculateRightYAxisWidthProps): number => {
|
|
49
|
+
const measuredTickValues = getRightTickValues({ config, tickValues, yScaleRight })
|
|
50
|
+
const formattedTicks = measuredTickValues.map(tick => String(formatNumber(tick, 'right') ?? ''))
|
|
51
|
+
const tickLabelWidth = config.yAxis?.rightHideLabel ? 0 : measureWidestLabel(formattedTicks, tickLabelFont)
|
|
52
|
+
const tickLabelSpace = tickLabelWidth ? tickLabelWidth + DEFAULT_TICK_LENGTH + TICK_LABEL_MARGIN_RIGHT : 0
|
|
53
|
+
const tickMarkSpace = config.runtime?.yAxis?.rightHideTicks ? 0 : DEFAULT_TICK_LENGTH
|
|
54
|
+
const tickContentSpace = Math.max(tickLabelSpace, tickMarkSpace)
|
|
55
|
+
const sideTitleSpace = getSideTitleSpace({ config, axisLabelFontSize, tickContentSpace })
|
|
56
|
+
|
|
57
|
+
return Math.ceil(Math.max(tickContentSpace, sideTitleSpace))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default calculateRightYAxisWidth
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
2
|
+
|
|
3
|
+
type DataRow = Record<string, any>
|
|
4
|
+
|
|
5
|
+
export const isCustomCategoryOrderActive = (config: Pick<ChartConfig, 'xAxis'>) => {
|
|
6
|
+
return (
|
|
7
|
+
config.xAxis?.type === 'categorical' &&
|
|
8
|
+
config.xAxis?.categoryOrderType === 'custom' &&
|
|
9
|
+
Array.isArray(config.xAxis?.categoryOrder) &&
|
|
10
|
+
config.xAxis.categoryOrder.length > 0 &&
|
|
11
|
+
Boolean(config.xAxis?.dataKey)
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getCategoryValues = (data: DataRow[] = [], dataKey?: string): string[] => {
|
|
16
|
+
if (!dataKey || !Array.isArray(data)) return []
|
|
17
|
+
|
|
18
|
+
const values: string[] = []
|
|
19
|
+
for (const row of data) {
|
|
20
|
+
const value = String(row?.[dataKey] ?? '')
|
|
21
|
+
if (value !== '') values.push(value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return Array.from(new Set(values))
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const getOrderedCategoryValues = (
|
|
28
|
+
data: DataRow[] = [],
|
|
29
|
+
dataKey?: string,
|
|
30
|
+
categoryOrder: string[] = []
|
|
31
|
+
): string[] => {
|
|
32
|
+
const values = getCategoryValues(data, dataKey)
|
|
33
|
+
if (!categoryOrder?.length) return values
|
|
34
|
+
|
|
35
|
+
const configuredValues = categoryOrder.map(String)
|
|
36
|
+
const configuredValueSet = new Set(configuredValues)
|
|
37
|
+
const valueSet = new Set(values)
|
|
38
|
+
const extras = values.filter(value => !configuredValueSet.has(value))
|
|
39
|
+
|
|
40
|
+
return [...configuredValues.filter(value => valueSet.has(value)), ...extras]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const sortByCategoryOrder = (data: DataRow[] = [], config: Pick<ChartConfig, 'xAxis'>): DataRow[] => {
|
|
44
|
+
if (!Array.isArray(data) || !isCustomCategoryOrderActive(config)) return data
|
|
45
|
+
|
|
46
|
+
const dataKey = config.xAxis.dataKey
|
|
47
|
+
const categoryOrder = Array.from(new Set((config.xAxis.categoryOrder || []).map(String)))
|
|
48
|
+
const indexByValue = new Map(categoryOrder.map((value, index) => [value, index]))
|
|
49
|
+
return data
|
|
50
|
+
.map((row, originalIndex) => ({ row, originalIndex }))
|
|
51
|
+
.sort((a, b) => {
|
|
52
|
+
const indexA = indexByValue.get(String(a.row?.[dataKey] ?? ''))
|
|
53
|
+
const indexB = indexByValue.get(String(b.row?.[dataKey] ?? ''))
|
|
54
|
+
const rankA = indexA ?? Number.MAX_SAFE_INTEGER
|
|
55
|
+
const rankB = indexB ?? Number.MAX_SAFE_INTEGER
|
|
56
|
+
|
|
57
|
+
return rankA === rankB ? a.originalIndex - b.originalIndex : rankA - rankB
|
|
58
|
+
})
|
|
59
|
+
.map(({ row }) => row)
|
|
60
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ChartConfig, type VisualizationType } from '../types/ChartConfig'
|
|
2
|
+
import { getSeriesName } from '@cdc/core/helpers/getSeriesName'
|
|
2
3
|
|
|
3
4
|
export type SeriesColorAssignment = {
|
|
4
5
|
key: string
|
|
@@ -45,8 +46,7 @@ export const hasSeriesColorAssignmentOverrides = (config: ChartConfig): boolean
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
export const getSeriesDisplayLabel = (config: ChartConfig, key: string): string => {
|
|
48
|
-
|
|
49
|
-
return String(config.runtime?.seriesLabels?.[key] || series?.name || (series as any)?.label || key)
|
|
49
|
+
return getSeriesName(key, config)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export const getSeriesColorAssignmentItems = (config: ChartConfig): SeriesColorAssignmentItem[] => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
2
|
+
import mergeWith from 'lodash/mergeWith'
|
|
3
|
+
|
|
4
|
+
import defaults from '../data/initial-state'
|
|
5
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
6
|
+
import { getChartTypeDefaultPalette } from './getChartTypeDefaultPalette'
|
|
7
|
+
import { CURRENT_COVE_CONFIG_VERSION } from '@cdc/core/helpers/coveUpdateWorker'
|
|
8
|
+
import { applyChartTypeContract } from './applyChartTypeContract'
|
|
9
|
+
|
|
10
|
+
type CreateNewChartConfigOptions = {
|
|
11
|
+
isDashboard?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates a complete config for a brand-new chart using current defaults.
|
|
16
|
+
* Existing and legacy configs must continue through the normal migration and
|
|
17
|
+
* backfill paths instead of using this helper.
|
|
18
|
+
*/
|
|
19
|
+
export const createNewChartConfig = (
|
|
20
|
+
starterConfig: Partial<ChartConfig> & Record<string, any>,
|
|
21
|
+
{ isDashboard = false }: CreateNewChartConfigOptions = {}
|
|
22
|
+
): ChartConfig => {
|
|
23
|
+
const currentDefaults = cloneDeep(defaults)
|
|
24
|
+
const defaultPalette = getChartTypeDefaultPalette(starterConfig.visualizationType)
|
|
25
|
+
|
|
26
|
+
if (defaultPalette && !starterConfig.general?.palette) {
|
|
27
|
+
currentDefaults.general.palette = cloneDeep(defaultPalette)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const config = applyChartTypeContract(
|
|
31
|
+
mergeWith(currentDefaults, cloneDeep(starterConfig), (_defaultValue, starterValue) =>
|
|
32
|
+
Array.isArray(starterValue) ? starterValue : undefined
|
|
33
|
+
) as ChartConfig
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if (config.orientation === 'horizontal') {
|
|
37
|
+
config.xAxis.hideAxis = starterConfig.xAxis?.hideAxis ?? true
|
|
38
|
+
config.xAxis.hideTicks = starterConfig.xAxis?.hideTicks ?? true
|
|
39
|
+
config.yAxis.hideAxis = starterConfig.yAxis?.hideAxis ?? false
|
|
40
|
+
config.yAxis.hideTicks = starterConfig.yAxis?.hideTicks ?? false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
config.table.show = starterConfig.table?.show ?? !isDashboard
|
|
44
|
+
config.table.download = starterConfig.table?.download ?? !isDashboard
|
|
45
|
+
config.legend.unified = starterConfig.legend?.unified ?? true
|
|
46
|
+
config.legend.expandForecastConfidenceIntervals = starterConfig.legend?.expandForecastConfidenceIntervals ?? true
|
|
47
|
+
|
|
48
|
+
if (starterConfig.visualizationType === 'HeatMap') {
|
|
49
|
+
config.xAxis.manual = starterConfig.xAxis?.manual ?? false
|
|
50
|
+
config.xAxis.numTicks = starterConfig.xAxis?.numTicks
|
|
51
|
+
config.xAxis.viewportNumTicks = starterConfig.xAxis?.viewportNumTicks ?? {}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
config.version = CURRENT_COVE_CONFIG_VERSION
|
|
55
|
+
return config
|
|
56
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AllChartsConfig } from '../types/ChartConfig'
|
|
2
|
+
import { getDynamicYAxisCategories } from './getDynamicYAxisCategories'
|
|
3
|
+
|
|
4
|
+
type DataDrivenYAxisConfig = Pick<AllChartsConfig, 'yAxis'>
|
|
5
|
+
|
|
6
|
+
export const isDataDrivenYAxis = (config: DataDrivenYAxisConfig) =>
|
|
7
|
+
config.yAxis.type === 'categorical' &&
|
|
8
|
+
(config.yAxis.categoryMode === 'data-driven' ||
|
|
9
|
+
(config.yAxis.categoryMode === undefined && !!config.yAxis.dataDrivenCategories))
|
|
10
|
+
|
|
11
|
+
export const getDataDrivenYAxisCategories = (config: DataDrivenYAxisConfig, data: Record<string, any>[]) => {
|
|
12
|
+
if (!isDataDrivenYAxis(config) || !config.yAxis.dataDrivenCategories) return null
|
|
13
|
+
|
|
14
|
+
return getDynamicYAxisCategories({
|
|
15
|
+
config: config.yAxis.dataDrivenCategories,
|
|
16
|
+
data
|
|
17
|
+
})
|
|
18
|
+
}
|