@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
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { getChartPatternId } from '../../../../helpers/getChartPatternId'
|
|
2
|
+
import {
|
|
3
|
+
createPatternColumnConfig,
|
|
4
|
+
ensurePatternColumnConfig,
|
|
5
|
+
findColumnConfigKey,
|
|
6
|
+
getPortionPatternBoundaryGeometry,
|
|
7
|
+
getPortionPatternGeometry,
|
|
8
|
+
getPortionPatternOverlay,
|
|
9
|
+
getPortionPatternRenderData,
|
|
10
|
+
isPortionPatternSupported,
|
|
11
|
+
removeUnusedPatternColumnConfig
|
|
12
|
+
} from '../portionPattern'
|
|
13
|
+
|
|
14
|
+
describe('portion pattern column configurations', () => {
|
|
15
|
+
it('creates only the standard tooltip and data-table settings needed by a pattern column', () => {
|
|
16
|
+
expect(createPatternColumnConfig('new_cases')).toEqual({
|
|
17
|
+
name: 'new_cases',
|
|
18
|
+
label: 'new_cases',
|
|
19
|
+
prefix: '',
|
|
20
|
+
suffix: '',
|
|
21
|
+
roundToPlace: 0,
|
|
22
|
+
commas: true,
|
|
23
|
+
dataTable: true,
|
|
24
|
+
tooltips: true
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('creates a configuration only when the selected column is not already configured', () => {
|
|
29
|
+
const columns = {
|
|
30
|
+
existing: {
|
|
31
|
+
name: 'new_cases',
|
|
32
|
+
label: 'New cases',
|
|
33
|
+
commas: false,
|
|
34
|
+
dataTable: false,
|
|
35
|
+
tooltips: false
|
|
36
|
+
}
|
|
37
|
+
} as any
|
|
38
|
+
|
|
39
|
+
expect(ensurePatternColumnConfig(columns, 'new_cases')).toBe(columns)
|
|
40
|
+
expect(ensurePatternColumnConfig(columns, 'previous_cases')).toEqual({
|
|
41
|
+
...columns,
|
|
42
|
+
previous_cases: createPatternColumnConfig('previous_cases')
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('finds configurations stored under either the column name or an alias key', () => {
|
|
47
|
+
expect(findColumnConfigKey({ new_cases: { label: 'New cases' } } as any, 'new_cases')).toBe('new_cases')
|
|
48
|
+
expect(
|
|
49
|
+
findColumnConfigKey({ additionalColumn1: { name: 'new_cases', label: 'New cases' } } as any, 'new_cases')
|
|
50
|
+
).toBe('additionalColumn1')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('removes an obsolete pattern column configuration', () => {
|
|
54
|
+
const columns = {
|
|
55
|
+
additionalColumn1: { name: 'new_cases', label: 'New cases' },
|
|
56
|
+
source: { name: 'source', label: 'Source' }
|
|
57
|
+
} as any
|
|
58
|
+
|
|
59
|
+
expect(
|
|
60
|
+
removeUnusedPatternColumnConfig({
|
|
61
|
+
columns,
|
|
62
|
+
columnName: 'new_cases',
|
|
63
|
+
patterns: {}
|
|
64
|
+
})
|
|
65
|
+
).toEqual({ source: columns.source })
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('preserves a column still used by another portion pattern', () => {
|
|
69
|
+
const columns = { new_cases: { name: 'new_cases', label: 'New cases' } } as any
|
|
70
|
+
|
|
71
|
+
expect(
|
|
72
|
+
removeUnusedPatternColumnConfig({
|
|
73
|
+
columns,
|
|
74
|
+
columnName: 'new_cases',
|
|
75
|
+
patterns: {
|
|
76
|
+
Pattern2: { application: 'portion', patternValueKey: 'new_cases' }
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
).toBe(columns)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('preserves columns owned by an axis or chart series', () => {
|
|
83
|
+
const columns = { cases: { name: 'cases', label: 'Cases' } } as any
|
|
84
|
+
|
|
85
|
+
expect(
|
|
86
|
+
removeUnusedPatternColumnConfig({
|
|
87
|
+
columns,
|
|
88
|
+
columnName: 'cases',
|
|
89
|
+
patterns: {},
|
|
90
|
+
protectedColumnNames: ['date', 'cases']
|
|
91
|
+
})
|
|
92
|
+
).toBe(columns)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe('portion pattern support', () => {
|
|
97
|
+
const supportedConfig = {
|
|
98
|
+
visualizationType: 'Bar',
|
|
99
|
+
visualizationSubType: 'regular',
|
|
100
|
+
isLollipopChart: false,
|
|
101
|
+
yAxis: { type: 'linear' }
|
|
102
|
+
} as any
|
|
103
|
+
|
|
104
|
+
it('supports regular Bar and Combo charts with linear axes', () => {
|
|
105
|
+
expect(isPortionPatternSupported(supportedConfig)).toBe(true)
|
|
106
|
+
expect(isPortionPatternSupported({ ...supportedConfig, visualizationType: 'Combo' })).toBe(true)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it.each([
|
|
110
|
+
['stacked bars', { visualizationSubType: 'stacked' }],
|
|
111
|
+
['lollipops', { isLollipopChart: true }],
|
|
112
|
+
['logarithmic axes', { yAxis: { type: 'logarithmic' } }],
|
|
113
|
+
['other chart types', { visualizationType: 'Line' }]
|
|
114
|
+
])('does not support %s', (_description, overrides) => {
|
|
115
|
+
expect(isPortionPatternSupported({ ...supportedConfig, ...overrides } as any)).toBe(false)
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
describe('getPortionPatternOverlay', () => {
|
|
120
|
+
const getOverlay = (totalValue: unknown, portion: unknown, placement?: 'start' | 'end') =>
|
|
121
|
+
getPortionPatternOverlay({
|
|
122
|
+
patterns: {
|
|
123
|
+
Pattern1: {
|
|
124
|
+
application: 'portion',
|
|
125
|
+
dataKey: 'total',
|
|
126
|
+
patternValueKey: 'portion',
|
|
127
|
+
placement,
|
|
128
|
+
color: '#123456'
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
datum: { total: totalValue, portion },
|
|
132
|
+
seriesKey: 'total',
|
|
133
|
+
totalValue
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('calculates ratios for portions less than and equal to the total', () => {
|
|
137
|
+
expect(getOverlay(100, 25)).toEqual({
|
|
138
|
+
patternUrl: `url(#${getChartPatternId('Pattern1')})`,
|
|
139
|
+
ratio: 0.25,
|
|
140
|
+
placement: 'end',
|
|
141
|
+
patternColor: '#123456'
|
|
142
|
+
})
|
|
143
|
+
expect(getOverlay('100', '100')?.ratio).toBe(1)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('preserves beginning placement', () => {
|
|
147
|
+
expect(getOverlay(100, 40, 'start')?.placement).toBe('start')
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it.each([
|
|
151
|
+
['missing total', undefined, 10],
|
|
152
|
+
['nonnumeric total', 'not-a-number', 10],
|
|
153
|
+
['zero total', 0, 0],
|
|
154
|
+
['negative total', -100, 10],
|
|
155
|
+
['missing portion', 100, undefined],
|
|
156
|
+
['nonnumeric portion', 100, 'not-a-number'],
|
|
157
|
+
['zero portion', 100, 0],
|
|
158
|
+
['negative portion', 100, -10],
|
|
159
|
+
['portion greater than total', 100, 101]
|
|
160
|
+
])('returns no overlay for %s', (_description, totalValue, portion) => {
|
|
161
|
+
expect(getOverlay(totalValue, portion)).toBeNull()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('uses only the first configured portion pattern for a target series', () => {
|
|
165
|
+
const overlay = getPortionPatternOverlay({
|
|
166
|
+
patterns: {
|
|
167
|
+
First: { application: 'portion', dataKey: 'total', patternValueKey: 'invalidPortion' },
|
|
168
|
+
Second: { application: 'portion', dataKey: 'total', patternValueKey: 'validPortion' }
|
|
169
|
+
},
|
|
170
|
+
datum: { total: 100, invalidPortion: 200, validPortion: 25 },
|
|
171
|
+
seriesKey: 'total',
|
|
172
|
+
totalValue: 100
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
expect(overlay).toBeNull()
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
describe('getPortionPatternGeometry', () => {
|
|
180
|
+
it.each([
|
|
181
|
+
['vertical end', 'vertical', 'end', { x: 10, y: 20, width: 100, height: 40 }],
|
|
182
|
+
['vertical start', 'vertical', 'start', { x: 10, y: 60, width: 100, height: 40 }],
|
|
183
|
+
['horizontal end', 'horizontal', 'end', { x: 60, y: 20, width: 50, height: 80 }],
|
|
184
|
+
['horizontal start', 'horizontal', 'start', { x: 10, y: 20, width: 50, height: 80 }]
|
|
185
|
+
] as const)('calculates %s placement', (_description, orientation, placement, expected) => {
|
|
186
|
+
expect(
|
|
187
|
+
getPortionPatternGeometry({
|
|
188
|
+
orientation,
|
|
189
|
+
bounds: { x: 10, y: 20, width: 100, height: 80 },
|
|
190
|
+
overlay: { ratio: 0.5, placement }
|
|
191
|
+
})
|
|
192
|
+
).toEqual(expected)
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe('getPortionPatternBoundaryGeometry', () => {
|
|
197
|
+
it.each([
|
|
198
|
+
['vertical end', 'vertical', 'end', { x1: 10, y1: 60, x2: 110, y2: 60 }],
|
|
199
|
+
['vertical start', 'vertical', 'start', { x1: 10, y1: 20, x2: 110, y2: 20 }],
|
|
200
|
+
['horizontal end', 'horizontal', 'end', { x1: 10, y1: 20, x2: 10, y2: 100 }],
|
|
201
|
+
['horizontal start', 'horizontal', 'start', { x1: 60, y1: 20, x2: 60, y2: 100 }]
|
|
202
|
+
] as const)('calculates the %s internal divider', (_description, orientation, placement, expected) => {
|
|
203
|
+
expect(
|
|
204
|
+
getPortionPatternBoundaryGeometry({
|
|
205
|
+
orientation,
|
|
206
|
+
geometry: {
|
|
207
|
+
x: 10,
|
|
208
|
+
y: 20,
|
|
209
|
+
width: orientation === 'vertical' ? 100 : 50,
|
|
210
|
+
height: orientation === 'vertical' ? 40 : 80
|
|
211
|
+
},
|
|
212
|
+
overlay: { ratio: 0.5, placement }
|
|
213
|
+
})
|
|
214
|
+
).toEqual(expected)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
it('omits the divider when the portion covers the full bar', () => {
|
|
218
|
+
expect(
|
|
219
|
+
getPortionPatternBoundaryGeometry({
|
|
220
|
+
orientation: 'vertical',
|
|
221
|
+
geometry: { x: 10, y: 20, width: 100, height: 80 },
|
|
222
|
+
overlay: { ratio: 1, placement: 'end' }
|
|
223
|
+
})
|
|
224
|
+
).toBeNull()
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe('getPortionPatternRenderData', () => {
|
|
229
|
+
it('returns a complete render model for supported valid portions', () => {
|
|
230
|
+
expect(
|
|
231
|
+
getPortionPatternRenderData({
|
|
232
|
+
config: {
|
|
233
|
+
visualizationType: 'Bar',
|
|
234
|
+
visualizationSubType: 'regular',
|
|
235
|
+
isLollipopChart: false,
|
|
236
|
+
yAxis: { type: 'linear' }
|
|
237
|
+
} as any,
|
|
238
|
+
orientation: 'vertical',
|
|
239
|
+
bounds: { x: 10, y: 20, width: 100, height: 80 },
|
|
240
|
+
patterns: {
|
|
241
|
+
Pattern1: {
|
|
242
|
+
application: 'portion',
|
|
243
|
+
dataKey: 'total',
|
|
244
|
+
patternValueKey: 'portion',
|
|
245
|
+
color: '#123456'
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
datum: { total: 100, portion: 25 },
|
|
249
|
+
seriesKey: 'total',
|
|
250
|
+
totalValue: 100
|
|
251
|
+
})
|
|
252
|
+
).toEqual({
|
|
253
|
+
patternUrl: `url(#${getChartPatternId('Pattern1')})`,
|
|
254
|
+
ratio: 0.25,
|
|
255
|
+
placement: 'end',
|
|
256
|
+
patternColor: '#123456',
|
|
257
|
+
geometry: { x: 10, y: 20, width: 100, height: 20 },
|
|
258
|
+
boundaryGeometry: { x1: 10, y1: 40, x2: 110, y2: 40 }
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('returns no render model for unsupported configurations', () => {
|
|
263
|
+
expect(
|
|
264
|
+
getPortionPatternRenderData({
|
|
265
|
+
config: {
|
|
266
|
+
visualizationType: 'Bar',
|
|
267
|
+
visualizationSubType: 'stacked',
|
|
268
|
+
isLollipopChart: false,
|
|
269
|
+
yAxis: { type: 'linear' }
|
|
270
|
+
} as any,
|
|
271
|
+
orientation: 'vertical',
|
|
272
|
+
bounds: { x: 10, y: 20, width: 100, height: 80 },
|
|
273
|
+
patterns: {},
|
|
274
|
+
datum: {},
|
|
275
|
+
seriesKey: 'total',
|
|
276
|
+
totalValue: 100
|
|
277
|
+
})
|
|
278
|
+
).toBeNull()
|
|
279
|
+
})
|
|
280
|
+
})
|
|
@@ -60,6 +60,10 @@ const makeConfigContext = (countColumnOverrides: Record<string, unknown> = {}) =
|
|
|
60
60
|
...baseConfig.runtime.yAxis,
|
|
61
61
|
dataKey: 'Percentage'
|
|
62
62
|
},
|
|
63
|
+
originalXAxis: {
|
|
64
|
+
...baseConfig.runtime.originalXAxis,
|
|
65
|
+
dataKey: 'Cause of death'
|
|
66
|
+
},
|
|
63
67
|
series: [{ dataKey: 'Percentage', type: 'Bar', axis: 'Left', tooltip: true }],
|
|
64
68
|
seriesKeys: ['Percentage'],
|
|
65
69
|
seriesLabels: { Percentage: 'Percentage' },
|
|
@@ -80,6 +84,106 @@ const makeConfigContext = (countColumnOverrides: Record<string, unknown> = {}) =
|
|
|
80
84
|
}
|
|
81
85
|
|
|
82
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
|
+
|
|
83
187
|
it('does not apply the left-axis suffix to an additional single-series tooltip column', () => {
|
|
84
188
|
const configContext = makeConfigContext()
|
|
85
189
|
const { result } = renderHook(() => useBarChart(vi.fn(), vi.fn(), configContext))
|
|
@@ -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 {
|
|
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,21 +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: '',
|
|
188
|
-
addColSuffix: '',
|
|
189
|
-
...getSeriesColumnFormattingParams(config.columns[colKeys])
|
|
190
|
-
}
|
|
174
|
+
const formattingParams = getAdditionalColumnFormattingParams(config.columns[colKeys])
|
|
191
175
|
|
|
192
176
|
const formattedValue = formatColNumber(
|
|
193
177
|
closestVal[config.columns[colKeys].name],
|
|
@@ -206,6 +190,22 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
206
190
|
return additionalTooltipItems
|
|
207
191
|
}
|
|
208
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
|
+
|
|
209
209
|
const onMouseOverBar = (categoryValue, barKey, event, data, barValue) => {
|
|
210
210
|
if (config.legend.highlightOnHover && config.legend.behavior === 'highlight' && barKey) {
|
|
211
211
|
dispatch({ type: 'SET_SERIES_HIGHLIGHT', payload: [barKey] })
|
|
@@ -255,6 +255,7 @@ export const useBarChart = (handleTooltipMouseOver, handleTooltipMouseOff, confi
|
|
|
255
255
|
barStackedSeriesKeys,
|
|
256
256
|
hasMultipleSeries,
|
|
257
257
|
labelFontSize,
|
|
258
|
+
formatTooltipValue,
|
|
258
259
|
applyRadius,
|
|
259
260
|
assignColorsToValues,
|
|
260
261
|
getHighlightedBarColorByValue,
|