@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
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react'
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { createMockChartContext, createMockConfig } from '../../../LinearChart/tests/mockConfigContext'
|
|
4
|
+
import { useBarChart } from '../useBarChart'
|
|
5
|
+
|
|
6
|
+
const baseConfig = createMockConfig()
|
|
7
|
+
|
|
8
|
+
const makeConfigContext = (countColumnOverrides: Record<string, unknown> = {}) => {
|
|
9
|
+
const config = createMockConfig({
|
|
10
|
+
visualizationType: 'Bar',
|
|
11
|
+
orientation: 'horizontal',
|
|
12
|
+
barHeight: 25,
|
|
13
|
+
xAxis: {
|
|
14
|
+
...baseConfig.xAxis,
|
|
15
|
+
type: 'categorical',
|
|
16
|
+
dataKey: 'Cause of death'
|
|
17
|
+
},
|
|
18
|
+
yAxis: {
|
|
19
|
+
...baseConfig.yAxis,
|
|
20
|
+
labelPlacement: 'Below Bar'
|
|
21
|
+
},
|
|
22
|
+
series: [{ dataKey: 'Percentage', type: 'Bar', axis: 'Left', tooltip: true }] as any,
|
|
23
|
+
columns: {
|
|
24
|
+
Count: {
|
|
25
|
+
name: 'Count',
|
|
26
|
+
label: 'Count',
|
|
27
|
+
prefix: '',
|
|
28
|
+
suffix: '',
|
|
29
|
+
roundToPlace: 0,
|
|
30
|
+
commas: true,
|
|
31
|
+
dataTable: true,
|
|
32
|
+
tooltips: true,
|
|
33
|
+
...countColumnOverrides
|
|
34
|
+
}
|
|
35
|
+
} as any,
|
|
36
|
+
dataFormat: {
|
|
37
|
+
abbreviated: false,
|
|
38
|
+
bottomAbbreviated: false,
|
|
39
|
+
bottomPrefix: '',
|
|
40
|
+
bottomRoundTo: 0,
|
|
41
|
+
bottomSuffix: '',
|
|
42
|
+
bottomCommas: false,
|
|
43
|
+
commas: false,
|
|
44
|
+
prefix: '',
|
|
45
|
+
preserveOriginalDecimals: false,
|
|
46
|
+
rightPrefix: '',
|
|
47
|
+
rightRoundTo: 0,
|
|
48
|
+
rightSuffix: '',
|
|
49
|
+
roundTo: 1,
|
|
50
|
+
suffix: '%'
|
|
51
|
+
} as any,
|
|
52
|
+
runtime: {
|
|
53
|
+
...baseConfig.runtime,
|
|
54
|
+
xAxis: {
|
|
55
|
+
...baseConfig.runtime.xAxis,
|
|
56
|
+
type: 'categorical',
|
|
57
|
+
dataKey: 'Cause of death'
|
|
58
|
+
},
|
|
59
|
+
yAxis: {
|
|
60
|
+
...baseConfig.runtime.yAxis,
|
|
61
|
+
dataKey: 'Percentage'
|
|
62
|
+
},
|
|
63
|
+
originalXAxis: {
|
|
64
|
+
...baseConfig.runtime.originalXAxis,
|
|
65
|
+
dataKey: 'Cause of death'
|
|
66
|
+
},
|
|
67
|
+
series: [{ dataKey: 'Percentage', type: 'Bar', axis: 'Left', tooltip: true }],
|
|
68
|
+
seriesKeys: ['Percentage'],
|
|
69
|
+
seriesLabels: { Percentage: 'Percentage' },
|
|
70
|
+
seriesLabelsAll: ['Percentage']
|
|
71
|
+
} as any
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return createMockChartContext(config, {
|
|
75
|
+
tableData: [
|
|
76
|
+
{
|
|
77
|
+
'Cause of death': 'Cardiovascular conditions',
|
|
78
|
+
Percentage: 22,
|
|
79
|
+
Count: 146
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
updateConfig: vi.fn()
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
describe('useBarChart', () => {
|
|
87
|
+
it('adds raw percent decoration without replacing the formatted tooltip value', () => {
|
|
88
|
+
const configContext = makeConfigContext()
|
|
89
|
+
configContext.tableData[0].Percentage = '22.500%'
|
|
90
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
91
|
+
|
|
92
|
+
expect(result.current.formatTooltipValue('Percentage', 'Cardiovascular conditions', '22.5')).toBe('22.5%')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('preserves a raw trailing percent sign for the matching dynamic category', () => {
|
|
96
|
+
const configContext = makeConfigContext()
|
|
97
|
+
configContext.config.series = [
|
|
98
|
+
{
|
|
99
|
+
dataKey: 'Data_Value',
|
|
100
|
+
dynamicCategory: 'Age group',
|
|
101
|
+
type: 'Bar',
|
|
102
|
+
axis: 'Left',
|
|
103
|
+
tooltip: true
|
|
104
|
+
}
|
|
105
|
+
] as any
|
|
106
|
+
configContext.tableData = [
|
|
107
|
+
{
|
|
108
|
+
'Cause of death': 'Cardiovascular conditions',
|
|
109
|
+
'Age group': '65 years or older',
|
|
110
|
+
Data_Value: '56.4%'
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
114
|
+
|
|
115
|
+
expect(result.current.formatTooltipValue('65 years or older', 'Cardiovascular conditions', '56.4')).toBe('56.4%')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('matches numeric dynamic-category values to their rendered string series keys', () => {
|
|
119
|
+
const configContext = makeConfigContext()
|
|
120
|
+
configContext.config.series = [
|
|
121
|
+
{
|
|
122
|
+
dataKey: 'Data_Value',
|
|
123
|
+
dynamicCategory: 'Age group',
|
|
124
|
+
type: 'Bar',
|
|
125
|
+
axis: 'Left',
|
|
126
|
+
tooltip: true
|
|
127
|
+
}
|
|
128
|
+
] as any
|
|
129
|
+
configContext.tableData = [
|
|
130
|
+
{
|
|
131
|
+
'Cause of death': 'Cardiovascular conditions',
|
|
132
|
+
'Age group': 65,
|
|
133
|
+
Data_Value: '56.4%'
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
137
|
+
|
|
138
|
+
expect(result.current.formatTooltipValue('65', 'Cardiovascular conditions', '56.4')).toBe('56.4%')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('uses the indexed raw row when static categories are duplicated', () => {
|
|
142
|
+
const configContext = makeConfigContext()
|
|
143
|
+
configContext.tableData = [
|
|
144
|
+
{ 'Cause of death': 'Repeated category', Percentage: '22.5%', Count: 146 },
|
|
145
|
+
{ 'Cause of death': 'Repeated category', Percentage: '30%', Count: 150 }
|
|
146
|
+
]
|
|
147
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
148
|
+
|
|
149
|
+
expect(result.current.formatTooltipValue('Percentage', 'Repeated category', '30.0', 1)).toBe('30.0%')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('uses the brush-aligned raw row when a duplicate category subset shifts the rendered index', () => {
|
|
153
|
+
const configContext = makeConfigContext()
|
|
154
|
+
configContext.tableData = [
|
|
155
|
+
{ 'Cause of death': 'Repeated category', Percentage: 22.5, Count: 146 },
|
|
156
|
+
{ 'Cause of death': 'Repeated category', Percentage: '30%', Count: 150 }
|
|
157
|
+
]
|
|
158
|
+
configContext.brushData = [configContext.tableData[1]]
|
|
159
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
160
|
+
|
|
161
|
+
expect(result.current.formatTooltipValue('Percentage', 'Repeated category', '30.0', 0)).toBe('30.0%')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('uses the formatted tooltip value when the raw value is numeric', () => {
|
|
165
|
+
const configContext = makeConfigContext()
|
|
166
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
167
|
+
|
|
168
|
+
expect(result.current.formatTooltipValue('Percentage', 'Cardiovascular conditions', '22.0')).toBe('22.0')
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('uses the formatted tooltip value when no raw row matches', () => {
|
|
172
|
+
const configContext = makeConfigContext()
|
|
173
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
174
|
+
|
|
175
|
+
expect(result.current.formatTooltipValue('Percentage', 'Missing category', 'N/A')).toBe('N/A')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('does not mutate config when horizontal bar label placement is missing', () => {
|
|
179
|
+
const configContext = makeConfigContext()
|
|
180
|
+
delete configContext.config.yAxis.labelPlacement
|
|
181
|
+
|
|
182
|
+
renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
183
|
+
|
|
184
|
+
expect(configContext.updateConfig).not.toHaveBeenCalled()
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('does not apply the left-axis suffix to an additional single-series tooltip column', () => {
|
|
188
|
+
const configContext = makeConfigContext()
|
|
189
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
190
|
+
|
|
191
|
+
expect(result.current.getAdditionalColumn('Percentage', 'Cardiovascular conditions')).toBe('Count : 146 <br/>')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('applies an additional column suffix when one is configured', () => {
|
|
195
|
+
const configContext = makeConfigContext({ suffix: ' deaths' })
|
|
196
|
+
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
197
|
+
|
|
198
|
+
expect(result.current.getAdditionalColumn('Percentage', 'Cardiovascular conditions')).toBe(
|
|
199
|
+
'Count : 146 deaths <br/>'
|
|
200
|
+
)
|
|
201
|
+
})
|
|
202
|
+
})
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useState } from 'react'
|
|
1
|
+
import React, { useContext, useEffect, useMemo, useState } from 'react'
|
|
2
2
|
import { ChartDispatchContext } from '../../../ConfigContext'
|
|
3
3
|
import { formatNumber as formatColNumber } from '@cdc/core/helpers/cove/number'
|
|
4
4
|
import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
|
|
5
|
-
import { getPaletteColors } from '@cdc/core/helpers/palettes/utils'
|
|
6
5
|
import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
|
|
7
6
|
import { getVizSubType, getVizTitle } from '@cdc/core/helpers/metrics/utils'
|
|
8
7
|
import { isMobileFontViewport } from '@cdc/core/helpers/viewports'
|
|
9
|
-
import { getSeriesOwnedColumnNames } from '../../../helpers/seriesColumnSettings'
|
|
8
|
+
import { getAdditionalColumnFormattingParams, getSeriesOwnedColumnNames } from '../../../helpers/seriesColumnSettings'
|
|
9
|
+
import { getColorCodeCategoryColorMap } from '../../../helpers/getColorCodeCategoryColorMap'
|
|
10
10
|
|
|
11
11
|
export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, configContext) => {
|
|
12
12
|
const {
|
|
13
13
|
config,
|
|
14
|
+
brushData,
|
|
14
15
|
colorPalettes,
|
|
15
16
|
tableData,
|
|
16
17
|
updateConfig,
|
|
@@ -53,18 +54,37 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
53
54
|
? seriesHighlight
|
|
54
55
|
: config.runtime.barSeriesKeys || config.runtime.seriesKeys
|
|
55
56
|
const seriesOwnedColumnNames = getSeriesOwnedColumnNames(config.series)
|
|
57
|
+
const dynamicSeries = config.series.find(series => series.dynamicCategory)
|
|
58
|
+
const originalXAxisDataKey = config.runtime.originalXAxis.dataKey
|
|
59
|
+
const sourceDataRows = Array.isArray(brushData) && brushData.length > 0 ? brushData : tableData
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
// Index source rows once so rendered bars can recover display-only formatting without rescanning the data.
|
|
62
|
+
const sourceRowsByXAxisAndSeries = useMemo(() => {
|
|
63
|
+
const rowsByXAxis = new Map<string, Map<string, Record<string, unknown>>>()
|
|
64
|
+
|
|
65
|
+
sourceDataRows?.forEach(row => {
|
|
66
|
+
const xAxisValue = String(row?.[originalXAxisDataKey])
|
|
67
|
+
const rowsBySeries = rowsByXAxis.get(xAxisValue) ?? new Map<string, Record<string, unknown>>()
|
|
68
|
+
|
|
69
|
+
config.series.forEach(series => {
|
|
70
|
+
const seriesKey = series.dynamicCategory ? row?.[series.dynamicCategory] : series.dataKey
|
|
71
|
+
if (seriesKey === undefined || seriesKey === null) return
|
|
72
|
+
rowsBySeries.set(String(seriesKey), row)
|
|
65
73
|
})
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
|
|
75
|
+
rowsByXAxis.set(xAxisValue, rowsBySeries)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
return rowsByXAxis
|
|
79
|
+
}, [sourceDataRows, config.series, originalXAxisDataKey])
|
|
80
|
+
|
|
81
|
+
const findSourceDataRow = (seriesKey: string, xAxisValue: unknown, rowIndex?: number) => {
|
|
82
|
+
// Static charts can repeat x-axis values, so prefer the rendered row index when it still matches.
|
|
83
|
+
const indexedRow = !dynamicSeries && Number.isInteger(rowIndex) ? sourceDataRows?.[rowIndex] : undefined
|
|
84
|
+
if (indexedRow && String(indexedRow[originalXAxisDataKey]) === String(xAxisValue)) return indexedRow
|
|
85
|
+
|
|
86
|
+
return sourceRowsByXAxisAndSeries.get(String(xAxisValue))?.get(String(seriesKey))
|
|
87
|
+
}
|
|
68
88
|
|
|
69
89
|
useEffect(() => {
|
|
70
90
|
if (config.isLollipopChart === false && config.barHeight < 25) {
|
|
@@ -72,15 +92,6 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
72
92
|
}
|
|
73
93
|
}, [config.isLollipopChart]) // eslint-disable-line
|
|
74
94
|
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
if (config.visualizationSubType === 'horizontal') {
|
|
77
|
-
updateConfig({
|
|
78
|
-
...config,
|
|
79
|
-
orientation: 'horizontal'
|
|
80
|
-
})
|
|
81
|
-
}
|
|
82
|
-
}, []) // eslint-disable-line
|
|
83
|
-
|
|
84
95
|
useEffect(() => {
|
|
85
96
|
if (config.barStyle === 'lollipop' && !config.isLollipopChart) {
|
|
86
97
|
updateConfig({ ...config, isLollipopChart: true })
|
|
@@ -111,34 +122,16 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
111
122
|
return style
|
|
112
123
|
}
|
|
113
124
|
|
|
114
|
-
const
|
|
115
|
-
if (!config.legend.colorCode && config.series.length > 1) {
|
|
116
|
-
return currentBarColor
|
|
117
|
-
}
|
|
118
|
-
const palettesArr = getPaletteColors(config, colorPalettes)
|
|
119
|
-
const values = tableData.map(d => {
|
|
120
|
-
return d[config.legend.colorCode]
|
|
121
|
-
})
|
|
122
|
-
// Map to hold unique values and their colors
|
|
123
|
-
let colorMap = new Map()
|
|
124
|
-
// Resultant array to hold colors to the values
|
|
125
|
-
let palette = []
|
|
125
|
+
const colorCodeCategoryColors = getColorCodeCategoryColorMap(config, tableData, colorPalettes)
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
colorMap.set(values[i], palettesArr[colorMap.size % palettesArr.length])
|
|
131
|
-
}
|
|
132
|
-
// push the color to the result array
|
|
133
|
-
palette.push(colorMap.get(values[i]))
|
|
134
|
-
}
|
|
127
|
+
const assignColorsToValues = (barIndex, currentBarColor) => {
|
|
128
|
+
const colorCode = config.legend.colorCode
|
|
129
|
+
if (!colorCode) return currentBarColor
|
|
135
130
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const barColor = palette[barIndex]
|
|
141
|
-
return barColor
|
|
131
|
+
const rowIndex = tableData.length ? barIndex % tableData.length : -1
|
|
132
|
+
const category = tableData[rowIndex]?.[colorCode]
|
|
133
|
+
|
|
134
|
+
return colorCodeCategoryColors.get(category) ?? currentBarColor
|
|
142
135
|
}
|
|
143
136
|
|
|
144
137
|
const getHighlightedBarColorByValue = value => {
|
|
@@ -173,22 +166,12 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
173
166
|
const columns = config.columns
|
|
174
167
|
const columnsWithTooltips = []
|
|
175
168
|
let additionalTooltipItems = ''
|
|
176
|
-
const
|
|
177
|
-
const closestVal =
|
|
178
|
-
tableData.find(d => {
|
|
179
|
-
const dynamicCategoryMatch = dynamicCategorySeries ? d[dynamicCategorySeries.dynamicCategory] === series : true
|
|
180
|
-
return d[config.xAxis.dataKey] === xAxisDataValue && dynamicCategoryMatch
|
|
181
|
-
}) || {}
|
|
169
|
+
const closestVal = findSourceDataRow(series, xAxisDataValue) || {}
|
|
182
170
|
Object.keys(columns).forEach(colKeys => {
|
|
183
171
|
const colConfig = config.columns[colKeys]
|
|
184
172
|
if (seriesOwnedColumnNames.includes(colConfig.name || colKeys)) return
|
|
185
173
|
if (series && colConfig.series && colConfig.series !== series && !colConfig.tooltips) return
|
|
186
|
-
const formattingParams =
|
|
187
|
-
addColPrefix: config.columns[colKeys].prefix,
|
|
188
|
-
addColSuffix: config.columns[colKeys].suffix,
|
|
189
|
-
addColRoundTo: config.columns[colKeys].roundToPlace ? config.columns[colKeys].roundToPlace : '',
|
|
190
|
-
addColCommas: config.columns[colKeys].commas
|
|
191
|
-
}
|
|
174
|
+
const formattingParams = getAdditionalColumnFormattingParams(config.columns[colKeys])
|
|
192
175
|
|
|
193
176
|
const formattedValue = formatColNumber(
|
|
194
177
|
closestVal[config.columns[colKeys].name],
|
|
@@ -207,6 +190,22 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
207
190
|
return additionalTooltipItems
|
|
208
191
|
}
|
|
209
192
|
|
|
193
|
+
const formatTooltipValue = (
|
|
194
|
+
seriesKey: string,
|
|
195
|
+
xAxisValue: unknown,
|
|
196
|
+
displayValue: string | number,
|
|
197
|
+
rowIndex?: number
|
|
198
|
+
): string | number => {
|
|
199
|
+
const rawRow = findSourceDataRow(seriesKey, xAxisValue, rowIndex)
|
|
200
|
+
const staticSeries = config.series.find(series => !series.dynamicCategory && series.dataKey === seriesKey)
|
|
201
|
+
const rawValueKey = staticSeries?.dataKey ?? dynamicSeries?.dataKey ?? seriesKey
|
|
202
|
+
const rawValue = rawRow?.[rawValueKey]
|
|
203
|
+
const formattedText = String(displayValue)
|
|
204
|
+
const sourceUsesPercentage = typeof rawValue === 'string' && rawValue.trim().endsWith('%')
|
|
205
|
+
|
|
206
|
+
return sourceUsesPercentage && formattedText && !formattedText.endsWith('%') ? `${formattedText}%` : displayValue
|
|
207
|
+
}
|
|
208
|
+
|
|
210
209
|
const onMouseOverBar = (categoryValue, barKey, event, data, barValue) => {
|
|
211
210
|
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey) {
|
|
212
211
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [barKey] })
|
|
@@ -256,6 +255,7 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
256
255
|
barStackedSeriesKeys,
|
|
257
256
|
hasMultipleSeries,
|
|
258
257
|
labelFontSize,
|
|
258
|
+
formatTooltipValue,
|
|
259
259
|
applyRadius,
|
|
260
260
|
assignColorsToValues,
|
|
261
261
|
getHighlightedBarColorByValue,
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { act, fireEvent, render, screen } from '@testing-library/react'
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
4
|
+
import ConfigContext from '../../ConfigContext'
|
|
5
|
+
import { createMockChartContext, createMockConfig } from '../LinearChart/tests/mockConfigContext'
|
|
6
|
+
import BarChartRace from './BarChartRace'
|
|
7
|
+
import { getBarRaceEligibility } from './helpers'
|
|
8
|
+
|
|
9
|
+
const data = [
|
|
10
|
+
{ Year: '2020', Place: 'Alpha', Value: 10 },
|
|
11
|
+
{ Year: '2020', Place: 'Beta', Value: 20 },
|
|
12
|
+
{ Year: '2021', Place: 'Alpha', Value: 30 },
|
|
13
|
+
{ Year: '2021', Place: 'Beta', Value: 15 }
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
type RaceOptions = {
|
|
17
|
+
displayNumbersOnBar?: boolean
|
|
18
|
+
formatNumber?: ReturnType<typeof vi.fn>
|
|
19
|
+
labelPlacement?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const getRaceContext = (rows = data, options: RaceOptions = {}) => {
|
|
23
|
+
const baseConfig = createMockConfig()
|
|
24
|
+
const yAxis = { ...baseConfig.yAxis }
|
|
25
|
+
delete yAxis.labelPlacement
|
|
26
|
+
if (options.labelPlacement) yAxis.labelPlacement = options.labelPlacement
|
|
27
|
+
if (options.displayNumbersOnBar !== undefined) yAxis.displayNumbersOnBar = options.displayNumbersOnBar
|
|
28
|
+
const config = createMockConfig({
|
|
29
|
+
visualizationType: 'Bar',
|
|
30
|
+
visualizationSubType: 'racing',
|
|
31
|
+
orientation: 'horizontal',
|
|
32
|
+
barStyle: 'flat',
|
|
33
|
+
isLollipopChart: false,
|
|
34
|
+
barRace: { maxBars: 2, secondsPerFrame: 0.5 },
|
|
35
|
+
xAxis: { ...baseConfig.xAxis, type: 'categorical', dataKey: 'Year', label: 'Year' },
|
|
36
|
+
yAxis,
|
|
37
|
+
series: [{ dataKey: 'Value', dynamicCategory: 'Place', axis: 'left', type: 'Bar' }] as any,
|
|
38
|
+
columns: { Value: { name: 'Value', label: 'Value', prefix: '$', roundToPlace: 0 } } as any,
|
|
39
|
+
runtime: { ...baseConfig.runtime, seriesKeys: ['Alpha', 'Beta'], seriesLabelsAll: ['Alpha', 'Beta'] }
|
|
40
|
+
})
|
|
41
|
+
return createMockChartContext(config, {
|
|
42
|
+
transformedData: rows,
|
|
43
|
+
colorScale: category => (category === 'Alpha' ? '#005ea8' : '#712177'),
|
|
44
|
+
formatNumber:
|
|
45
|
+
options.formatNumber ?? ((value, _axis, _abbreviated, prefix = '', suffix = '') => `${prefix}${value}${suffix}`)
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const renderRace = (rows = data, options: RaceOptions = {}) => {
|
|
50
|
+
const context = getRaceContext(rows, options)
|
|
51
|
+
const race = getBarRaceEligibility(context.config, rows)
|
|
52
|
+
|
|
53
|
+
return render(
|
|
54
|
+
<ConfigContext.Provider value={context}>
|
|
55
|
+
<BarChartRace parentWidth={700} race={race} />
|
|
56
|
+
</ConfigContext.Provider>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe('BarChartRace', () => {
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
vi.useFakeTimers()
|
|
63
|
+
vi.stubGlobal('matchMedia', () => ({
|
|
64
|
+
matches: true,
|
|
65
|
+
addEventListener: vi.fn(),
|
|
66
|
+
removeEventListener: vi.fn()
|
|
67
|
+
}))
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
vi.useRealTimers()
|
|
72
|
+
vi.unstubAllGlobals()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('uses the on-axis label layout when label placement is missing', () => {
|
|
76
|
+
const { container } = renderRace()
|
|
77
|
+
|
|
78
|
+
expect(container.querySelector('.bar-chart-race')).toHaveClass('bar-chart-race--labels-on-axis')
|
|
79
|
+
expect(container.querySelector('.bar-chart-race__plot')).toHaveStyle({ height: '96px' })
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('uses the on-axis label layout when label placement is explicit', () => {
|
|
83
|
+
const { container } = renderRace(data, { labelPlacement: 'On Date/Category Axis' })
|
|
84
|
+
|
|
85
|
+
expect(container.querySelector('.bar-chart-race')).toHaveClass('bar-chart-race--labels-on-axis')
|
|
86
|
+
expect(container.querySelector('.bar-chart-race')).not.toHaveClass('bar-chart-race--labels-below-bar')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('moves labels below bars and adds row spacing for the below-bar layout', () => {
|
|
90
|
+
const { container } = renderRace(data, { labelPlacement: 'Below Bar' })
|
|
91
|
+
|
|
92
|
+
expect(container.querySelector('.bar-chart-race')).toHaveClass('bar-chart-race--labels-below-bar')
|
|
93
|
+
expect(container.querySelector('.bar-chart-race__plot')).toHaveStyle({ height: '128px' })
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('keeps value labels after bars when display numbers on bar is disabled', () => {
|
|
97
|
+
const { container } = renderRace(data, { displayNumbersOnBar: false })
|
|
98
|
+
|
|
99
|
+
expect(container.querySelectorAll('.bar-chart-race__value')).toHaveLength(2)
|
|
100
|
+
expect(container.querySelectorAll('.bar-chart-race__value--after')).toHaveLength(2)
|
|
101
|
+
expect(container.querySelector('.bar-chart-race__value--inside')).not.toBeInTheDocument()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('places displayed values inside fitting bars and after bars that are too short', () => {
|
|
105
|
+
const rows = [
|
|
106
|
+
{ Year: '2020', Place: 'Short', Value: 1 },
|
|
107
|
+
{ Year: '2020', Place: 'Long', Value: 100 },
|
|
108
|
+
{ Year: '2021', Place: 'Short', Value: 2 },
|
|
109
|
+
{ Year: '2021', Place: 'Long', Value: 90 }
|
|
110
|
+
]
|
|
111
|
+
const { container } = renderRace(rows, { displayNumbersOnBar: true })
|
|
112
|
+
|
|
113
|
+
expect(container.querySelector('[data-category="Long"] .bar-chart-race__value')).toHaveClass(
|
|
114
|
+
'bar-chart-race__value--inside'
|
|
115
|
+
)
|
|
116
|
+
expect(container.querySelector('[data-category="Short"] .bar-chart-race__value')).toHaveClass(
|
|
117
|
+
'bar-chart-race__value--after'
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('starts paused, advances, pauses, and replays from the first frame', () => {
|
|
122
|
+
const { container } = renderRace()
|
|
123
|
+
|
|
124
|
+
const frame = container.querySelector('.bar-chart-race__frame')
|
|
125
|
+
const frameAxis = screen.getByRole('group', { name: 'Year axis' })
|
|
126
|
+
expect(frame).toHaveTextContent('2020')
|
|
127
|
+
expect(frameAxis).toHaveTextContent('2020')
|
|
128
|
+
expect(frameAxis).toHaveTextContent('2021')
|
|
129
|
+
expect(frameAxis.querySelector('[aria-current="step"]')).toHaveTextContent('2020')
|
|
130
|
+
const playButton = screen.getByRole('button', { name: 'Play' })
|
|
131
|
+
expect(container.querySelector('.bar-chart-race__header')?.firstElementChild).toBe(playButton)
|
|
132
|
+
expect(playButton.nextElementSibling).toBe(frame)
|
|
133
|
+
expect(playButton.querySelector('[data-icon="play"]')).toBeInTheDocument()
|
|
134
|
+
fireEvent.click(playButton)
|
|
135
|
+
expect(screen.getByRole('button', { name: 'Pause' }).querySelector('[data-icon="pause"]')).toBeInTheDocument()
|
|
136
|
+
act(() => vi.advanceTimersByTime(500))
|
|
137
|
+
expect(frame).toHaveTextContent('2021')
|
|
138
|
+
expect(frameAxis.querySelector('[aria-current="step"]')).toHaveTextContent('2021')
|
|
139
|
+
expect(screen.getByRole('button', { name: 'Replay' }).querySelector('[data-icon="replay"]')).toBeInTheDocument()
|
|
140
|
+
|
|
141
|
+
fireEvent.click(screen.getByRole('button', { name: 'Replay' }))
|
|
142
|
+
expect(frame).toHaveTextContent('2020')
|
|
143
|
+
expect(screen.getByRole('button', { name: 'Pause' })).toBeInTheDocument()
|
|
144
|
+
fireEvent.click(screen.getByRole('button', { name: 'Pause' }))
|
|
145
|
+
act(() => vi.advanceTimersByTime(500))
|
|
146
|
+
expect(frame).toHaveTextContent('2020')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('uses stable category colors, column formatting, and the standard chart tooltip markup', () => {
|
|
150
|
+
const { container } = renderRace(data, { displayNumbersOnBar: true })
|
|
151
|
+
const alphaRow = container.querySelector('[data-category="Alpha"]') as HTMLElement
|
|
152
|
+
|
|
153
|
+
expect(alphaRow.querySelector('.bar-chart-race__bar')).toHaveStyle({ backgroundColor: '#005ea8' })
|
|
154
|
+
expect(alphaRow).toHaveTextContent('$10')
|
|
155
|
+
expect(alphaRow.dataset.tooltipHtml).toContain('class="tooltip-heading">2020')
|
|
156
|
+
expect(alphaRow.dataset.tooltipHtml).toContain('Alpha: $10')
|
|
157
|
+
expect(alphaRow.dataset.tooltipId).toContain('cdc-open-viz-tooltip-')
|
|
158
|
+
expect(container.querySelector('.bar-chart-race__tooltip')).not.toBeInTheDocument()
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('normalizes authored series axis names before applying value formatting', () => {
|
|
162
|
+
const formatNumber = vi.fn((value, _axis, _abbreviated, prefix = '', suffix = '') => `${prefix}${value}${suffix}`)
|
|
163
|
+
const { container } = renderRace(data, { formatNumber })
|
|
164
|
+
|
|
165
|
+
expect(container.querySelector('[data-category="Alpha"] .bar-chart-race__value')).toHaveTextContent('$10')
|
|
166
|
+
expect(formatNumber).toHaveBeenCalledWith(10, 'left', false, '$', undefined, 0)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('clears playback timers when unmounted', () => {
|
|
170
|
+
const clearIntervalSpy = vi.spyOn(window, 'clearInterval')
|
|
171
|
+
const view = renderRace()
|
|
172
|
+
fireEvent.click(screen.getByRole('button', { name: 'Play' }))
|
|
173
|
+
view.unmount()
|
|
174
|
+
expect(clearIntervalSpy).toHaveBeenCalled()
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('resets to the first paused frame when visible data changes', () => {
|
|
178
|
+
const context = getRaceContext()
|
|
179
|
+
const race = getBarRaceEligibility(context.config, data)
|
|
180
|
+
const view = render(
|
|
181
|
+
<ConfigContext.Provider value={context}>
|
|
182
|
+
<BarChartRace parentWidth={700} race={race} />
|
|
183
|
+
</ConfigContext.Provider>
|
|
184
|
+
)
|
|
185
|
+
fireEvent.click(screen.getByRole('button', { name: 'Play' }))
|
|
186
|
+
act(() => vi.advanceTimersByTime(500))
|
|
187
|
+
expect(view.container.querySelector('.bar-chart-race__frame')).toHaveTextContent('2021')
|
|
188
|
+
|
|
189
|
+
const nextRows = [...data, { Year: '2022', Place: 'Alpha', Value: 50 }, { Year: '2022', Place: 'Beta', Value: 45 }]
|
|
190
|
+
view.rerender(
|
|
191
|
+
<ConfigContext.Provider value={{ ...context, transformedData: nextRows }}>
|
|
192
|
+
<BarChartRace parentWidth={700} race={getBarRaceEligibility(context.config, nextRows)} />
|
|
193
|
+
</ConfigContext.Provider>
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
expect(view.container.querySelector('.bar-chart-race__frame')).toHaveTextContent('2020')
|
|
197
|
+
expect(screen.getByRole('button', { name: 'Play' })).toBeInTheDocument()
|
|
198
|
+
})
|
|
199
|
+
})
|