@cdc/chart 4.26.7 → 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 +502 -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 -97406
- 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/overlay-legend.json +485 -0
- package/examples/private/pie-test.json +469 -0
- package/examples/vega-remote.json +22 -0
- package/package.json +11 -5
- package/src/CdcChartComponent.tsx +405 -208
- 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 +4 -2
- 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/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/_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 +38 -6
- package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +12 -3
- package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
- package/src/components/BarChart/components/BarChart.StackedVertical.tsx +29 -25
- package/src/components/BarChart/components/BarChart.Vertical.tsx +37 -26
- package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
- package/src/components/BarChart/components/context.tsx +6 -0
- 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 +104 -0
- package/src/components/BarChart/helpers/useBarChart.ts +60 -59
- 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/helpers/index.ts +11 -82
- package/src/components/Brush/BrushSelector.tsx +9 -23
- 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 +1005 -760
- 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 +88 -54
- 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 +233 -33
- 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 +2 -2
- 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 +2 -1
- package/src/components/EditorPanel/useEditorPermissions.test.tsx +62 -0
- package/src/components/EditorPanel/useEditorPermissions.ts +70 -24
- package/src/components/Forecasting/Forecasting.tsx +15 -6
- 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 +110 -90
- package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
- package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -34
- 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 -4
- package/src/components/LineChart/index.tsx +69 -24
- 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 +81 -51
- 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 +9 -8
- 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 -4
- 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 +77 -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/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 +18 -37
- package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
- package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
- package/src/helpers/getColorScale.ts +31 -22
- package/src/helpers/getDynamicYAxisCategories.ts +69 -0
- package/src/helpers/getMinMax.ts +9 -7
- package/src/helpers/getNewRuntime.ts +5 -2
- package/src/helpers/getStackedSeriesMax.ts +51 -0
- package/src/helpers/getV21ChartDistributionColors.ts +57 -0
- package/src/helpers/handleRankByValue.ts +15 -15
- package/src/helpers/seriesColumnSettings.ts +10 -0
- package/src/helpers/sizeHelpers.ts +4 -3
- 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/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 +23 -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/useTooltip.test.tsx +353 -1
- package/src/hooks/usePrefersReducedMotion.ts +22 -0
- package/src/hooks/useReduceData.ts +4 -36
- package/src/hooks/useScales.ts +15 -0
- package/src/hooks/useTooltip.tsx +95 -64
- package/src/scss/main.scss +74 -2
- package/src/store/chart.reducer.ts +4 -3
- package/src/test/CdcChart.dataTable.test.tsx +490 -16
- 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,
|
|
@@ -149,6 +153,7 @@ const createInitialState = () => {
|
|
|
149
153
|
brushActive: false,
|
|
150
154
|
brushDefaultRecentDateCount: undefined,
|
|
151
155
|
brushDynamicYAxis: false,
|
|
156
|
+
brushHideHatching: false,
|
|
152
157
|
viewportNumTicks: {
|
|
153
158
|
xs: 4,
|
|
154
159
|
xxs: 4
|
|
@@ -162,10 +167,11 @@ const createInitialState = () => {
|
|
|
162
167
|
caption: '',
|
|
163
168
|
showDownloadUrl: false,
|
|
164
169
|
downloadUrlLabel: '',
|
|
165
|
-
showDataTableLink:
|
|
170
|
+
showDataTableLink: false,
|
|
166
171
|
showDownloadLinkBelow: true,
|
|
167
172
|
search: false,
|
|
168
173
|
searchPlaceholder: '',
|
|
174
|
+
stickyFirstColumn: false,
|
|
169
175
|
indexLabel: '',
|
|
170
176
|
download: false,
|
|
171
177
|
showVertical: true,
|
|
@@ -175,7 +181,6 @@ const createInitialState = () => {
|
|
|
175
181
|
collapsible: true
|
|
176
182
|
},
|
|
177
183
|
orientation: 'vertical',
|
|
178
|
-
color: 'qualitative-bold',
|
|
179
184
|
columns: {
|
|
180
185
|
// start with a blank list
|
|
181
186
|
},
|
|
@@ -315,10 +320,12 @@ const createInitialState = () => {
|
|
|
315
320
|
},
|
|
316
321
|
heatmap: {
|
|
317
322
|
cellPadding: 1,
|
|
318
|
-
rowLabelGap:
|
|
319
|
-
columnLabelGap:
|
|
323
|
+
rowLabelGap: 0,
|
|
324
|
+
columnLabelGap: 20,
|
|
320
325
|
xAxisPosition: 'top',
|
|
321
|
-
showCellValues: false
|
|
326
|
+
showCellValues: false,
|
|
327
|
+
horizontalScroll: false,
|
|
328
|
+
minColumnWidth: 44
|
|
322
329
|
},
|
|
323
330
|
area: {
|
|
324
331
|
isStacked: false
|
|
@@ -336,22 +343,20 @@ const createInitialState = () => {
|
|
|
336
343
|
axisLabelOffset: 15
|
|
337
344
|
},
|
|
338
345
|
sankey: {
|
|
339
|
-
title: {
|
|
340
|
-
defaultColor: 'black'
|
|
341
|
-
},
|
|
342
346
|
iterations: 1,
|
|
343
347
|
rxValue: 0.9,
|
|
344
|
-
|
|
345
|
-
width: 900,
|
|
346
|
-
height: 700
|
|
347
|
-
},
|
|
348
|
+
horizontalScrollWidth: 900,
|
|
348
349
|
margin: {
|
|
349
350
|
margin_y: 25,
|
|
350
351
|
margin_x: 0
|
|
351
352
|
},
|
|
353
|
+
columns: {
|
|
354
|
+
source: 'source',
|
|
355
|
+
target: 'target',
|
|
356
|
+
value: 'value'
|
|
357
|
+
},
|
|
352
358
|
nodeSize: {
|
|
353
|
-
nodeWidth: 26
|
|
354
|
-
nodeHeight: 40
|
|
359
|
+
nodeWidth: 26
|
|
355
360
|
},
|
|
356
361
|
nodePadding: 55,
|
|
357
362
|
nodeFontColor: 'black',
|
|
@@ -369,13 +374,54 @@ const createInitialState = () => {
|
|
|
369
374
|
LinkOpacityDefault: 1.0,
|
|
370
375
|
LinkOpacityInactive: 0.1
|
|
371
376
|
},
|
|
372
|
-
storyNodeFontColor: '#006778',
|
|
373
|
-
storyNodeText: [],
|
|
374
377
|
nodeValueStyle: {
|
|
375
378
|
textBefore: '(',
|
|
376
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'
|
|
377
422
|
},
|
|
378
|
-
|
|
423
|
+
leafSpacing: 40,
|
|
424
|
+
depthSpacing: 140
|
|
379
425
|
},
|
|
380
426
|
markupVariables: [],
|
|
381
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
|
|
@@ -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
|
+
}
|
|
@@ -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
|
+
}
|