@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,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
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import uniq from 'lodash/uniq'
|
|
2
2
|
|
|
3
3
|
export const isConvertLineToBarGraph = (configObj, filteredData) => {
|
|
4
4
|
const { allowLineToBarGraph, series, visualizationType, xAxis } = configObj
|
|
@@ -6,6 +6,6 @@ export const isConvertLineToBarGraph = (configObj, filteredData) => {
|
|
|
6
6
|
const lineWithLessThanThreePoints = visualizationType === 'Line' && filteredData?.length < 3
|
|
7
7
|
const isDynamicSeries = series?.some(series => series.dynamicCategory)
|
|
8
8
|
const isDynamicWithLessThanThreePoints =
|
|
9
|
-
isDynamicSeries &&
|
|
9
|
+
isDynamicSeries && uniq(filteredData?.map(data => data[xAxis.dataKey])).length <= 2
|
|
10
10
|
return lineWithLessThanThreePoints || isDynamicWithLessThanThreePoints
|
|
11
11
|
}
|
|
@@ -12,13 +12,22 @@ type ColumnFormattingParams = {
|
|
|
12
12
|
|
|
13
13
|
const hasOwn = (object: object, key: keyof Column) => Object.prototype.hasOwnProperty.call(object, key)
|
|
14
14
|
const isNonEmptyString = (value: unknown) => typeof value === 'string' && value !== ''
|
|
15
|
+
const getNumericRoundToPlace = (value: Column['roundToPlace']): number | undefined => {
|
|
16
|
+
if (value === undefined || value === null || (typeof value === 'string' && value.trim() === '')) {
|
|
17
|
+
return undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const numericValue = Number(value)
|
|
21
|
+
if (!Number.isFinite(numericValue)) return undefined
|
|
22
|
+
|
|
23
|
+
return Math.max(0, Math.min(20, Math.round(numericValue)))
|
|
24
|
+
}
|
|
15
25
|
|
|
16
26
|
export const createDefaultSeriesColumnConfig = (columnName: string): Column => ({
|
|
17
27
|
name: columnName,
|
|
18
28
|
label: columnName,
|
|
19
29
|
prefix: '',
|
|
20
30
|
suffix: '',
|
|
21
|
-
roundToPlace: 0,
|
|
22
31
|
commas: false,
|
|
23
32
|
dataTable: true,
|
|
24
33
|
order: undefined,
|
|
@@ -70,12 +79,18 @@ export const upsertSeriesColumnConfig = (
|
|
|
70
79
|
): ChartColumns => {
|
|
71
80
|
const existingEntry = findColumnConfigByName(columns, seriesKey)
|
|
72
81
|
const columnKey = existingEntry?.columnKey || seriesKey
|
|
73
|
-
const nextColumnConfig = {
|
|
82
|
+
const nextColumnConfig: Partial<Column> = {
|
|
74
83
|
...(existingEntry?.columnConfig || {}),
|
|
75
84
|
...updates,
|
|
76
85
|
name: seriesKey
|
|
77
86
|
}
|
|
78
87
|
|
|
88
|
+
Object.entries(updates).forEach(([key, value]) => {
|
|
89
|
+
if (value === undefined) {
|
|
90
|
+
delete nextColumnConfig[key as keyof Column]
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
79
94
|
if (
|
|
80
95
|
nextColumnConfig.label === undefined &&
|
|
81
96
|
!hasOwn(existingEntry?.columnConfig || {}, 'label') &&
|
|
@@ -104,7 +119,10 @@ export const getSeriesColumnFormattingParams = (columnConfig?: Partial<Column>):
|
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
if (hasOwn(columnConfig, 'roundToPlace')) {
|
|
107
|
-
|
|
122
|
+
const numericRoundToPlace = getNumericRoundToPlace(columnConfig.roundToPlace)
|
|
123
|
+
if (numericRoundToPlace !== undefined) {
|
|
124
|
+
formattingParams.addColRoundTo = numericRoundToPlace
|
|
125
|
+
}
|
|
108
126
|
}
|
|
109
127
|
|
|
110
128
|
if (hasOwn(columnConfig, 'commas')) {
|
|
@@ -113,3 +131,13 @@ export const getSeriesColumnFormattingParams = (columnConfig?: Partial<Column>):
|
|
|
113
131
|
|
|
114
132
|
return Object.keys(formattingParams).length ? formattingParams : undefined
|
|
115
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,12 +1,11 @@
|
|
|
1
|
-
import { clamp } from 'lodash'
|
|
2
|
-
|
|
3
1
|
import { isMobileHeightViewport } from '@cdc/core/helpers/viewports'
|
|
4
|
-
import { ChartConfig
|
|
2
|
+
import { ChartConfig } from '../types/ChartConfig'
|
|
5
3
|
import { EDITOR_WIDTH } from '@cdc/core/helpers/constants'
|
|
4
|
+
import type { ViewPort } from '@cdc/core/types/ViewPort'
|
|
6
5
|
|
|
7
6
|
export function getOrientation(
|
|
8
7
|
{ orientation, heights, visualizationType }: Pick<ChartConfig, 'orientation' | 'heights' | 'visualizationType'>,
|
|
9
|
-
currentViewport:
|
|
8
|
+
currentViewport: ViewPort
|
|
10
9
|
): 'vertical' | 'horizontal' | 'mobileVertical' {
|
|
11
10
|
const isForestPlot = visualizationType === 'Forest Plot'
|
|
12
11
|
const useVertical = orientation === 'vertical' || isForestPlot
|
|
@@ -17,7 +16,7 @@ export function getOrientation(
|
|
|
17
16
|
}
|
|
18
17
|
export function calcInitialHeight(
|
|
19
18
|
{ heights, orientation, visualizationType }: Pick<ChartConfig, 'heights' | 'orientation' | 'visualizationType'>,
|
|
20
|
-
currentViewport:
|
|
19
|
+
currentViewport: ViewPort
|
|
21
20
|
): number {
|
|
22
21
|
// if no heights are provided assume config has not been loaded
|
|
23
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
|
+
})
|
|
@@ -72,4 +72,33 @@ describe('buildBumpChartTooltipHtml', () => {
|
|
|
72
72
|
expect(html).not.toContain('2014-01-01T00:00:00.000Z')
|
|
73
73
|
expect(html).toContain('Frequency: 149')
|
|
74
74
|
})
|
|
75
|
+
|
|
76
|
+
it('uses a customized column label instead of the active runtime series name', () => {
|
|
77
|
+
const html = buildBumpChartTooltipHtml({
|
|
78
|
+
config: buildConfig({
|
|
79
|
+
columns: {
|
|
80
|
+
Rank: { name: 'Rank', label: 'Rank Label' }
|
|
81
|
+
}
|
|
82
|
+
}),
|
|
83
|
+
colorScale: undefined,
|
|
84
|
+
dataRow: { Timestamp: '2014-01-01T00:00:00.000Z', Rank: '1' },
|
|
85
|
+
series: { dataKey: 'Rank', name: 'Runtime Rank' },
|
|
86
|
+
helpers
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
expect(html).toContain('Rank Label: 1')
|
|
90
|
+
expect(html).not.toContain('Runtime Rank: 1')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('falls back to the series name when the column label matches the data key', () => {
|
|
94
|
+
const html = buildBumpChartTooltipHtml({
|
|
95
|
+
config: buildConfig(),
|
|
96
|
+
colorScale: undefined,
|
|
97
|
+
dataRow: { Timestamp: '2014-01-01T00:00:00.000Z', Rank: '1' },
|
|
98
|
+
series: { dataKey: 'Rank', name: 'Runtime Rank' },
|
|
99
|
+
helpers
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
expect(html).toContain('Runtime Rank: 1')
|
|
103
|
+
})
|
|
75
104
|
})
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { createMockConfig } from '../../components/LinearChart/tests/mockConfigContext'
|
|
3
|
+
|
|
4
|
+
vi.mock('@cdc/core/helpers/getTextWidth', () => ({
|
|
5
|
+
getTextWidth: (text: string) => text.length
|
|
6
|
+
}))
|
|
7
|
+
|
|
8
|
+
import { calculateRightYAxisWidth } from '../calculateRightYAxisWidth'
|
|
9
|
+
|
|
10
|
+
const formatNumber = (value: any) => String(value)
|
|
11
|
+
const tickLabelFont = 'normal 16px sans-serif'
|
|
12
|
+
const axisLabelFontSize = 16
|
|
13
|
+
|
|
14
|
+
const createRightAxisConfig = (yAxisOverrides = {}, runtimeYAxisOverrides = {}) => {
|
|
15
|
+
const baseConfig = createMockConfig()
|
|
16
|
+
|
|
17
|
+
return createMockConfig({
|
|
18
|
+
yAxis: {
|
|
19
|
+
...baseConfig.yAxis,
|
|
20
|
+
rightAxisSize: 0,
|
|
21
|
+
rightHideAxis: false,
|
|
22
|
+
rightHideLabel: false,
|
|
23
|
+
rightHideTicks: false,
|
|
24
|
+
rightLabel: '',
|
|
25
|
+
rightLabelOffsetSize: 0,
|
|
26
|
+
rightTitlePlacement: 'top',
|
|
27
|
+
...yAxisOverrides
|
|
28
|
+
},
|
|
29
|
+
runtime: {
|
|
30
|
+
...baseConfig.runtime,
|
|
31
|
+
yAxis: {
|
|
32
|
+
...baseConfig.runtime.yAxis,
|
|
33
|
+
rightHideTicks: false,
|
|
34
|
+
...runtimeYAxisOverrides
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('calculateRightYAxisWidth', () => {
|
|
41
|
+
it('uses measured right tick label space when saved rightAxisSize is zero', () => {
|
|
42
|
+
const width = calculateRightYAxisWidth({
|
|
43
|
+
axisLabelFontSize,
|
|
44
|
+
config: createRightAxisConfig(),
|
|
45
|
+
formatNumber,
|
|
46
|
+
tickLabelFont,
|
|
47
|
+
tickValues: [0, 1000],
|
|
48
|
+
yScaleRight: undefined
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
expect(width).toBe(17)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('reserves measured side-title space after right tick labels while ignoring saved offset', () => {
|
|
55
|
+
const width = calculateRightYAxisWidth({
|
|
56
|
+
axisLabelFontSize,
|
|
57
|
+
config: createRightAxisConfig({
|
|
58
|
+
rightLabel: 'New Participants',
|
|
59
|
+
rightLabelOffsetSize: 36,
|
|
60
|
+
rightTitlePlacement: 'side'
|
|
61
|
+
}),
|
|
62
|
+
formatNumber,
|
|
63
|
+
tickLabelFont,
|
|
64
|
+
tickValues: [0, 10],
|
|
65
|
+
yScaleRight: undefined
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
expect(width).toBe(36)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('ignores a larger saved rightAxisSize', () => {
|
|
72
|
+
const width = calculateRightYAxisWidth({
|
|
73
|
+
axisLabelFontSize,
|
|
74
|
+
config: createRightAxisConfig({ rightAxisSize: 100 }),
|
|
75
|
+
formatNumber,
|
|
76
|
+
tickLabelFont,
|
|
77
|
+
tickValues: [0, 1000],
|
|
78
|
+
yScaleRight: undefined
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
expect(width).toBe(17)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('allows no gutter when all right-axis content is hidden', () => {
|
|
85
|
+
const width = calculateRightYAxisWidth({
|
|
86
|
+
axisLabelFontSize,
|
|
87
|
+
config: createRightAxisConfig(
|
|
88
|
+
{
|
|
89
|
+
rightHideAxis: true,
|
|
90
|
+
rightHideLabel: true,
|
|
91
|
+
rightTitlePlacement: 'top'
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
rightHideTicks: true
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
formatNumber,
|
|
98
|
+
tickLabelFont,
|
|
99
|
+
tickValues: [],
|
|
100
|
+
yScaleRight: undefined
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
expect(width).toBe(0)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { getOrderedCategoryValues, isCustomCategoryOrderActive, sortByCategoryOrder } from '../categoryOrder'
|
|
3
|
+
|
|
4
|
+
describe('categoryOrder helpers', () => {
|
|
5
|
+
const data = [
|
|
6
|
+
{ category: 'A', value: 1 },
|
|
7
|
+
{ category: 'D', value: 4 },
|
|
8
|
+
{ category: 'C', value: 3 },
|
|
9
|
+
{ category: 'D', value: 5 },
|
|
10
|
+
{ category: 'L', value: 12 }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
it('detects custom category order only for categorical axes with configured values', () => {
|
|
14
|
+
expect(
|
|
15
|
+
isCustomCategoryOrderActive({
|
|
16
|
+
xAxis: { type: 'categorical', dataKey: 'category', categoryOrderType: 'custom', categoryOrder: ['C'] } as any
|
|
17
|
+
})
|
|
18
|
+
).toBe(true)
|
|
19
|
+
|
|
20
|
+
expect(
|
|
21
|
+
isCustomCategoryOrderActive({
|
|
22
|
+
xAxis: { type: 'date', dataKey: 'category', categoryOrderType: 'custom', categoryOrder: ['C'] } as any
|
|
23
|
+
})
|
|
24
|
+
).toBe(false)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('sorts rows by configured category order and keeps duplicate rows stable', () => {
|
|
28
|
+
const sorted = sortByCategoryOrder(data, {
|
|
29
|
+
xAxis: {
|
|
30
|
+
type: 'categorical',
|
|
31
|
+
dataKey: 'category',
|
|
32
|
+
categoryOrderType: 'custom',
|
|
33
|
+
categoryOrder: ['A', 'C', 'D', 'L']
|
|
34
|
+
} as any
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
expect(sorted.map(row => row.category)).toEqual(['A', 'C', 'D', 'D', 'L'])
|
|
38
|
+
expect(sorted.map(row => row.value)).toEqual([1, 3, 4, 5, 12])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('appends values missing from custom order in source order', () => {
|
|
42
|
+
const sorted = sortByCategoryOrder(data, {
|
|
43
|
+
xAxis: {
|
|
44
|
+
type: 'categorical',
|
|
45
|
+
dataKey: 'category',
|
|
46
|
+
categoryOrderType: 'custom',
|
|
47
|
+
categoryOrder: ['C']
|
|
48
|
+
} as any
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
expect(sorted.map(row => row.category)).toEqual(['C', 'A', 'D', 'D', 'L'])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('merges configured category order with newly discovered values', () => {
|
|
55
|
+
expect(getOrderedCategoryValues(data, 'category', ['C', 'A'])).toEqual(['C', 'A', 'D', 'L'])
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -31,6 +31,17 @@ describe('countNumOfTicks', () => {
|
|
|
31
31
|
expect(result).toBe(6)
|
|
32
32
|
})
|
|
33
33
|
|
|
34
|
+
it('treats a null viewport override like the historical missing value', () => {
|
|
35
|
+
const result = countNumOfTicks({
|
|
36
|
+
...baseArgs,
|
|
37
|
+
axis: 'xAxis',
|
|
38
|
+
runtime: { xAxis: { numTicks: 6, viewportNumTicks: null } },
|
|
39
|
+
currentViewport: 'xs',
|
|
40
|
+
isHorizontal: false
|
|
41
|
+
})
|
|
42
|
+
expect(result).toBe(6)
|
|
43
|
+
})
|
|
44
|
+
|
|
34
45
|
it('falls back to numTicks when current viewport has no entry in viewportNumTicks', () => {
|
|
35
46
|
const result = countNumOfTicks({
|
|
36
47
|
...baseArgs,
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import defaults from '../../data/initial-state'
|
|
2
|
+
import { createNewChartConfig } from '../createNewChartConfig'
|
|
3
|
+
import { CURRENT_COVE_CONFIG_VERSION } from '@cdc/core/helpers/coveUpdateWorker'
|
|
4
|
+
|
|
5
|
+
describe('createNewChartConfig', () => {
|
|
6
|
+
it('deeply combines starter settings with current chart defaults', () => {
|
|
7
|
+
const starterConfig = {
|
|
8
|
+
type: 'chart',
|
|
9
|
+
visualizationType: 'Bar',
|
|
10
|
+
xAxis: { type: 'categorical', size: 100 },
|
|
11
|
+
filters: []
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const config = createNewChartConfig(starterConfig)
|
|
15
|
+
|
|
16
|
+
expect(config).toMatchObject({
|
|
17
|
+
type: 'chart',
|
|
18
|
+
visualizationType: 'Bar',
|
|
19
|
+
version: CURRENT_COVE_CONFIG_VERSION,
|
|
20
|
+
titleStyle: 'small',
|
|
21
|
+
barThickness: defaults.barThickness,
|
|
22
|
+
xAxis: {
|
|
23
|
+
type: 'categorical',
|
|
24
|
+
size: 100,
|
|
25
|
+
numTicks: 6,
|
|
26
|
+
viewportNumTicks: { xs: 4, xxs: 4 }
|
|
27
|
+
},
|
|
28
|
+
yAxis: {
|
|
29
|
+
titlePlacement: 'top',
|
|
30
|
+
labelPlacement: 'On Date/Category Axis',
|
|
31
|
+
numTicks: 4,
|
|
32
|
+
gridLines: true,
|
|
33
|
+
hideAxis: true,
|
|
34
|
+
hideTicks: true,
|
|
35
|
+
autoMaxStrategy: 'clean-top-tick'
|
|
36
|
+
},
|
|
37
|
+
legend: { position: 'top', unified: true, expandForecastConfidenceIntervals: true },
|
|
38
|
+
table: {
|
|
39
|
+
download: true,
|
|
40
|
+
expanded: false,
|
|
41
|
+
show: true,
|
|
42
|
+
showDataTableLink: false,
|
|
43
|
+
stickyFirstColumn: false
|
|
44
|
+
},
|
|
45
|
+
dataFormat: { commas: true }
|
|
46
|
+
})
|
|
47
|
+
expect(config.filters).toEqual([])
|
|
48
|
+
expect(starterConfig).toEqual({
|
|
49
|
+
type: 'chart',
|
|
50
|
+
visualizationType: 'Bar',
|
|
51
|
+
xAxis: { type: 'categorical', size: 100 },
|
|
52
|
+
filters: []
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it.each([
|
|
57
|
+
['Area Chart', { visualizationSubType: 'stacked' }],
|
|
58
|
+
['Forecasting', { xAxis: { type: 'date', dateParseFormat: '%Y-%m-%d', dateDisplayFormat: '%b. %-d %Y' } }],
|
|
59
|
+
['HeatMap', { yAxis: { type: 'categorical', titlePlacement: 'top' }, legend: { position: 'top' } }],
|
|
60
|
+
['Horizon Chart', { horizon: { numLayers: 4, mode: 'offset', bandGap: 15, bottomPadding: 15 } }],
|
|
61
|
+
['Box Plot', { yAxis: { labelPlacement: 'On Date/Category Axis' } }],
|
|
62
|
+
['Paired Bar', { orientation: 'horizontal' }],
|
|
63
|
+
['Deviation Bar', { orientation: 'horizontal' }],
|
|
64
|
+
['Bump Chart', { xAxis: { type: 'date-time' } }]
|
|
65
|
+
])('authors the %s stable type contract', (visualizationType, expected) => {
|
|
66
|
+
expect(createNewChartConfig({ type: 'chart', visualizationType })).toMatchObject(expected)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('uses horizontal axis visibility defaults while preserving explicit starter values', () => {
|
|
70
|
+
const config = createNewChartConfig({
|
|
71
|
+
type: 'chart',
|
|
72
|
+
visualizationType: 'Bar',
|
|
73
|
+
orientation: 'horizontal'
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
expect(config.xAxis).toMatchObject({ hideAxis: true, hideTicks: true })
|
|
77
|
+
expect(config.yAxis).toMatchObject({ hideAxis: false, hideTicks: false })
|
|
78
|
+
|
|
79
|
+
const explicitlyConfigured = createNewChartConfig({
|
|
80
|
+
type: 'chart',
|
|
81
|
+
visualizationType: 'Bar',
|
|
82
|
+
orientation: 'horizontal',
|
|
83
|
+
xAxis: { hideAxis: false, hideTicks: false },
|
|
84
|
+
yAxis: { hideAxis: true, hideTicks: true }
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
expect(explicitlyConfigured.xAxis).toMatchObject({ hideAxis: false, hideTicks: false })
|
|
88
|
+
expect(explicitlyConfigured.yAxis).toMatchObject({ hideAxis: true, hideTicks: true })
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('uses dashboard-specific table visibility', () => {
|
|
92
|
+
const config = createNewChartConfig({ type: 'chart', visualizationType: 'Bar' }, { isDashboard: true })
|
|
93
|
+
|
|
94
|
+
expect(config.table.show).toBe(false)
|
|
95
|
+
expect(config.table.download).toBe(false)
|
|
96
|
+
expect(config.table.showDataTableLink).toBe(false)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('preserves an explicit dashboard data table link opt-in', () => {
|
|
100
|
+
const config = createNewChartConfig({
|
|
101
|
+
type: 'chart',
|
|
102
|
+
visualizationType: 'Bar',
|
|
103
|
+
table: { showDataTableLink: true }
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
expect(config.table.showDataTableLink).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('preserves an explicit table download opt-out', () => {
|
|
110
|
+
const config = createNewChartConfig({
|
|
111
|
+
type: 'chart',
|
|
112
|
+
visualizationType: 'Bar',
|
|
113
|
+
table: { download: false }
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
expect(config.table.download).toBe(false)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('preserves an explicit unified legend opt-out', () => {
|
|
120
|
+
const config = createNewChartConfig({
|
|
121
|
+
type: 'chart',
|
|
122
|
+
visualizationType: 'Bar',
|
|
123
|
+
legend: { unified: false }
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
expect(config.legend.unified).toBe(false)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('enables expanded forecast confidence interval legends for new charts', () => {
|
|
130
|
+
const config = createNewChartConfig({ type: 'chart', visualizationType: 'Forecasting' })
|
|
131
|
+
|
|
132
|
+
expect(config.legend.expandForecastConfidenceIntervals).toBe(true)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('preserves an explicit expanded forecast confidence interval legend opt-out', () => {
|
|
136
|
+
const config = createNewChartConfig({
|
|
137
|
+
type: 'chart',
|
|
138
|
+
visualizationType: 'Forecasting',
|
|
139
|
+
legend: { expandForecastConfidenceIntervals: false }
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
expect(config.legend.expandForecastConfidenceIntervals).toBe(false)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('uses compact label gaps and automatic ticks for a new HeatMap', () => {
|
|
146
|
+
const config = createNewChartConfig({ type: 'chart', visualizationType: 'HeatMap' })
|
|
147
|
+
|
|
148
|
+
expect(config.heatmap).toMatchObject({
|
|
149
|
+
rowLabelGap: 0,
|
|
150
|
+
columnLabelGap: 20,
|
|
151
|
+
horizontalScroll: false,
|
|
152
|
+
minColumnWidth: 44
|
|
153
|
+
})
|
|
154
|
+
expect(config.xAxis.manual).toBe(false)
|
|
155
|
+
expect(config.xAxis.numTicks).toBeUndefined()
|
|
156
|
+
expect(config.xAxis.viewportNumTicks).toEqual({})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it.each([
|
|
160
|
+
['Bar', 'sequential_blue', false],
|
|
161
|
+
['Line', 'qualitative_standard', false],
|
|
162
|
+
['Horizon Chart', 'sequential_blue', false],
|
|
163
|
+
['HeatMap', 'sequential_blue', false],
|
|
164
|
+
['Sankey', 'sequential_bluereverse', true]
|
|
165
|
+
])('uses the new-chart palette for %s charts', (visualizationType, name, isReversed) => {
|
|
166
|
+
const config = createNewChartConfig({ type: 'chart', visualizationType })
|
|
167
|
+
|
|
168
|
+
expect(config.general.palette).toMatchObject({ name, isReversed, version: '2.1' })
|
|
169
|
+
expect(defaults.general.palette.version).toBe('2.1')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('preserves an explicit palette instead of replacing it with the chart-type default', () => {
|
|
173
|
+
const config = createNewChartConfig({
|
|
174
|
+
type: 'chart',
|
|
175
|
+
visualizationType: 'Line',
|
|
176
|
+
general: { palette: { name: 'qualitative_bold', isReversed: true, version: '2.1' } }
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
expect(config.general.palette).toMatchObject({ name: 'qualitative_bold', isReversed: true, version: '2.1' })
|
|
180
|
+
expect(defaults.general.palette.name).toBe('sequential_blue')
|
|
181
|
+
})
|
|
182
|
+
})
|