@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,13 +1,10 @@
|
|
|
1
1
|
import capitalize from 'lodash/capitalize'
|
|
2
|
-
import chain from 'lodash/chain'
|
|
3
|
-
import filter from 'lodash/filter'
|
|
4
2
|
import flatMap from 'lodash/flatMap'
|
|
5
3
|
import map from 'lodash/map'
|
|
6
|
-
import min from 'lodash/min'
|
|
7
4
|
import round from 'lodash/round'
|
|
8
5
|
import uniq from 'lodash/uniq'
|
|
6
|
+
import { calculateBoxPlotStats } from './boxPlotStats'
|
|
9
7
|
import { ChartConfig } from '../types/ChartConfig'
|
|
10
|
-
import * as d3 from 'd3-array'
|
|
11
8
|
|
|
12
9
|
export const getBoxPlotConfig = (newConfig: ChartConfig, data: object[]) => {
|
|
13
10
|
const combinedData = data
|
|
@@ -20,44 +17,28 @@ export const getBoxPlotConfig = (newConfig: ChartConfig, data: object[]) => {
|
|
|
20
17
|
try {
|
|
21
18
|
if (!g) throw new Error('No groups resolved in box plots')
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
// Filter by xAxis data key
|
|
26
|
-
.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
27
|
-
// perform multiple operations on the filtered data
|
|
28
|
-
.thru(filteredData => ({
|
|
29
|
-
count: filteredData.length,
|
|
30
|
-
sortedData: map(filteredData, item => Number(item[seriesKey])).sort()
|
|
31
|
-
}))
|
|
32
|
-
// get the results from the chain
|
|
33
|
-
.value()
|
|
20
|
+
const filteredData = combinedData.filter(item => item[newConfig.xAxis.dataKey] === g)
|
|
21
|
+
const stats = calculateBoxPlotStats(map(filteredData, item => item[seriesKey]))
|
|
34
22
|
|
|
35
|
-
if (!
|
|
23
|
+
if (!stats) throw new Error('boxplots dont have data yet')
|
|
36
24
|
if (!plots) throw new Error('boxplots dont have plots yet')
|
|
37
25
|
|
|
38
|
-
const q1 = d3.quantile(sortedData, 0.25)
|
|
39
|
-
const q3 = d3.quantile(sortedData, 0.75)
|
|
40
|
-
|
|
41
|
-
const iqr = q3 - q1
|
|
42
|
-
const lowerBounds = q1 - 1.5 * iqr
|
|
43
|
-
const upperBounds = q3 + 1.5 * iqr
|
|
44
|
-
const nonOutliers = sortedData.filter(value => value >= lowerBounds && value <= upperBounds)
|
|
45
26
|
plots.push({
|
|
46
27
|
columnCategory: g,
|
|
47
|
-
columnMax:
|
|
48
|
-
columnThirdQuartile: round(q3, newConfig.dataFormat.roundTo),
|
|
49
|
-
columnMedian: Number(
|
|
50
|
-
columnFirstQuartile: round(q1, newConfig.dataFormat.roundTo),
|
|
51
|
-
columnMin:
|
|
52
|
-
columnCount:
|
|
53
|
-
columnSd: Number(
|
|
54
|
-
columnMean: Number(
|
|
55
|
-
columnIqr: round(iqr, newConfig.dataFormat.roundTo),
|
|
56
|
-
values:
|
|
57
|
-
columnLowerBounds:
|
|
58
|
-
columnUpperBounds:
|
|
59
|
-
columnOutliers:
|
|
60
|
-
columnNonOutliers:
|
|
28
|
+
columnMax: stats.whiskerMax,
|
|
29
|
+
columnThirdQuartile: round(stats.q3, newConfig.dataFormat.roundTo),
|
|
30
|
+
columnMedian: Number(stats.median).toFixed(newConfig.dataFormat.roundTo),
|
|
31
|
+
columnFirstQuartile: round(stats.q1, newConfig.dataFormat.roundTo),
|
|
32
|
+
columnMin: stats.whiskerMin,
|
|
33
|
+
columnCount: stats.values.length,
|
|
34
|
+
columnSd: Number(stats.deviation).toFixed(newConfig.dataFormat.roundTo),
|
|
35
|
+
columnMean: Number(stats.mean).toFixed(newConfig.dataFormat.roundTo),
|
|
36
|
+
columnIqr: round(stats.iqr, newConfig.dataFormat.roundTo),
|
|
37
|
+
values: stats.values,
|
|
38
|
+
columnLowerBounds: stats.lowerBound,
|
|
39
|
+
columnUpperBounds: stats.upperBound,
|
|
40
|
+
columnOutliers: stats.outliers,
|
|
41
|
+
columnNonOutliers: stats.nonOutliers
|
|
61
42
|
})
|
|
62
43
|
} catch (e) {
|
|
63
44
|
console.error('COVE: ', e.message) // eslint-disable-line
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Palette } from '@cdc/core/types/Palette'
|
|
2
|
+
|
|
3
|
+
const paletteDefaultsByVisualizationType: Record<string, Palette> = {
|
|
4
|
+
Line: {
|
|
5
|
+
isReversed: false,
|
|
6
|
+
version: '2.1',
|
|
7
|
+
name: 'qualitative_standard'
|
|
8
|
+
},
|
|
9
|
+
'Horizon Chart': {
|
|
10
|
+
isReversed: false,
|
|
11
|
+
version: '2.1',
|
|
12
|
+
name: 'sequential_blue'
|
|
13
|
+
},
|
|
14
|
+
HeatMap: {
|
|
15
|
+
isReversed: false,
|
|
16
|
+
version: '2.1',
|
|
17
|
+
name: 'sequential_blue'
|
|
18
|
+
},
|
|
19
|
+
Sankey: {
|
|
20
|
+
isReversed: true,
|
|
21
|
+
version: '2.1',
|
|
22
|
+
name: 'sequential_bluereverse'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const getChartTypeDefaultPalette = (visualizationType?: string): Palette | undefined => {
|
|
27
|
+
const palette = visualizationType ? paletteDefaultsByVisualizationType[visualizationType] : undefined
|
|
28
|
+
return palette ? { ...palette } : undefined
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getPaletteColors } from '@cdc/core/helpers/palettes/utils'
|
|
2
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
3
|
+
import { getV21ChartDistributionColors } from './getV21ChartDistributionColors'
|
|
4
|
+
|
|
5
|
+
export const getColorCodeCategoryColorMap = (
|
|
6
|
+
config: ChartConfig,
|
|
7
|
+
tableData: Record<string, unknown>[],
|
|
8
|
+
colorPalettes: Record<string, string[]>
|
|
9
|
+
): Map<unknown, string> => {
|
|
10
|
+
const colorCode = config.legend?.colorCode
|
|
11
|
+
if (!colorCode || !Array.isArray(tableData)) return new Map()
|
|
12
|
+
|
|
13
|
+
let palette = getPaletteColors(config, colorPalettes)
|
|
14
|
+
if (!palette.length) return new Map()
|
|
15
|
+
|
|
16
|
+
const categoryColors = new Map<unknown, string>()
|
|
17
|
+
|
|
18
|
+
const categories = Array.from(new Set(tableData.map(row => row[colorCode])))
|
|
19
|
+
palette = getV21ChartDistributionColors(config, palette, categories.length) ?? palette
|
|
20
|
+
|
|
21
|
+
categories.forEach((category, index) => {
|
|
22
|
+
categoryColors.set(category, palette[index % palette.length])
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return categoryColors
|
|
26
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { twoColorPalette } from '@cdc/core/data/colorPalettes'
|
|
2
2
|
import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
|
|
3
|
-
import {
|
|
3
|
+
import { getColorPaletteMajorVersion } from '@cdc/core/helpers/getColorPaletteMajorVersion'
|
|
4
4
|
import { scaleOrdinal } from '@visx/scale'
|
|
5
5
|
import { ChartConfig } from '../types/ChartConfig'
|
|
6
6
|
import { paletteMigrationMap } from '@cdc/core/helpers/palettes/migratePaletteName'
|
|
@@ -10,7 +10,17 @@ import {
|
|
|
10
10
|
divergentColorDistribution,
|
|
11
11
|
colorblindColorDistribution
|
|
12
12
|
} from '@cdc/core/helpers/palettes/colorDistributions'
|
|
13
|
+
import { isValidPaletteColor } from '@cdc/core/helpers/palettes/colorValidation'
|
|
13
14
|
import { applySeriesColorAssignmentsToRange } from './colorAssignmentHelpers'
|
|
15
|
+
import { getV21ChartDistributionColors } from './getV21ChartDistributionColors'
|
|
16
|
+
|
|
17
|
+
const INVALID_CUSTOM_COLOR_FALLBACK = '#000000'
|
|
18
|
+
|
|
19
|
+
const normalizeCustomColors = (colors: unknown): string[] => {
|
|
20
|
+
if (!Array.isArray(colors) || colors.length === 0) return []
|
|
21
|
+
|
|
22
|
+
return colors.map(color => (isValidPaletteColor(color) ? color.trim() : INVALID_CUSTOM_COLOR_FALLBACK))
|
|
23
|
+
}
|
|
14
24
|
|
|
15
25
|
export const getColorScale = (config: ChartConfig): ((value: string) => string) => {
|
|
16
26
|
const configPalette = ['Paired Bar', 'Deviation Bar'].includes(config.visualizationType)
|
|
@@ -19,7 +29,7 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
19
29
|
const colorPalettes = filterChartColorPalettes(config)
|
|
20
30
|
|
|
21
31
|
// Get the correct version of two-color palettes
|
|
22
|
-
const version =
|
|
32
|
+
const version = getColorPaletteMajorVersion(config)
|
|
23
33
|
const versionKey = `v${version}`
|
|
24
34
|
const versionedTwoColorPalette = twoColorPalette[versionKey] || twoColorPalette.v2
|
|
25
35
|
|
|
@@ -28,17 +38,24 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
28
38
|
? versionedTwoColorPalette
|
|
29
39
|
: colorPalettes
|
|
30
40
|
|
|
31
|
-
const allPalettes: Record<string, string[]> = { ...versionedTwoColorPalette, ...colorPalettes }
|
|
32
|
-
|
|
33
41
|
// Migrate old palette name if needed
|
|
34
42
|
const migratedPaletteName = configPalette ? configPalette : getFallbackColorPalette(config)
|
|
43
|
+
let namedPalette =
|
|
44
|
+
palettesSource[migratePaletteWithMap(migratedPaletteName, paletteMigrationMap, false)] ||
|
|
45
|
+
palettesSource[configPalette]
|
|
46
|
+
|
|
47
|
+
// Use a visible sentinel when saved palette config cannot resolve.
|
|
48
|
+
if (!namedPalette) {
|
|
49
|
+
console.warn(`Palette "${configPalette}" not found, falling back to black`)
|
|
50
|
+
namedPalette = [INVALID_CUSTOM_COLOR_FALLBACK]
|
|
51
|
+
}
|
|
35
52
|
|
|
36
53
|
const domain = config.runtime.seriesLabelsAll
|
|
37
54
|
|
|
38
|
-
const customColorsOrdered = config.general?.palette?.customColorsOrdered
|
|
55
|
+
const customColorsOrdered = normalizeCustomColors(config.general?.palette?.customColorsOrdered)
|
|
39
56
|
|
|
40
57
|
// Check for customColorsOrdered first (direct 1-to-1 mapping, no distribution needed)
|
|
41
|
-
if (
|
|
58
|
+
if (customColorsOrdered.length > 0) {
|
|
42
59
|
const range = applySeriesColorAssignmentsToRange(config, domain, customColorsOrdered)
|
|
43
60
|
return scaleOrdinal({
|
|
44
61
|
domain,
|
|
@@ -47,26 +64,19 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
47
64
|
})
|
|
48
65
|
}
|
|
49
66
|
|
|
50
|
-
const customColors = config.general?.palette?.customColors
|
|
51
|
-
const isUsingCustomColors =
|
|
52
|
-
let palette =
|
|
53
|
-
(isUsingCustomColors ? customColors : undefined) ||
|
|
54
|
-
palettesSource[migratePaletteWithMap(migratedPaletteName, paletteMigrationMap, false)] ||
|
|
55
|
-
palettesSource[configPalette]
|
|
56
|
-
|
|
57
|
-
// Fallback to a default palette if none found
|
|
58
|
-
if (!palette) {
|
|
59
|
-
console.warn(`Palette "${configPalette}" not found, falling back to default`)
|
|
60
|
-
palette = Object.values(allPalettes)[0] || ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
|
|
61
|
-
}
|
|
67
|
+
const customColors = normalizeCustomColors(config.general?.palette?.customColors)
|
|
68
|
+
const isUsingCustomColors = customColors.length > 0
|
|
69
|
+
let palette = isUsingCustomColors ? customColors : namedPalette
|
|
62
70
|
|
|
63
71
|
let numberOfKeys = config.runtime.seriesKeys.length
|
|
64
72
|
|
|
65
73
|
// Apply enhanced color distribution (same logic as pie charts)
|
|
66
|
-
const paletteVersion =
|
|
74
|
+
const paletteVersion = getColorPaletteMajorVersion(config)
|
|
75
|
+
const v21DistributionColors = getV21ChartDistributionColors(config, palette, numberOfKeys)
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
if (v21DistributionColors) {
|
|
78
|
+
palette = v21DistributionColors
|
|
79
|
+
} else if (isUsingCustomColors || paletteVersion !== 2 || numberOfKeys > 9 || palette.length !== 9) {
|
|
70
80
|
// Use existing logic for v1 palettes and other cases
|
|
71
81
|
while (numberOfKeys > palette.length) {
|
|
72
82
|
palette = palette.concat(palette)
|
|
@@ -78,7 +88,6 @@ export const getColorScale = (config: ChartConfig): ((value: string) => string)
|
|
|
78
88
|
const isDivergent = configPalette && configPalette.includes('divergent')
|
|
79
89
|
const isColorblindSafe =
|
|
80
90
|
configPalette && (configPalette.includes('colorblindsafe') || configPalette.includes('qualitative_standard'))
|
|
81
|
-
|
|
82
91
|
// Determine which distribution to use based on palette type
|
|
83
92
|
let distributionMap = null
|
|
84
93
|
if (isDivergent) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Axis } from '@cdc/core/types/Axis'
|
|
2
|
+
|
|
3
|
+
export type DynamicYAxisCategory = {
|
|
4
|
+
label: string
|
|
5
|
+
upperBoundKey?: string
|
|
6
|
+
color?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type DynamicYAxisCategoriesConfig = {
|
|
10
|
+
categories: DynamicYAxisCategory[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type DynamicYAxisCategoriesParams = {
|
|
14
|
+
config?: DynamicYAxisCategoriesConfig
|
|
15
|
+
data?: Record<string, any>[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type DynamicYAxisCategoriesResult = {
|
|
19
|
+
categories: NonNullable<Axis['categories']>
|
|
20
|
+
axisMax: number | null
|
|
21
|
+
} | null
|
|
22
|
+
|
|
23
|
+
const toFiniteNumber = value => {
|
|
24
|
+
const number = Number(value)
|
|
25
|
+
return Number.isFinite(number) ? number : null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const getDynamicYAxisCategories = ({
|
|
29
|
+
config,
|
|
30
|
+
data = []
|
|
31
|
+
}: DynamicYAxisCategoriesParams): DynamicYAxisCategoriesResult => {
|
|
32
|
+
if (!config || !data.length || !config.categories?.length) return null
|
|
33
|
+
|
|
34
|
+
const currentRow = data.find(row =>
|
|
35
|
+
config.categories.every(
|
|
36
|
+
(category, index) =>
|
|
37
|
+
(index === config.categories.length - 1 && !category.upperBoundKey) ||
|
|
38
|
+
toFiniteNumber(row?.[category.upperBoundKey]) !== null
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
if (!currentRow) return null
|
|
42
|
+
|
|
43
|
+
const upperBounds = config.categories.map(category =>
|
|
44
|
+
category.upperBoundKey ? toFiniteNumber(currentRow[category.upperBoundKey]) : null
|
|
45
|
+
)
|
|
46
|
+
const lastCategory = config.categories[config.categories.length - 1]
|
|
47
|
+
const hasOpenEndedFinalCategory = !lastCategory.upperBoundKey
|
|
48
|
+
const axisMax = hasOpenEndedFinalCategory ? null : upperBounds[upperBounds.length - 1]
|
|
49
|
+
const resolvedUpperBounds = hasOpenEndedFinalCategory ? upperBounds.slice(0, -1) : upperBounds
|
|
50
|
+
|
|
51
|
+
if (resolvedUpperBounds.some(bound => bound === null)) return null
|
|
52
|
+
if (!hasOpenEndedFinalCategory && (axisMax === null || axisMax <= 0)) return null
|
|
53
|
+
|
|
54
|
+
const numericUpperBounds = resolvedUpperBounds as number[]
|
|
55
|
+
if (numericUpperBounds.some((bound, index) => index > 0 && bound <= numericUpperBounds[index - 1])) {
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const categories = config.categories.map((category, index) => ({
|
|
60
|
+
label: category.label,
|
|
61
|
+
height:
|
|
62
|
+
hasOpenEndedFinalCategory && index === config.categories.length - 1
|
|
63
|
+
? ''
|
|
64
|
+
: String(numericUpperBounds[index] - (numericUpperBounds[index - 1] || 0)),
|
|
65
|
+
color: category.color || '#f1f1f1'
|
|
66
|
+
}))
|
|
67
|
+
|
|
68
|
+
return { categories, axisMax }
|
|
69
|
+
}
|
package/src/helpers/getMinMax.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ChartConfig } from '../types/ChartConfig'
|
|
2
2
|
import { getCleanTopTickMax } from './getCleanTopTickMax'
|
|
3
3
|
import { getAxisMaxOverride } from './getAxisMaxOverride'
|
|
4
|
+
import getStackedSeriesMax from './getStackedSeriesMax'
|
|
4
5
|
|
|
5
6
|
type GetMinMaxProps = {
|
|
6
7
|
/** config - standard chart config */
|
|
@@ -120,24 +121,25 @@ const getMinMax = ({
|
|
|
120
121
|
let rightAxisSeriesItems = series.filter(s => s.axis === 'Right')
|
|
121
122
|
|
|
122
123
|
const findMaxFromSeriesKeys = (data, seriesData, max, axis = 'left') => {
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
const stackedBarSeries =
|
|
125
|
+
config.visualizationSubType === 'stacked' && axis === 'left' ? seriesData.filter(s => s.type === 'Bar') : []
|
|
126
|
+
const stackedBarSeriesKeys = new Set(stackedBarSeries.map(s => s.dataKey))
|
|
127
|
+
let axisSeriesKeys = seriesData.map(i => i.dataKey).filter(key => !stackedBarSeriesKeys.has(key)) || []
|
|
125
128
|
|
|
126
129
|
axisSeriesKeys.forEach(key => {
|
|
127
|
-
let _seriesData = seriesData.find(s => s.dataKey === key)
|
|
128
130
|
let _data = data.map(d => d[key])
|
|
129
131
|
let seriesMax = Math.max.apply(null, _data)
|
|
130
|
-
if (config.visualizationSubType === 'stacked' && axis === 'left' && _seriesData.type === 'Bar') {
|
|
131
|
-
stackedBarMax += seriesMax
|
|
132
|
-
}
|
|
133
132
|
if (seriesMax > max) {
|
|
134
133
|
max = seriesMax
|
|
135
134
|
}
|
|
135
|
+
})
|
|
136
136
|
|
|
137
|
+
if (stackedBarSeries.length) {
|
|
138
|
+
const stackedBarMax = getStackedSeriesMax(data, stackedBarSeries)
|
|
137
139
|
if (max < stackedBarMax) {
|
|
138
140
|
max = stackedBarMax
|
|
139
141
|
}
|
|
140
|
-
}
|
|
142
|
+
}
|
|
141
143
|
return max
|
|
142
144
|
}
|
|
143
145
|
leftMax = findMaxFromSeriesKeys(dataForMinMax, leftAxisSeriesItems, leftMax, 'left')
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getSeriesName } from '@cdc/core/helpers/getSeriesName'
|
|
2
|
+
|
|
1
3
|
export const getNewRuntime = (visualizationConfig, newFilteredData) => {
|
|
2
4
|
const runtime = visualizationConfig.runtime ? { ...visualizationConfig.runtime } : {}
|
|
3
5
|
runtime.series = []
|
|
@@ -23,8 +25,9 @@ export const getNewRuntime = (visualizationConfig, newFilteredData) => {
|
|
|
23
25
|
|
|
24
26
|
runtime.seriesKeys = runtime.series
|
|
25
27
|
? runtime.series.map(series => {
|
|
26
|
-
|
|
27
|
-
runtime.
|
|
28
|
+
const seriesName = getSeriesName(series.dataKey, { series: [series] })
|
|
29
|
+
runtime.seriesLabels[series.dataKey] = seriesName
|
|
30
|
+
runtime.seriesLabelsAll.push(seriesName)
|
|
28
31
|
return series.dataKey
|
|
29
32
|
})
|
|
30
33
|
: []
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import isNumber from '@cdc/core/helpers/isNumber'
|
|
2
|
+
|
|
3
|
+
type SeriesKeyOrItem =
|
|
4
|
+
| string
|
|
5
|
+
| {
|
|
6
|
+
dataKey?: string
|
|
7
|
+
originalDataKey?: string
|
|
8
|
+
dynamicCategory?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const cleanValue = value => {
|
|
12
|
+
if (value === null || value === '') return ''
|
|
13
|
+
return typeof value === 'string' ? value.replace(/[,$]/g, '') : value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const getSeriesDataKey = (seriesKeyOrItem: SeriesKeyOrItem) => {
|
|
17
|
+
if (typeof seriesKeyOrItem === 'string') return seriesKeyOrItem
|
|
18
|
+
return seriesKeyOrItem.dynamicCategory && seriesKeyOrItem.originalDataKey
|
|
19
|
+
? seriesKeyOrItem.originalDataKey
|
|
20
|
+
: seriesKeyOrItem.dataKey
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const getStackedSeriesMax = (data: Object[] = [], seriesKeysOrSeriesItems: SeriesKeyOrItem[] = []) => {
|
|
24
|
+
let maxValue = Number.NEGATIVE_INFINITY
|
|
25
|
+
let hasValue = false
|
|
26
|
+
|
|
27
|
+
for (const row of data) {
|
|
28
|
+
let rowSum = 0
|
|
29
|
+
let hasRowValue = false
|
|
30
|
+
|
|
31
|
+
for (const seriesKeyOrItem of seriesKeysOrSeriesItems) {
|
|
32
|
+
const seriesKey = getSeriesDataKey(seriesKeyOrItem)
|
|
33
|
+
if (!seriesKey) continue
|
|
34
|
+
|
|
35
|
+
const value = cleanValue(row[seriesKey])
|
|
36
|
+
if (isNumber(value)) {
|
|
37
|
+
rowSum += Number(value)
|
|
38
|
+
hasRowValue = true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (hasRowValue) {
|
|
43
|
+
maxValue = Math.max(maxValue, rowSum)
|
|
44
|
+
hasValue = true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return hasValue ? maxValue : 0
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default getStackedSeriesMax
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ChartConfig, type VisualizationType } from '../types/ChartConfig'
|
|
2
|
+
import {
|
|
3
|
+
chartV21ColorDistribution,
|
|
4
|
+
divergentColorDistribution,
|
|
5
|
+
qualitativeStandardColorDistribution
|
|
6
|
+
} from '@cdc/core/helpers/palettes/colorDistributions'
|
|
7
|
+
|
|
8
|
+
const V21_COLOR_DISTRIBUTION_CHART_TYPES = new Set<VisualizationType>([
|
|
9
|
+
'Area Chart',
|
|
10
|
+
'Bar',
|
|
11
|
+
'Box Plot',
|
|
12
|
+
'Bump Chart',
|
|
13
|
+
'Combo',
|
|
14
|
+
'Line',
|
|
15
|
+
'Pie',
|
|
16
|
+
'Radar',
|
|
17
|
+
'Scatter Plot',
|
|
18
|
+
'Spark Line'
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
export const getV21ChartDistributionColors = (
|
|
22
|
+
config: ChartConfig,
|
|
23
|
+
palette: string[],
|
|
24
|
+
itemCount: number
|
|
25
|
+
): string[] | undefined => {
|
|
26
|
+
const paletteName = config.general?.palette?.name || config.palette || ''
|
|
27
|
+
const hasCustomColors =
|
|
28
|
+
Boolean(config.general?.palette?.customColors?.length) ||
|
|
29
|
+
Boolean(config.general?.palette?.customColorsOrdered?.length)
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
config.general?.palette?.version !== '2.1' ||
|
|
33
|
+
!V21_COLOR_DISTRIBUTION_CHART_TYPES.has(config.visualizationType) ||
|
|
34
|
+
hasCustomColors ||
|
|
35
|
+
palette.length !== 9
|
|
36
|
+
) {
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let distributionMap: Record<number, number[]> | undefined
|
|
41
|
+
if (paletteName.includes('sequential')) {
|
|
42
|
+
distributionMap = chartV21ColorDistribution
|
|
43
|
+
} else if (paletteName.includes('divergent')) {
|
|
44
|
+
distributionMap = divergentColorDistribution
|
|
45
|
+
} else if (paletteName.includes('qualitative_standard')) {
|
|
46
|
+
distributionMap = qualitativeStandardColorDistribution
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const distributionIndices = distributionMap?.[itemCount]
|
|
50
|
+
if (!distributionIndices) return undefined
|
|
51
|
+
|
|
52
|
+
const orientedIndices = paletteName.endsWith('reverse')
|
|
53
|
+
? [...distributionIndices].reverse().map(index => palette.length - 1 - index)
|
|
54
|
+
: distributionIndices
|
|
55
|
+
|
|
56
|
+
return orientedIndices.map(index => palette[index])
|
|
57
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { ChartConfig } from '../types/ChartConfig'
|
|
2
|
-
|
|
3
|
-
const getNumericValue = number => {
|
|
4
|
-
if (typeof number === 'string') return parseFloat(number.replace(/,/g, ''))
|
|
5
|
-
return Number(number)
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export const handleRankByValue = (data, passedConfig: ChartConfig) => {
|
|
9
|
-
if (passedConfig.rankByValue) {
|
|
10
|
-
const series = passedConfig.series[0].dataKey
|
|
11
|
-
const sorted = data.
|
|
12
|
-
return passedConfig.rankByValue === 'asc' ? sorted : sorted.reverse()
|
|
13
|
-
}
|
|
14
|
-
return data
|
|
15
|
-
}
|
|
1
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
2
|
+
|
|
3
|
+
const getNumericValue = number => {
|
|
4
|
+
if (typeof number === 'string') return parseFloat(number.replace(/,/g, ''))
|
|
5
|
+
return Number(number)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const handleRankByValue = (data, passedConfig: ChartConfig) => {
|
|
9
|
+
if (passedConfig.rankByValue) {
|
|
10
|
+
const series = passedConfig.series[0].dataKey
|
|
11
|
+
const sorted = data.toSorted((a, b) => getNumericValue(a[series]) - getNumericValue(b[series]))
|
|
12
|
+
return passedConfig.rankByValue === 'asc' ? sorted : sorted.reverse()
|
|
13
|
+
}
|
|
14
|
+
return data
|
|
15
|
+
}
|
|
@@ -131,3 +131,13 @@ export const getSeriesColumnFormattingParams = (columnConfig?: Partial<Column>):
|
|
|
131
131
|
|
|
132
132
|
return Object.keys(formattingParams).length ? formattingParams : undefined
|
|
133
133
|
}
|
|
134
|
+
|
|
135
|
+
export const getAdditionalColumnFormattingParams = (columnConfig?: Partial<Column>): ColumnFormattingParams => {
|
|
136
|
+
const explicitFormattingParams = getSeriesColumnFormattingParams(columnConfig)
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
addColPrefix: '',
|
|
140
|
+
addColSuffix: '',
|
|
141
|
+
...(explicitFormattingParams || {})
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { isMobileHeightViewport } from '@cdc/core/helpers/viewports'
|
|
2
|
-
import { ChartConfig
|
|
2
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
3
3
|
import { EDITOR_WIDTH } from '@cdc/core/helpers/constants'
|
|
4
|
+
import type { ViewPort } from '@cdc/core/types/ViewPort'
|
|
4
5
|
|
|
5
6
|
export function getOrientation(
|
|
6
7
|
{ orientation, heights, visualizationType }: Pick<ChartConfig, 'orientation' | 'heights' | 'visualizationType'>,
|
|
7
|
-
currentViewport:
|
|
8
|
+
currentViewport: ViewPort
|
|
8
9
|
): 'vertical' | 'horizontal' | 'mobileVertical' {
|
|
9
10
|
const isForestPlot = visualizationType === 'Forest Plot'
|
|
10
11
|
const useVertical = orientation === 'vertical' || isForestPlot
|
|
@@ -15,7 +16,7 @@ export function getOrientation(
|
|
|
15
16
|
}
|
|
16
17
|
export function calcInitialHeight(
|
|
17
18
|
{ heights, orientation, visualizationType }: Pick<ChartConfig, 'heights' | 'orientation' | 'visualizationType'>,
|
|
18
|
-
currentViewport:
|
|
19
|
+
currentViewport: ViewPort
|
|
19
20
|
): number {
|
|
20
21
|
// if no heights are provided assume config has not been loaded
|
|
21
22
|
if (!heights) return 0
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sortByNumericText } from '@cdc/core/helpers/sorting'
|
|
1
2
|
import { scaleOrdinal } from '@visx/scale'
|
|
2
3
|
import { getColorScale } from './getColorScale'
|
|
3
4
|
import { ColorScale } from '../types/ChartContext'
|
|
@@ -105,18 +106,16 @@ export const applyTileOrder = (tileItems, orderType, customOrder, config) => {
|
|
|
105
106
|
|
|
106
107
|
switch (orderType) {
|
|
107
108
|
case 'asc':
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return titleA.localeCompare(titleB)
|
|
112
|
-
})
|
|
109
|
+
return sortByNumericText(tileItems, item =>
|
|
110
|
+
getTileDisplayTitle(item.mode, item.seriesKey, item.tileValue, item.key, config)
|
|
111
|
+
)
|
|
113
112
|
|
|
114
113
|
case 'desc':
|
|
115
|
-
return
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
return sortByNumericText(
|
|
115
|
+
tileItems,
|
|
116
|
+
item => getTileDisplayTitle(item.mode, item.seriesKey, item.tileValue, item.key, config),
|
|
117
|
+
'desc'
|
|
118
|
+
)
|
|
120
119
|
|
|
121
120
|
case 'custom':
|
|
122
121
|
if (!customOrder || customOrder.length === 0) {
|
|
@@ -154,9 +153,7 @@ export const getTileKeys = (config, data) => {
|
|
|
154
153
|
const tileColumn = config.smallMultiples.tileColumn
|
|
155
154
|
if (!tileColumn) return []
|
|
156
155
|
|
|
157
|
-
return Array.from(new Set(data.map(row => row[tileColumn])))
|
|
158
|
-
.filter(val => val != null)
|
|
159
|
-
.sort()
|
|
156
|
+
return sortByNumericText(Array.from(new Set(data.map(row => row[tileColumn]))).filter(val => val != null))
|
|
160
157
|
}
|
|
161
158
|
return []
|
|
162
159
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { applyChartTypeContract } from '../applyChartTypeContract'
|
|
3
|
+
|
|
4
|
+
const chart = (visualizationType: string) => ({
|
|
5
|
+
type: 'chart',
|
|
6
|
+
visualizationType,
|
|
7
|
+
orientation: 'vertical',
|
|
8
|
+
series: [{ dataKey: 'value' }],
|
|
9
|
+
xAxis: { type: 'categorical', dataKey: 'date' },
|
|
10
|
+
yAxis: {},
|
|
11
|
+
legend: {},
|
|
12
|
+
general: { paletteColorCount: 2 },
|
|
13
|
+
table: {}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('applyChartTypeContract', () => {
|
|
17
|
+
it.each(['Area Chart', 'Horizon Chart', 'Box Plot', 'Bump Chart', 'HeatMap', 'Forecasting'])(
|
|
18
|
+
'does not mutate the input for %s transitions',
|
|
19
|
+
visualizationType => {
|
|
20
|
+
const input = chart(visualizationType)
|
|
21
|
+
const before = structuredClone(input)
|
|
22
|
+
|
|
23
|
+
const result = applyChartTypeContract(input as any)
|
|
24
|
+
|
|
25
|
+
expect(input).toEqual(before)
|
|
26
|
+
expect(result).not.toBe(input)
|
|
27
|
+
expect(result.xAxis).not.toBe(input.xAxis)
|
|
28
|
+
expect(result.series).not.toBe(input.series)
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
it('authors the stacked Area Chart subtype', () => {
|
|
33
|
+
const result = applyChartTypeContract(chart('Area Chart') as any)
|
|
34
|
+
|
|
35
|
+
expect(result.visualizationSubType).toBe('stacked')
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { calculateBoxPlotStats, getSortedBoxPlotValues } from '../boxPlotStats'
|
|
4
|
+
|
|
5
|
+
describe('boxPlotStats', () => {
|
|
6
|
+
it('normalizes numeric strings once for all box plot stats consumers', () => {
|
|
7
|
+
expect(getSortedBoxPlotValues(['1,000', '20', '', null, undefined, 'n/a', '3'])).toEqual([3, 20, 1000])
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('calculates quartiles, fences, whiskers, and outliers from sorted numeric values', () => {
|
|
11
|
+
const stats = calculateBoxPlotStats(['10', '20', '30', '40', '1,000'])
|
|
12
|
+
|
|
13
|
+
expect(stats).toMatchObject({
|
|
14
|
+
values: [10, 20, 30, 40, 1000],
|
|
15
|
+
q1: 20,
|
|
16
|
+
q3: 40,
|
|
17
|
+
median: 30,
|
|
18
|
+
iqr: 20,
|
|
19
|
+
lowerBound: -10,
|
|
20
|
+
upperBound: 70,
|
|
21
|
+
nonOutliers: [10, 20, 30, 40],
|
|
22
|
+
outliers: [1000],
|
|
23
|
+
whiskerMin: 10,
|
|
24
|
+
whiskerMax: 40
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('returns null when no finite values can be calculated', () => {
|
|
29
|
+
expect(calculateBoxPlotStats(['', null, undefined, 'n/a'])).toBeNull()
|
|
30
|
+
})
|
|
31
|
+
})
|