@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,185 @@
|
|
|
1
|
+
import { type ChartConfig } from '../../types/ChartConfig'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_BAR_RACE_MAX_BARS = 10
|
|
4
|
+
|
|
5
|
+
type DataRow = Record<string, unknown>
|
|
6
|
+
|
|
7
|
+
export type BarRaceItem = {
|
|
8
|
+
category: string
|
|
9
|
+
dataKey: string
|
|
10
|
+
frame: string
|
|
11
|
+
rank: number
|
|
12
|
+
seriesKey: string
|
|
13
|
+
value: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type BarRaceFrame = {
|
|
17
|
+
key: string
|
|
18
|
+
items: BarRaceItem[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type BarRaceProjection = {
|
|
22
|
+
competitorCount: number
|
|
23
|
+
hasDuplicateRows: boolean
|
|
24
|
+
frames: BarRaceFrame[]
|
|
25
|
+
globalMax: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type BarRaceEligibility = BarRaceProjection & {
|
|
29
|
+
eligible: boolean
|
|
30
|
+
reason?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const toFiniteNonnegativeNumber = (value: unknown): number | null => {
|
|
34
|
+
if (typeof value !== 'string' && typeof value !== 'number') return null
|
|
35
|
+
if (typeof value === 'string' && value.trim() === '') return null
|
|
36
|
+
const numericValue = typeof value === 'string' ? Number(value.replaceAll(',', '').replace(/%$/, '')) : Number(value)
|
|
37
|
+
return Number.isFinite(numericValue) && numericValue >= 0 ? numericValue : null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const getMaxBars = (config: Partial<ChartConfig>, competitorCount: number) => {
|
|
41
|
+
const authoredMax = Number(config.barRace?.maxBars ?? DEFAULT_BAR_RACE_MAX_BARS)
|
|
42
|
+
const roundedMax = Number.isFinite(authoredMax) ? Math.round(authoredMax) : DEFAULT_BAR_RACE_MAX_BARS
|
|
43
|
+
return Math.max(1, Math.min(competitorCount, roundedMax))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const buildBarRaceFrames = (config: Partial<ChartConfig>, data: DataRow[] = []): BarRaceProjection => {
|
|
47
|
+
const series = Array.isArray(config.series) ? config.series.filter(item => item?.dataKey) : []
|
|
48
|
+
const dynamicSeries = series.filter(item => item.dynamicCategory)
|
|
49
|
+
const usesDynamicCategory = series.length === 1 && dynamicSeries.length === 1
|
|
50
|
+
const frameKey = config.xAxis?.dataKey
|
|
51
|
+
const emptyProjection = { competitorCount: 0, hasDuplicateRows: false, frames: [], globalMax: 0 }
|
|
52
|
+
|
|
53
|
+
if (!frameKey || !series.length || !Array.isArray(data)) return emptyProjection
|
|
54
|
+
|
|
55
|
+
const frameOrder: string[] = []
|
|
56
|
+
const categoryOrder: string[] = []
|
|
57
|
+
const categoryOrderSet = new Set<string>()
|
|
58
|
+
const valuesByFrame = new Map<string, Map<string, Omit<BarRaceItem, 'frame' | 'rank'>>>()
|
|
59
|
+
let hasDuplicateRows = false
|
|
60
|
+
|
|
61
|
+
const addValue = (frame: string, category: string, seriesKey: string, dataKey: string, value: number) => {
|
|
62
|
+
if (!categoryOrderSet.has(seriesKey)) {
|
|
63
|
+
categoryOrder.push(seriesKey)
|
|
64
|
+
categoryOrderSet.add(seriesKey)
|
|
65
|
+
}
|
|
66
|
+
const frameValues = valuesByFrame.get(frame)
|
|
67
|
+
if (frameValues?.has(seriesKey)) hasDuplicateRows = true
|
|
68
|
+
frameValues?.set(seriesKey, { category, dataKey, seriesKey, value })
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
data.forEach(row => {
|
|
72
|
+
const rawFrame = row?.[frameKey]
|
|
73
|
+
if (rawFrame === null || rawFrame === undefined || String(rawFrame).trim() === '') return
|
|
74
|
+
const frame = String(rawFrame).trim()
|
|
75
|
+
if (!valuesByFrame.has(frame)) {
|
|
76
|
+
frameOrder.push(frame)
|
|
77
|
+
valuesByFrame.set(frame, new Map())
|
|
78
|
+
} else if (!usesDynamicCategory) {
|
|
79
|
+
hasDuplicateRows = true
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (usesDynamicCategory) {
|
|
83
|
+
const item = dynamicSeries[0]
|
|
84
|
+
const rawCategory = row?.[item.dynamicCategory]
|
|
85
|
+
const value = toFiniteNonnegativeNumber(row?.[item.dataKey])
|
|
86
|
+
if (rawCategory === null || rawCategory === undefined || String(rawCategory).trim() === '' || value === null)
|
|
87
|
+
return
|
|
88
|
+
const category = String(rawCategory).trim()
|
|
89
|
+
addValue(frame, category, category, item.dataKey, value)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
series.forEach(item => {
|
|
94
|
+
const value = toFiniteNonnegativeNumber(row?.[item.dataKey])
|
|
95
|
+
if (value === null) return
|
|
96
|
+
addValue(frame, item.name || item.dataKey, item.dataKey, item.dataKey, value)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const runtimeCategoryOrder = Array.isArray(config.runtime?.seriesKeys)
|
|
101
|
+
? [...new Set(config.runtime.seriesKeys.map(value => String(value).trim()))].filter(category =>
|
|
102
|
+
categoryOrderSet.has(category)
|
|
103
|
+
)
|
|
104
|
+
: []
|
|
105
|
+
const runtimeCategorySet = new Set(runtimeCategoryOrder)
|
|
106
|
+
const stableCategoryOrder = [...runtimeCategoryOrder, ...categoryOrder.filter(item => !runtimeCategorySet.has(item))]
|
|
107
|
+
const categoryIndex = new Map(stableCategoryOrder.map((category, index) => [category, index]))
|
|
108
|
+
const maxBars = getMaxBars(config, stableCategoryOrder.length)
|
|
109
|
+
let globalMax = 0
|
|
110
|
+
|
|
111
|
+
const frames = frameOrder.map(frame => {
|
|
112
|
+
const values = valuesByFrame.get(frame) ?? new Map()
|
|
113
|
+
const items = [...values.values()]
|
|
114
|
+
.map(item => ({ ...item, frame, rank: 0 }))
|
|
115
|
+
.sort(
|
|
116
|
+
(a, b) => b.value - a.value || (categoryIndex.get(a.seriesKey) ?? 0) - (categoryIndex.get(b.seriesKey) ?? 0)
|
|
117
|
+
)
|
|
118
|
+
.slice(0, maxBars)
|
|
119
|
+
.map((item, rank) => ({ ...item, rank }))
|
|
120
|
+
|
|
121
|
+
items.forEach(item => {
|
|
122
|
+
globalMax = Math.max(globalMax, item.value)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
return { key: frame, items }
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Domain stability must include valid values outside the displayed top N.
|
|
129
|
+
valuesByFrame.forEach(values => values.forEach(item => (globalMax = Math.max(globalMax, item.value))))
|
|
130
|
+
|
|
131
|
+
return { competitorCount: stableCategoryOrder.length, hasDuplicateRows, frames, globalMax }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const getBarRaceEligibility = (config: Partial<ChartConfig>, data: DataRow[] = []): BarRaceEligibility => {
|
|
135
|
+
const projection = buildBarRaceFrames(config, data)
|
|
136
|
+
const series = Array.isArray(config.series) ? config.series : []
|
|
137
|
+
const dynamicSeries = series.filter(item => item?.dynamicCategory)
|
|
138
|
+
const hasValidDynamicSeries =
|
|
139
|
+
series.length === 1 && Boolean(series[0]?.dataKey) && Boolean(String(series[0]?.dynamicCategory || '').trim())
|
|
140
|
+
const hasValidWideSeries = series.length >= 2 && dynamicSeries.length === 0 && series.every(item => item?.dataKey)
|
|
141
|
+
|
|
142
|
+
if (config.visualizationType !== 'Bar') {
|
|
143
|
+
return { ...projection, eligible: false, reason: 'Racing mode is available only for Bar charts.' }
|
|
144
|
+
}
|
|
145
|
+
if (config.xAxis?.type !== 'categorical') {
|
|
146
|
+
return { ...projection, eligible: false, reason: 'Racing mode requires a categorical Date/Category Axis.' }
|
|
147
|
+
}
|
|
148
|
+
if (!hasValidDynamicSeries && !hasValidWideSeries) {
|
|
149
|
+
return {
|
|
150
|
+
...projection,
|
|
151
|
+
eligible: false,
|
|
152
|
+
reason: 'Racing mode requires at least two ordinary data series or one series with a Dynamic Category column.'
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (config.smallMultiples?.mode) {
|
|
156
|
+
return { ...projection, eligible: false, reason: 'Racing mode does not support small multiples.' }
|
|
157
|
+
}
|
|
158
|
+
if (config.orientation !== 'horizontal') {
|
|
159
|
+
return { ...projection, eligible: false, reason: 'Racing mode requires horizontal orientation.' }
|
|
160
|
+
}
|
|
161
|
+
if (config.isLollipopChart || (config.barStyle && config.barStyle !== 'flat')) {
|
|
162
|
+
return { ...projection, eligible: false, reason: 'Racing mode requires the flat bar style.' }
|
|
163
|
+
}
|
|
164
|
+
if (projection.hasDuplicateRows) {
|
|
165
|
+
return {
|
|
166
|
+
...projection,
|
|
167
|
+
eligible: false,
|
|
168
|
+
reason: 'Racing mode requires one row per frame and competitor combination.'
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (projection.frames.length < 2) {
|
|
172
|
+
return { ...projection, eligible: false, reason: 'Racing mode requires at least two valid frame values.' }
|
|
173
|
+
}
|
|
174
|
+
if (projection.competitorCount < 2) {
|
|
175
|
+
return { ...projection, eligible: false, reason: 'Racing mode requires at least two valid competitors.' }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { ...projection, eligible: true }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const clampBarRaceMaxBars = (value: unknown, competitorCount: number) => {
|
|
182
|
+
const numericValue = Number(value)
|
|
183
|
+
const nextValue = Number.isFinite(numericValue) ? Math.round(numericValue) : DEFAULT_BAR_RACE_MAX_BARS
|
|
184
|
+
return Math.max(1, Math.min(Math.max(1, competitorCount), nextValue))
|
|
185
|
+
}
|
|
@@ -5,7 +5,8 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
|
|
|
5
5
|
import ConfigContext from '../../ConfigContext'
|
|
6
6
|
import { handleTooltip, createPlots } from './helpers/index'
|
|
7
7
|
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
8
|
-
import
|
|
8
|
+
import map from 'lodash/map'
|
|
9
|
+
import round from 'lodash/round'
|
|
9
10
|
|
|
10
11
|
const BoxPlotHorizontal = ({ xScale, yScale, seriesScale }) => {
|
|
11
12
|
const { config, transformedData: data, colorScale, seriesHighlight } = useContext(ConfigContext)
|
|
@@ -63,7 +64,7 @@ const BoxPlotHorizontal = ({ xScale, yScale, seriesScale }) => {
|
|
|
63
64
|
maxProps={{ stroke: defaultColor, strokeWidth: 1, opacity: fillOpacity }}
|
|
64
65
|
medianProps={{ stroke: defaultColor, strokeWidth: 1, opacity: fillOpacity }}
|
|
65
66
|
outliers={
|
|
66
|
-
config.boxplot.plotOutlierValues ?
|
|
67
|
+
config.boxplot.plotOutlierValues ? map(plot.columnOutliers[item.dataKey], item => item) : []
|
|
67
68
|
}
|
|
68
69
|
outlierProps={{
|
|
69
70
|
style: {
|
|
@@ -78,10 +79,10 @@ const BoxPlotHorizontal = ({ xScale, yScale, seriesScale }) => {
|
|
|
78
79
|
config.boxplot,
|
|
79
80
|
plot.columnCategory,
|
|
80
81
|
item.dataKey,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
round(plot.q1[item.dataKey], config.dataFormat.roundTo),
|
|
83
|
+
round(plot.q3[item.dataKey], config.dataFormat.roundTo),
|
|
84
|
+
round(plot.median[item.dataKey], config.dataFormat.roundTo),
|
|
85
|
+
round(plot.iqr[item.dataKey], config.dataFormat.roundTo),
|
|
85
86
|
config.xAxis.label,
|
|
86
87
|
defaultColor
|
|
87
88
|
),
|
|
@@ -7,7 +7,9 @@ import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
|
|
|
7
7
|
import { getCurrentPaletteName } from '@cdc/core/helpers/palettes/utils'
|
|
8
8
|
import { handleTooltip, createPlots } from './helpers/index'
|
|
9
9
|
import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
|
|
10
|
-
import
|
|
10
|
+
import get from 'lodash/get'
|
|
11
|
+
import map from 'lodash/map'
|
|
12
|
+
import round from 'lodash/round'
|
|
11
13
|
|
|
12
14
|
const BoxPlotVertical = ({ xScale, yScale, seriesScale, yAxisWidth }) => {
|
|
13
15
|
const { config, colorScale, seriesHighlight, transformedData: data } = useContext(ConfigContext)
|
|
@@ -19,7 +21,7 @@ const BoxPlotVertical = ({ xScale, yScale, seriesScale, yAxisWidth }) => {
|
|
|
19
21
|
const bodyStyles = getComputedStyle(document.body)
|
|
20
22
|
const defaultColor = APP_FONT_COLOR
|
|
21
23
|
const constrainedWidth = Math.min(40, boxWidth)
|
|
22
|
-
const color_0 =
|
|
24
|
+
const color_0 = get(colorPalettesChart, [getCurrentPaletteName(config), 0], '#000')
|
|
23
25
|
const plots = createPlots(data, config)
|
|
24
26
|
return (
|
|
25
27
|
<ErrorBoundary component='BoxPlot Vertical'>
|
|
@@ -76,7 +78,7 @@ const BoxPlotVertical = ({ xScale, yScale, seriesScale, yAxisWidth }) => {
|
|
|
76
78
|
fillOpacity={1}
|
|
77
79
|
stroke={defaultColor}
|
|
78
80
|
valueScale={yScale}
|
|
79
|
-
outliers={boxplot.plotOutlierValues ?
|
|
81
|
+
outliers={boxplot.plotOutlierValues ? map(d.columnOutliers[item.dataKey], item => item) : []}
|
|
80
82
|
outlierProps={{
|
|
81
83
|
style: {
|
|
82
84
|
fill: defaultColor,
|
|
@@ -109,10 +111,10 @@ const BoxPlotVertical = ({ xScale, yScale, seriesScale, yAxisWidth }) => {
|
|
|
109
111
|
boxplot,
|
|
110
112
|
d.columnCategory,
|
|
111
113
|
item.dataKey,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
round(d.q1[item.dataKey], config.dataFormat.roundTo),
|
|
115
|
+
round(d.q3[item.dataKey], config.dataFormat.roundTo),
|
|
116
|
+
round(d.median[item.dataKey], config.dataFormat.roundTo),
|
|
117
|
+
round(d.iqr[item.dataKey], config.dataFormat.roundTo),
|
|
116
118
|
config.xAxis.label,
|
|
117
119
|
defaultColor
|
|
118
120
|
),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import uniq from 'lodash/uniq'
|
|
2
|
+
import { calculateBoxPlotStats } from '../../../helpers/boxPlotStats'
|
|
3
3
|
|
|
4
4
|
interface Plot {
|
|
5
5
|
columnCategory: string
|
|
@@ -29,47 +29,6 @@ export const handleTooltip = (boxplot, columnCategory, key, q1, q3, median, iqr,
|
|
|
29
29
|
`
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const calculateBoxPlotStats = (values: number[]) => {
|
|
33
|
-
if (!values || values.length === 0) return {}
|
|
34
|
-
|
|
35
|
-
// Sort the values
|
|
36
|
-
const sortedValues = _.sortBy(values.map(v => Number(v)))
|
|
37
|
-
|
|
38
|
-
// Quartiles
|
|
39
|
-
const firstQuartile = d3.quantile(sortedValues, 0.25) ?? 0
|
|
40
|
-
const thirdQuartile = d3.quantile(sortedValues, 0.75) ?? 0
|
|
41
|
-
|
|
42
|
-
// Interquartile Range (IQR)
|
|
43
|
-
const iqr = thirdQuartile - firstQuartile
|
|
44
|
-
|
|
45
|
-
// Outlier Bounds
|
|
46
|
-
const lowerBound = firstQuartile - 1.5 * iqr
|
|
47
|
-
const upperBound = thirdQuartile + 1.5 * iqr
|
|
48
|
-
// const lowerFence = q1 - 1.5 * iqr
|
|
49
|
-
// const upperFence = q3 + 1.5 * iqr
|
|
50
|
-
|
|
51
|
-
// Non-Outlier Values
|
|
52
|
-
const nonOutliers = sortedValues.filter(value => value >= lowerBound && value <= upperBound)
|
|
53
|
-
// **Outliers** =
|
|
54
|
-
const outliers = sortedValues.filter(v => v < lowerBound || v > upperBound)
|
|
55
|
-
const whiskerMax =
|
|
56
|
-
sortedValues
|
|
57
|
-
.slice()
|
|
58
|
-
.reverse()
|
|
59
|
-
.find(v => v <= upperBound) ?? thirdQuartile
|
|
60
|
-
const whiskerMin = nonOutliers.length > 0 ? nonOutliers[0] : firstQuartile
|
|
61
|
-
// Calculate Box Plot Stats
|
|
62
|
-
return {
|
|
63
|
-
min: whiskerMin,
|
|
64
|
-
max: whiskerMax,
|
|
65
|
-
median: d3.median(sortedValues),
|
|
66
|
-
firstQuartile,
|
|
67
|
-
thirdQuartile,
|
|
68
|
-
iqr,
|
|
69
|
-
outliers
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
32
|
const getValuesBySeriesKey = (group: string, config, data) => {
|
|
74
33
|
const allSeriesKeys = config.series.map(item => item?.dataKey)
|
|
75
34
|
const result = {}
|
|
@@ -81,29 +40,11 @@ const getValuesBySeriesKey = (group: string, config, data) => {
|
|
|
81
40
|
return result
|
|
82
41
|
}
|
|
83
42
|
|
|
84
|
-
// Helper to calculate outliers based on IQR
|
|
85
|
-
const calculateOutliers = (values: number[], firstQuartile: number, thirdQuartile: number) => {
|
|
86
|
-
const iqr = thirdQuartile - firstQuartile
|
|
87
|
-
const lowerBound = firstQuartile - 1.5 * iqr
|
|
88
|
-
const upperBound = thirdQuartile + 1.5 * iqr
|
|
89
|
-
return values.filter(value => value < lowerBound || value > upperBound)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Helper to calculate non-outliers based on IQR
|
|
93
|
-
const calculateNonOutliers = (values: number[], firstQuartile: number, thirdQuartile: number): number[] => {
|
|
94
|
-
const iqr = thirdQuartile - firstQuartile
|
|
95
|
-
const lowerBound = firstQuartile - 1.5 * iqr
|
|
96
|
-
const upperBound = thirdQuartile + 1.5 * iqr
|
|
97
|
-
|
|
98
|
-
// Return values within the bounds
|
|
99
|
-
return values.filter(value => value >= lowerBound && value <= upperBound)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
43
|
// Main function to create plots with additional outlier data
|
|
103
44
|
export const createPlots = (data, config) => {
|
|
104
45
|
const dataKeys = data.map(d => d[config.xAxis.dataKey])
|
|
105
46
|
const plots: Plot[] = []
|
|
106
|
-
const groups: string[] =
|
|
47
|
+
const groups: string[] = uniq(dataKeys)
|
|
107
48
|
|
|
108
49
|
if (groups && groups.length > 0) {
|
|
109
50
|
groups.forEach(group => {
|
|
@@ -119,29 +60,18 @@ export const createPlots = (data, config) => {
|
|
|
119
60
|
|
|
120
61
|
// Calculate outliers and non-outliers for each series key
|
|
121
62
|
Object.keys(keyValues).forEach(key => {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
// 2) normalize → trim, drop empties/non-numbers, coerce to Number
|
|
125
|
-
const cleaned: number[] = raw
|
|
126
|
-
.map(v => (typeof v === 'string' ? v.trim() : v)) // trim strings
|
|
127
|
-
.filter(v => v != null && v !== '' && !isNaN(+v)) // drop null/''/non-nums
|
|
128
|
-
.map(v => +v)
|
|
129
|
-
|
|
130
|
-
if (cleaned.length === 0) {
|
|
131
|
-
return
|
|
132
|
-
}
|
|
63
|
+
const stats = calculateBoxPlotStats(keyValues[key] ?? [])
|
|
64
|
+
if (!stats) return
|
|
133
65
|
|
|
134
66
|
// Calculate box plot statistics
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
columnQ3[key] = thirdQuartile
|
|
144
|
-
columnIqr[key] = iqr
|
|
67
|
+
columnOutliers[key] = stats.outliers
|
|
68
|
+
columnNonOutliers[key] = stats.nonOutliers
|
|
69
|
+
columnMedian[key] = stats.median ?? null
|
|
70
|
+
columnMin[key] = stats.whiskerMin
|
|
71
|
+
columnMax[key] = stats.whiskerMax
|
|
72
|
+
columnQ1[key] = stats.q1
|
|
73
|
+
columnQ3[key] = stats.q3
|
|
74
|
+
columnIqr[key] = stats.iqr
|
|
145
75
|
})
|
|
146
76
|
|
|
147
77
|
// Add the plot object to the plots array
|
|
@@ -9,6 +9,7 @@ import type { BrushHandleRenderProps } from '@visx/brush/lib/BrushHandle'
|
|
|
9
9
|
import { isDateScale } from '@cdc/core/helpers/cove/date'
|
|
10
10
|
import ConfigContext, { ChartDispatchContext } from '../../ConfigContext'
|
|
11
11
|
import { getChartPatternId } from '../../helpers/getChartPatternId'
|
|
12
|
+
import getStackedSeriesMax from '../../helpers/getStackedSeriesMax'
|
|
12
13
|
import MiniChartPreview from './MiniChartPreview'
|
|
13
14
|
import { classifyBrushInteraction } from './brushInteraction'
|
|
14
15
|
import type { BrushInteractionTarget } from './brushInteraction'
|
|
@@ -128,7 +129,7 @@ const BrushSelector: FC<BrushSelectorProps> = ({ xMax, yMax }) => {
|
|
|
128
129
|
<>
|
|
129
130
|
{Object.entries(config.legend.patterns).map(([key, pattern]) => {
|
|
130
131
|
const patternId = getChartPatternId(key)
|
|
131
|
-
const size = pattern.patternSize ||
|
|
132
|
+
const size = pattern.patternSize || 10
|
|
132
133
|
|
|
133
134
|
switch (pattern.shape) {
|
|
134
135
|
case 'circles':
|
|
@@ -173,8 +174,9 @@ const BrushSelector: FC<BrushSelectorProps> = ({ xMax, yMax }) => {
|
|
|
173
174
|
id={patternId}
|
|
174
175
|
height={size}
|
|
175
176
|
width={size}
|
|
176
|
-
fill=
|
|
177
|
-
|
|
177
|
+
fill='transparent'
|
|
178
|
+
stroke={pattern.color}
|
|
179
|
+
strokeWidth={0.75}
|
|
178
180
|
/>
|
|
179
181
|
)
|
|
180
182
|
default:
|
|
@@ -298,24 +300,9 @@ const BrushSelector: FC<BrushSelectorProps> = ({ xMax, yMax }) => {
|
|
|
298
300
|
let hasValidValues = false
|
|
299
301
|
|
|
300
302
|
if (isStacked) {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
for (const s of leftSeries) {
|
|
305
|
-
if (!s.dataKey) continue
|
|
306
|
-
const value = parseFloat(row[s.dataKey])
|
|
307
|
-
if (!isNaN(value) && isFinite(value)) {
|
|
308
|
-
rowSum += value
|
|
309
|
-
hasRowValue = true
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (hasRowValue) {
|
|
313
|
-
hasValidValues = true
|
|
314
|
-
minValue = Math.min(minValue, rowSum)
|
|
315
|
-
maxValue = Math.max(maxValue, rowSum)
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
minValue = Math.min(0, minValue)
|
|
303
|
+
maxValue = getStackedSeriesMax(tableData, leftSeries)
|
|
304
|
+
minValue = 0
|
|
305
|
+
hasValidValues = maxValue !== 0
|
|
319
306
|
} else {
|
|
320
307
|
for (const s of leftSeries) {
|
|
321
308
|
if (!s.dataKey) continue
|
|
@@ -831,14 +818,13 @@ const BrushSelector: FC<BrushSelectorProps> = ({ xMax, yMax }) => {
|
|
|
831
818
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
832
819
|
}, [config.xAxis.brushDefaultRecentDateCount])
|
|
833
820
|
|
|
834
|
-
// Selected box style with solid border and pattern fill
|
|
835
821
|
const selectedBoxStyle = useMemo(
|
|
836
822
|
() => ({
|
|
837
|
-
fill: 'url(#brush_pattern)',
|
|
823
|
+
fill: config.xAxis.brushHideHatching === true ? 'transparent' : 'url(#brush_pattern)',
|
|
838
824
|
stroke: '#333',
|
|
839
825
|
strokeWidth: 1
|
|
840
826
|
}),
|
|
841
|
-
[]
|
|
827
|
+
[config.xAxis.brushHideHatching]
|
|
842
828
|
)
|
|
843
829
|
|
|
844
830
|
// Helper to update brush position programmatically
|
|
@@ -4,6 +4,8 @@ import * as allCurves from '@visx/curve'
|
|
|
4
4
|
import { handleLineType } from '../../helpers/handleLineType'
|
|
5
5
|
import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
|
|
6
6
|
import { getPatternUrl as getPatternUrlForBar } from '../BarChart/helpers/getPatternUrl'
|
|
7
|
+
import { getPortionPatternRenderData } from '../BarChart/helpers/portionPattern'
|
|
8
|
+
import { PortionPatternBoundary } from '../BarChart/components/PortionPatternOverlay'
|
|
7
9
|
|
|
8
10
|
interface MiniChartPreviewProps {
|
|
9
11
|
series: any[]
|
|
@@ -141,12 +143,20 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
141
143
|
seriesKeys: patternSeriesKeys,
|
|
142
144
|
allowNonSeriesFieldMatch: allowGroupedNonSeriesFieldMatch
|
|
143
145
|
})
|
|
144
|
-
|
|
145
146
|
// Calculate bar position and height
|
|
146
147
|
const valueY = miniYScale(value)
|
|
147
148
|
const barHeight = Math.abs(valueY - zeroY)
|
|
148
149
|
const barX = x - barWidth / 2 + seriesIndex * groupBarWidth
|
|
149
150
|
const y = Math.min(valueY, zeroY) // Top of bar (smaller Y value)
|
|
151
|
+
const portionPatternRenderData = getPortionPatternRenderData({
|
|
152
|
+
config,
|
|
153
|
+
orientation: 'vertical',
|
|
154
|
+
bounds: { x: barX, y, width: groupBarWidth, height: barHeight },
|
|
155
|
+
patterns: config.legend?.patterns,
|
|
156
|
+
datum: d,
|
|
157
|
+
seriesKey: s.dataKey,
|
|
158
|
+
totalValue: value
|
|
159
|
+
})
|
|
150
160
|
|
|
151
161
|
bars.push(
|
|
152
162
|
<rect
|
|
@@ -178,6 +188,28 @@ const MiniChartPreview = memo<MiniChartPreviewProps>(
|
|
|
178
188
|
/>
|
|
179
189
|
)
|
|
180
190
|
}
|
|
191
|
+
if (portionPatternRenderData) {
|
|
192
|
+
bars.push(
|
|
193
|
+
<rect
|
|
194
|
+
key={`mini-bar-grouped-portion-pattern-${i}-${seriesIndex}`}
|
|
195
|
+
className='pattern-overlay pattern-overlay--portion pattern-overlay--brush'
|
|
196
|
+
{...portionPatternRenderData.geometry}
|
|
197
|
+
fill={portionPatternRenderData.patternUrl}
|
|
198
|
+
stroke='transparent'
|
|
199
|
+
strokeWidth={0}
|
|
200
|
+
fillOpacity={1}
|
|
201
|
+
pointerEvents='none'
|
|
202
|
+
/>
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
bars.push(
|
|
206
|
+
<PortionPatternBoundary
|
|
207
|
+
key={`mini-bar-grouped-portion-pattern-boundary-${i}-${seriesIndex}`}
|
|
208
|
+
renderData={portionPatternRenderData}
|
|
209
|
+
brush
|
|
210
|
+
/>
|
|
211
|
+
)
|
|
212
|
+
}
|
|
181
213
|
})
|
|
182
214
|
}
|
|
183
215
|
})
|