@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
|
@@ -20,6 +20,7 @@ describe('LineChart helpers', () => {
|
|
|
20
20
|
const basePreliminaryData: PreliminaryDataItem[] = [
|
|
21
21
|
{
|
|
22
22
|
type: 'effect',
|
|
23
|
+
lineEffectScope: 'outgoing',
|
|
23
24
|
seriesKeys: ['COVID-19'],
|
|
24
25
|
column: 'Attribute',
|
|
25
26
|
value: 'Dotted',
|
|
@@ -38,6 +39,7 @@ describe('LineChart helpers', () => {
|
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
41
|
type: 'effect',
|
|
42
|
+
lineEffectScope: 'outgoing',
|
|
41
43
|
seriesKeys: ['Influenza'],
|
|
42
44
|
column: 'Attribute',
|
|
43
45
|
value: 'Dotted',
|
|
@@ -56,14 +58,54 @@ describe('LineChart helpers', () => {
|
|
|
56
58
|
}
|
|
57
59
|
]
|
|
58
60
|
|
|
61
|
+
describe('existing effect rules without a scope marker', () => {
|
|
62
|
+
it.each(['', 'Category'])('preserves adjacent segments for %s category data', dynamicCategory => {
|
|
63
|
+
const data = [
|
|
64
|
+
{ Date: 'Oct.–Dec. 2024', Category: 'COVID-19', 'COVID-19': 5.2, Value: 5.2, Attribute: '' },
|
|
65
|
+
{ Date: 'Jan.–Mar. 2025', Category: 'COVID-19', 'COVID-19': 5.7, Value: 5.7, Attribute: 'Dotted' },
|
|
66
|
+
{ Date: 'Apr.–Jun. 2025', Category: 'COVID-19', 'COVID-19': 5.4, Value: 5.4, Attribute: 'Dotted' }
|
|
67
|
+
]
|
|
68
|
+
const styles = createStyles({
|
|
69
|
+
preliminaryData: [{ ...basePreliminaryData[0], lineEffectScope: undefined, weight: 4 }],
|
|
70
|
+
data,
|
|
71
|
+
stroke: '#000',
|
|
72
|
+
strokeWidth: 2,
|
|
73
|
+
handleLineType: mockHandleLineType,
|
|
74
|
+
lineType: 'solid-line',
|
|
75
|
+
seriesKey: 'COVID-19',
|
|
76
|
+
dynamicCategory,
|
|
77
|
+
originalSeriesKey: 'Value'
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
expect(styles.map(style => style.strokeDasharray)).toEqual(['5 5', '5 5', '5 5'])
|
|
81
|
+
expect(styles.map(style => style.strokeWidth)).toEqual([4, 4, 4])
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('keeps an incoming effect when only the latest point matches', () => {
|
|
85
|
+
const styles = createStyles({
|
|
86
|
+
preliminaryData: basePreliminaryData.map(rule => ({ ...rule, lineEffectScope: undefined })),
|
|
87
|
+
data: [{ Attribute: '' }, { Attribute: 'Dotted' }],
|
|
88
|
+
stroke: '#000',
|
|
89
|
+
strokeWidth: 2,
|
|
90
|
+
handleLineType: mockHandleLineType,
|
|
91
|
+
lineType: 'solid-line',
|
|
92
|
+
seriesKey: 'COVID-19',
|
|
93
|
+
dynamicCategory: '',
|
|
94
|
+
originalSeriesKey: ''
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
expect(styles.map(style => style.strokeDasharray)).toEqual(['5 5', '5 5'])
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
|
|
59
101
|
describe('with dynamic category data (single-row/tall format)', () => {
|
|
60
102
|
const dynamicCategoryData = [
|
|
61
|
-
{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: '
|
|
62
|
-
{ Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: '' },
|
|
63
|
-
{ Date: '10/19/2025', Category: 'COVID-19', Value: '42.6', Attribute: '
|
|
103
|
+
{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: '' },
|
|
104
|
+
{ Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: 'Dotted' },
|
|
105
|
+
{ Date: '10/19/2025', Category: 'COVID-19', Value: '42.6', Attribute: '' }
|
|
64
106
|
]
|
|
65
107
|
|
|
66
|
-
it('should apply
|
|
108
|
+
it('should apply an isolated effect only to the segment leaving the matching point', () => {
|
|
67
109
|
const styles = createStyles({
|
|
68
110
|
preliminaryData: basePreliminaryData,
|
|
69
111
|
data: dynamicCategoryData,
|
|
@@ -77,14 +119,51 @@ describe('LineChart helpers', () => {
|
|
|
77
119
|
})
|
|
78
120
|
|
|
79
121
|
expect(styles).toHaveLength(3)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
122
|
+
expect(styles.map(style => style.strokeDasharray)).toEqual([0, '5 5', 0])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should create a continuous effect range from consecutive matching points', () => {
|
|
126
|
+
const data = [
|
|
127
|
+
{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: '' },
|
|
128
|
+
{ Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: 'Dotted' },
|
|
129
|
+
{ Date: '10/19/2025', Category: 'COVID-19', Value: '42.6', Attribute: 'Dotted' },
|
|
130
|
+
{ Date: '10/26/2025', Category: 'COVID-19', Value: '44.1', Attribute: '' }
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
const styles = createStyles({
|
|
134
|
+
preliminaryData: basePreliminaryData,
|
|
135
|
+
data,
|
|
136
|
+
stroke: '#000',
|
|
137
|
+
strokeWidth: 2,
|
|
138
|
+
handleLineType: mockHandleLineType,
|
|
139
|
+
lineType: 'solid-line',
|
|
140
|
+
seriesKey: 'COVID-19',
|
|
141
|
+
dynamicCategory: 'Category',
|
|
142
|
+
originalSeriesKey: 'Value'
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
expect(styles.map(style => style.strokeDasharray)).toEqual([0, '5 5', '5 5', 0])
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it('should leave the incoming segment normal when only the final point matches', () => {
|
|
149
|
+
const data = [
|
|
150
|
+
{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: '' },
|
|
151
|
+
{ Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: 'Dotted' }
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
const styles = createStyles({
|
|
155
|
+
preliminaryData: basePreliminaryData,
|
|
156
|
+
data,
|
|
157
|
+
stroke: '#000',
|
|
158
|
+
strokeWidth: 2,
|
|
159
|
+
handleLineType: mockHandleLineType,
|
|
160
|
+
lineType: 'solid-line',
|
|
161
|
+
seriesKey: 'COVID-19',
|
|
162
|
+
dynamicCategory: 'Category',
|
|
163
|
+
originalSeriesKey: 'Value'
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
expect(styles.map(style => style.strokeDasharray)).toEqual([0, 0])
|
|
88
167
|
})
|
|
89
168
|
|
|
90
169
|
it('should apply solid style when no effect matches', () => {
|
|
@@ -111,7 +190,7 @@ describe('LineChart helpers', () => {
|
|
|
111
190
|
})
|
|
112
191
|
|
|
113
192
|
it('should only match effects for the correct series', () => {
|
|
114
|
-
// Using
|
|
193
|
+
// Using an unconfigured series key with COVID data should not match
|
|
115
194
|
const styles = createStyles({
|
|
116
195
|
preliminaryData: basePreliminaryData,
|
|
117
196
|
data: dynamicCategoryData,
|
|
@@ -119,14 +198,12 @@ describe('LineChart helpers', () => {
|
|
|
119
198
|
strokeWidth: 2,
|
|
120
199
|
handleLineType: mockHandleLineType,
|
|
121
200
|
lineType: 'solid-line',
|
|
122
|
-
seriesKey: '
|
|
201
|
+
seriesKey: 'RSV', // Series not configured for an effect
|
|
123
202
|
dynamicCategory: 'Category',
|
|
124
203
|
originalSeriesKey: 'Value'
|
|
125
204
|
})
|
|
126
205
|
|
|
127
|
-
|
|
128
|
-
// (assuming we use the COVID effect config)
|
|
129
|
-
expect(styles.every(s => s.strokeDasharray === 0 || s.strokeDasharray === '10 5')).toBe(true)
|
|
206
|
+
expect(styles.every(style => style.strokeDasharray === 0)).toBe(true)
|
|
130
207
|
})
|
|
131
208
|
})
|
|
132
209
|
|
|
@@ -134,6 +211,7 @@ describe('LineChart helpers', () => {
|
|
|
134
211
|
const multiColumnPreliminaryData: PreliminaryDataItem[] = [
|
|
135
212
|
{
|
|
136
213
|
type: 'effect',
|
|
214
|
+
lineEffectScope: 'outgoing',
|
|
137
215
|
seriesKeys: ['COVID-19'],
|
|
138
216
|
column: 'COVID-19-Attribute',
|
|
139
217
|
value: 'Dotted',
|
|
@@ -153,12 +231,12 @@ describe('LineChart helpers', () => {
|
|
|
153
231
|
]
|
|
154
232
|
|
|
155
233
|
const multiColumnData = [
|
|
156
|
-
{ Date: '10/5/2025', 'COVID-19': '43.6', 'COVID-19-Attribute': '
|
|
157
|
-
{ Date: '10/12/2025', 'COVID-19': '40.7', 'COVID-19-Attribute': '' },
|
|
158
|
-
{ Date: '10/19/2025', 'COVID-19': '42.6', 'COVID-19-Attribute': '
|
|
234
|
+
{ Date: '10/5/2025', 'COVID-19': '43.6', 'COVID-19-Attribute': '' },
|
|
235
|
+
{ Date: '10/12/2025', 'COVID-19': '40.7', 'COVID-19-Attribute': 'Dotted' },
|
|
236
|
+
{ Date: '10/19/2025', 'COVID-19': '42.6', 'COVID-19-Attribute': '' }
|
|
159
237
|
]
|
|
160
238
|
|
|
161
|
-
it('should apply
|
|
239
|
+
it('should apply an isolated effect only to the segment leaving the matching point', () => {
|
|
162
240
|
const styles = createStyles({
|
|
163
241
|
preliminaryData: multiColumnPreliminaryData,
|
|
164
242
|
data: multiColumnData,
|
|
@@ -172,12 +250,7 @@ describe('LineChart helpers', () => {
|
|
|
172
250
|
})
|
|
173
251
|
|
|
174
252
|
expect(styles).toHaveLength(3)
|
|
175
|
-
|
|
176
|
-
expect(styles[0].strokeDasharray).toBe('5 5')
|
|
177
|
-
// Second point inherits from third point's effect
|
|
178
|
-
expect(styles[1].strokeDasharray).toBe('5 5')
|
|
179
|
-
// Third point has attribute - dashed
|
|
180
|
-
expect(styles[2].strokeDasharray).toBe('5 5')
|
|
253
|
+
expect(styles.map(style => style.strokeDasharray)).toEqual([0, '5 5', 0])
|
|
181
254
|
})
|
|
182
255
|
})
|
|
183
256
|
|
|
@@ -186,6 +259,7 @@ describe('LineChart helpers', () => {
|
|
|
186
259
|
const preliminaryDataWithWeight: PreliminaryDataItem[] = [
|
|
187
260
|
{
|
|
188
261
|
type: 'effect',
|
|
262
|
+
lineEffectScope: 'outgoing',
|
|
189
263
|
seriesKeys: ['COVID-19'],
|
|
190
264
|
column: 'Attribute',
|
|
191
265
|
value: 'Dotted',
|
|
@@ -206,9 +280,9 @@ describe('LineChart helpers', () => {
|
|
|
206
280
|
]
|
|
207
281
|
|
|
208
282
|
const data = [
|
|
209
|
-
{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: '
|
|
210
|
-
{ Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: '' },
|
|
211
|
-
{ Date: '10/19/2025', Category: 'COVID-19', Value: '42.6', Attribute: '
|
|
283
|
+
{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: '' },
|
|
284
|
+
{ Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: 'Dotted' },
|
|
285
|
+
{ Date: '10/19/2025', Category: 'COVID-19', Value: '42.6', Attribute: '' }
|
|
212
286
|
]
|
|
213
287
|
|
|
214
288
|
const styles = createStyles({
|
|
@@ -224,18 +298,15 @@ describe('LineChart helpers', () => {
|
|
|
224
298
|
})
|
|
225
299
|
|
|
226
300
|
expect(styles).toHaveLength(3)
|
|
227
|
-
|
|
228
|
-
expect(styles
|
|
229
|
-
// Second point inherits custom weight from next point's effect
|
|
230
|
-
expect(styles[1].strokeWidth).toBe(5)
|
|
231
|
-
// Third point has effect with custom weight
|
|
232
|
-
expect(styles[2].strokeWidth).toBe(5)
|
|
301
|
+
expect(styles.map(style => style.strokeWidth)).toEqual([2, 5, 2])
|
|
302
|
+
expect(styles.map(style => style.strokeDasharray)).toEqual([0, '5 5', 0])
|
|
233
303
|
})
|
|
234
304
|
|
|
235
305
|
it('should use default strokeWidth when preliminaryData has no weight', () => {
|
|
236
306
|
const preliminaryDataNoWeight: PreliminaryDataItem[] = [
|
|
237
307
|
{
|
|
238
308
|
type: 'effect',
|
|
309
|
+
lineEffectScope: 'outgoing',
|
|
239
310
|
seriesKeys: ['COVID-19'],
|
|
240
311
|
column: 'Attribute',
|
|
241
312
|
value: 'Dotted',
|
|
@@ -287,6 +358,7 @@ describe('LineChart helpers', () => {
|
|
|
287
358
|
const preliminaryData: PreliminaryDataItem[] = [
|
|
288
359
|
{
|
|
289
360
|
type: 'effect',
|
|
361
|
+
lineEffectScope: 'outgoing',
|
|
290
362
|
seriesKeys: ['COVID-19'],
|
|
291
363
|
column: 'Attribute',
|
|
292
364
|
value: 'Dotted',
|
|
@@ -306,7 +378,10 @@ describe('LineChart helpers', () => {
|
|
|
306
378
|
}
|
|
307
379
|
]
|
|
308
380
|
|
|
309
|
-
const data = [
|
|
381
|
+
const data = [
|
|
382
|
+
{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: 'Dotted' },
|
|
383
|
+
{ Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: '' }
|
|
384
|
+
]
|
|
310
385
|
|
|
311
386
|
const styles = createStyles({
|
|
312
387
|
preliminaryData,
|
|
@@ -320,7 +395,7 @@ describe('LineChart helpers', () => {
|
|
|
320
395
|
originalSeriesKey: 'Value'
|
|
321
396
|
})
|
|
322
397
|
|
|
323
|
-
expect(styles).toHaveLength(
|
|
398
|
+
expect(styles).toHaveLength(2)
|
|
324
399
|
expect(styles[0].strokeWidth).toBe(expected)
|
|
325
400
|
})
|
|
326
401
|
})
|
|
@@ -570,5 +645,18 @@ describe('LineChart helpers', () => {
|
|
|
570
645
|
expect(suppressedSegments.length).toBeGreaterThan(0)
|
|
571
646
|
expect(suppressedSegments.every(segment => segment.weight === undefined)).toBe(true)
|
|
572
647
|
})
|
|
648
|
+
|
|
649
|
+
it('returns no segments when a brushed dynamic-category series has no rows', () => {
|
|
650
|
+
const segments = createDataSegments({
|
|
651
|
+
data: [],
|
|
652
|
+
seriesKey: 'Influenza',
|
|
653
|
+
preliminaryData: [makeSuppression({ seriesKeys: ['Influenza'] })],
|
|
654
|
+
dynamicCategory: 'Category',
|
|
655
|
+
originalSeriesKey: 'Value',
|
|
656
|
+
colorScale: mockColorScale
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
expect(segments).toEqual([])
|
|
660
|
+
})
|
|
573
661
|
})
|
|
574
662
|
})
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { DataItem, StyleProps, Style } from './LineChartProps'
|
|
2
2
|
import { PreliminaryDataItem } from '../../types/ChartConfig'
|
|
3
3
|
|
|
4
|
-
import _ from 'lodash'
|
|
5
|
-
|
|
6
4
|
const isCalculable = value => !isNaN(parseFloat(value)) && isFinite(value)
|
|
7
5
|
|
|
8
6
|
export const createStyles = (props: StyleProps): Style[] => {
|
|
@@ -57,16 +55,19 @@ export const createStyles = (props: StyleProps): Style[] => {
|
|
|
57
55
|
}
|
|
58
56
|
|
|
59
57
|
data.forEach((d, index) => {
|
|
60
|
-
const matchingPd
|
|
58
|
+
const matchingPd = getMatchingPd(d)
|
|
59
|
+
const preserveAdjacentSegments =
|
|
60
|
+
matchingPd && !matchingPd.style.includes('Circles') && matchingPd.lineEffectScope !== 'outgoing'
|
|
61
|
+
const hasStyledSegment = matchingPd && (preserveAdjacentSegments || index < data.length - 1)
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
const style: Style = hasStyledSegment
|
|
63
64
|
? createStyle(handleLineType(matchingPd.style), matchingPd.weight)
|
|
64
65
|
: createStyle(handleLineType(lineType))
|
|
65
66
|
|
|
66
67
|
styles.push(style)
|
|
67
68
|
|
|
68
|
-
//
|
|
69
|
-
if (
|
|
69
|
+
// Existing rules also style the incoming segment, including a preliminary final point.
|
|
70
|
+
if (preserveAdjacentSegments && index > 0) {
|
|
70
71
|
styles[index - 1] = createStyle(handleLineType(matchingPd.style), matchingPd.weight)
|
|
71
72
|
}
|
|
72
73
|
})
|
|
@@ -299,6 +300,8 @@ export const createDataSegments = props => {
|
|
|
299
300
|
|
|
300
301
|
return (props.preliminaryData ?? []).some(pd => d?.[props.dynamicCategory] === props?.seriesKey)
|
|
301
302
|
})
|
|
303
|
+
if (dynamicData.length === 0) return []
|
|
304
|
+
|
|
302
305
|
const isSuppressed = (pd, dataItem) => {
|
|
303
306
|
if (pd.type === 'effect' || pd.hideLineStyle) return false
|
|
304
307
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext } from 'react'
|
|
1
|
+
import React, { useContext, useId } from 'react'
|
|
2
2
|
|
|
3
3
|
// VisX library imports
|
|
4
4
|
import * as allCurves from '@visx/curve'
|
|
@@ -26,6 +26,7 @@ import { type ChartContext } from '../../types/ChartContext'
|
|
|
26
26
|
import { type LineChartProps } from './LineChartProps'
|
|
27
27
|
|
|
28
28
|
const LineChart = (props: LineChartProps) => {
|
|
29
|
+
const lineRaceMaskPrefix = `line-race-reveal-${useId().replace(/[^a-zA-Z0-9_-]/g, '')}`
|
|
29
30
|
// prettier-ignore
|
|
30
31
|
const {
|
|
31
32
|
getXAxisData,
|
|
@@ -41,10 +42,12 @@ const LineChart = (props: LineChartProps) => {
|
|
|
41
42
|
} = props
|
|
42
43
|
|
|
43
44
|
const {
|
|
45
|
+
brushData,
|
|
44
46
|
colorScale,
|
|
45
47
|
config,
|
|
46
48
|
formatNumber,
|
|
47
49
|
handleLineType,
|
|
50
|
+
lineRaceProgress,
|
|
48
51
|
parseDate,
|
|
49
52
|
seriesHighlight,
|
|
50
53
|
tableData,
|
|
@@ -60,16 +63,21 @@ const LineChart = (props: LineChartProps) => {
|
|
|
60
63
|
})
|
|
61
64
|
const { yScaleRight } = useRightAxis({ config, yMax, data: dataForRightAxis, updateConfig })
|
|
62
65
|
const showSingleSeries = config.tooltips.singleSeries
|
|
66
|
+
const isLineRace = typeof lineRaceProgress === 'number'
|
|
67
|
+
const normalizedLineRaceProgress =
|
|
68
|
+
isLineRace && Number.isFinite(lineRaceProgress) ? Math.min(Math.max(lineRaceProgress, 0), 1) : 0
|
|
63
69
|
if (!handleTooltipMouseOver) return
|
|
64
70
|
|
|
65
71
|
const DEBUG = false
|
|
66
72
|
const { lineDatapointStyle, showLineSeriesLabels, legend } = config
|
|
73
|
+
const activeRawData = Array.isArray(brushData) && brushData.length > 0 ? brushData : tableData
|
|
67
74
|
|
|
68
75
|
const xPos = d => {
|
|
69
76
|
return xScale(getXAxisData(d)) + (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
const tooltipPoints = []
|
|
80
|
+
const hasConfidenceIntervalBand = Boolean(config.confidenceKeys?.lower && config.confidenceKeys?.upper)
|
|
73
81
|
|
|
74
82
|
return (
|
|
75
83
|
<ErrorBoundary component='LineChart'>
|
|
@@ -79,12 +87,21 @@ const LineChart = (props: LineChartProps) => {
|
|
|
79
87
|
const seriesData = config.runtime.series.find(item => item.dataKey === seriesKey)
|
|
80
88
|
const _seriesKey = seriesData.dynamicCategory ? seriesData.originalDataKey : seriesKey
|
|
81
89
|
const lineType = seriesData.type
|
|
90
|
+
const lineRaceMaskId = `${lineRaceMaskPrefix}-${index}`
|
|
82
91
|
const seriesAxis = seriesData.axis || 'left'
|
|
92
|
+
const seriesColor = colorScale(config.runtime.seriesLabels[seriesKey])
|
|
83
93
|
const displayArea =
|
|
84
94
|
legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1
|
|
85
95
|
|
|
96
|
+
const _data = seriesData.dynamicCategory
|
|
97
|
+
? data.filter(d => d[seriesData.dynamicCategory] === seriesKey)
|
|
98
|
+
: data
|
|
99
|
+
const activeRawSeriesData = seriesData.dynamicCategory
|
|
100
|
+
? activeRawData.filter(d => d[seriesData.dynamicCategory] === seriesKey)
|
|
101
|
+
: activeRawData
|
|
102
|
+
const pointData = isLineRace ? activeRawSeriesData : _data
|
|
86
103
|
const suppressedSegments = createDataSegments({
|
|
87
|
-
data:
|
|
104
|
+
data: activeRawSeriesData,
|
|
88
105
|
seriesKey,
|
|
89
106
|
preliminaryData: config.preliminaryData,
|
|
90
107
|
dynamicCategory: seriesData.dynamicCategory,
|
|
@@ -94,30 +111,28 @@ const LineChart = (props: LineChartProps) => {
|
|
|
94
111
|
const isSplitLine =
|
|
95
112
|
config?.preliminaryData?.filter(pd => pd.style && !pd.style.includes('Circles')).length > 0
|
|
96
113
|
|
|
97
|
-
const _data = seriesData.dynamicCategory
|
|
98
|
-
? data.filter(d => d[seriesData.dynamicCategory] === seriesKey)
|
|
99
|
-
: data
|
|
100
114
|
const circleData = filterCircles(
|
|
101
115
|
config?.preliminaryData,
|
|
102
|
-
|
|
116
|
+
activeRawSeriesData,
|
|
103
117
|
seriesKey,
|
|
104
118
|
seriesData.dynamicCategory,
|
|
105
119
|
_seriesKey
|
|
106
120
|
)
|
|
107
121
|
|
|
108
122
|
// Prepare sorted data for line and area rendering
|
|
109
|
-
const sortedData =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
const sortedData = isLineRace
|
|
124
|
+
? [..._data].sort((d1, d2) => xPos(d1) - xPos(d2))
|
|
125
|
+
: config.visualizationType == 'Bump Chart'
|
|
126
|
+
? _data
|
|
127
|
+
: config.xAxis.type === 'date-time' || config.xAxis.type === 'date'
|
|
128
|
+
? _data.sort((d1, d2) => {
|
|
129
|
+
let x1 = getXAxisData(d1)
|
|
130
|
+
let x2 = getXAxisData(d2)
|
|
131
|
+
if (x1 < x2) return -1
|
|
132
|
+
if (x2 < x1) return 1
|
|
133
|
+
return 0
|
|
134
|
+
})
|
|
135
|
+
: _data
|
|
121
136
|
|
|
122
137
|
return (
|
|
123
138
|
<Group
|
|
@@ -145,7 +160,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
145
160
|
fill={DEBUG ? 'red' : 'transparent'}
|
|
146
161
|
fillOpacity={0.05}
|
|
147
162
|
/>
|
|
148
|
-
{
|
|
163
|
+
{pointData.map((d, dataIndex) => {
|
|
149
164
|
tooltipPoints.push({
|
|
150
165
|
color: colorScale(config.runtime.seriesLabels[seriesKey]),
|
|
151
166
|
seriesKey: _seriesKey,
|
|
@@ -155,7 +170,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
155
170
|
})
|
|
156
171
|
|
|
157
172
|
// Build array of point coordinates for intelligent label positioning
|
|
158
|
-
const dataPointsForSeries =
|
|
173
|
+
const dataPointsForSeries = pointData
|
|
159
174
|
.filter(item => isNumber(item[_seriesKey]))
|
|
160
175
|
.map(item => ({
|
|
161
176
|
x: xPos(item),
|
|
@@ -212,10 +227,11 @@ const LineChart = (props: LineChartProps) => {
|
|
|
212
227
|
mode='ALWAYS_SHOW_POINTS'
|
|
213
228
|
dataIndex={dataIndex}
|
|
214
229
|
tableData={tableData}
|
|
215
|
-
data={
|
|
230
|
+
data={pointData}
|
|
216
231
|
d={d}
|
|
217
232
|
config={config}
|
|
218
233
|
seriesKey={_seriesKey}
|
|
234
|
+
seriesColor={seriesColor}
|
|
219
235
|
displayArea={displayArea}
|
|
220
236
|
tooltipData={tooltipData}
|
|
221
237
|
xScale={xScale}
|
|
@@ -234,10 +250,11 @@ const LineChart = (props: LineChartProps) => {
|
|
|
234
250
|
mode='HOVER_POINTS'
|
|
235
251
|
dataIndex={dataIndex}
|
|
236
252
|
tableData={tableData}
|
|
237
|
-
data={
|
|
253
|
+
data={pointData}
|
|
238
254
|
d={d}
|
|
239
255
|
config={config}
|
|
240
256
|
seriesKey={_seriesKey}
|
|
257
|
+
seriesColor={seriesColor}
|
|
241
258
|
displayArea={displayArea}
|
|
242
259
|
xScale={xScale}
|
|
243
260
|
yScale={yScale}
|
|
@@ -255,10 +272,11 @@ const LineChart = (props: LineChartProps) => {
|
|
|
255
272
|
seriesIndex={index}
|
|
256
273
|
dataIndex={dataIndex}
|
|
257
274
|
tableData={tableData}
|
|
258
|
-
data={
|
|
275
|
+
data={pointData}
|
|
259
276
|
d={d}
|
|
260
277
|
config={config}
|
|
261
278
|
seriesKey={_seriesKey}
|
|
279
|
+
seriesColor={seriesColor}
|
|
262
280
|
displayArea={displayArea}
|
|
263
281
|
xScale={xScale}
|
|
264
282
|
yScale={yScale}
|
|
@@ -336,7 +354,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
336
354
|
) : (
|
|
337
355
|
<>
|
|
338
356
|
{/* Confidence Interval Band */}
|
|
339
|
-
{
|
|
357
|
+
{hasConfidenceIntervalBand &&
|
|
340
358
|
config.series.map((seriesData, seriesKey) => {
|
|
341
359
|
if (seriesData.dynamicCategory) {
|
|
342
360
|
// Get unique categories from the data
|
|
@@ -383,7 +401,31 @@ const LineChart = (props: LineChartProps) => {
|
|
|
383
401
|
})}
|
|
384
402
|
|
|
385
403
|
{/* STANDARD LINE */}
|
|
404
|
+
{isLineRace && (
|
|
405
|
+
<mask id={lineRaceMaskId} maskUnits='userSpaceOnUse'>
|
|
406
|
+
<LinePath
|
|
407
|
+
data-line-race-reveal-path
|
|
408
|
+
pathLength={1}
|
|
409
|
+
curve={allCurves[seriesData.lineType]}
|
|
410
|
+
data={sortedData}
|
|
411
|
+
x={d => xPos(d)}
|
|
412
|
+
y={d =>
|
|
413
|
+
seriesAxis === 'Right'
|
|
414
|
+
? yScaleRight(getYAxisData(d, _seriesKey))
|
|
415
|
+
: yScale(Number(getYAxisData(d, _seriesKey)))
|
|
416
|
+
}
|
|
417
|
+
fill='none'
|
|
418
|
+
stroke='white'
|
|
419
|
+
strokeWidth={(seriesData.weight || 2) + 2}
|
|
420
|
+
strokeDasharray={`${normalizedLineRaceProgress} 1`}
|
|
421
|
+
defined={(item, i) => {
|
|
422
|
+
return item[_seriesKey] !== '' && item[_seriesKey] !== null && item[_seriesKey] !== undefined
|
|
423
|
+
}}
|
|
424
|
+
/>
|
|
425
|
+
</mask>
|
|
426
|
+
)}
|
|
386
427
|
<LinePath
|
|
428
|
+
data-line-race-series-path={isLineRace ? true : undefined}
|
|
387
429
|
curve={allCurves[seriesData.lineType]}
|
|
388
430
|
data={sortedData}
|
|
389
431
|
x={d => xPos(d)}
|
|
@@ -397,6 +439,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
397
439
|
strokeOpacity={1}
|
|
398
440
|
shapeRendering='geometricPrecision'
|
|
399
441
|
strokeDasharray={lineType ? handleLineType(lineType) : 0}
|
|
442
|
+
mask={isLineRace ? `url(#${lineRaceMaskId})` : undefined}
|
|
400
443
|
defined={(item, i) => {
|
|
401
444
|
return item[_seriesKey] !== '' && item[_seriesKey] !== null && item[_seriesKey] !== undefined
|
|
402
445
|
}}
|
|
@@ -473,6 +516,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
473
516
|
)}
|
|
474
517
|
{/* Render series labels at end if each line if selected in the editor */}
|
|
475
518
|
{showLineSeriesLabels &&
|
|
519
|
+
(!isLineRace || lineRaceProgress >= 1) &&
|
|
476
520
|
(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map(seriesKey => {
|
|
477
521
|
let lastDatum
|
|
478
522
|
for (let i = _data.length - 1; i >= 0; i--) {
|
|
@@ -481,9 +525,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
481
525
|
break
|
|
482
526
|
}
|
|
483
527
|
}
|
|
484
|
-
if (!lastDatum || legend.position === 'right')
|
|
485
|
-
return <></>
|
|
486
|
-
}
|
|
528
|
+
if (!lastDatum || legend.position === 'right') return null
|
|
487
529
|
|
|
488
530
|
let labelText = config.runtime.seriesLabels[seriesKey] || seriesKey
|
|
489
531
|
// truncate labels longer that 10 chars
|
|
@@ -494,6 +536,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
494
536
|
|
|
495
537
|
return (
|
|
496
538
|
<Text
|
|
539
|
+
key={seriesKey}
|
|
497
540
|
display={
|
|
498
541
|
legend.behavior === 'highlight' ||
|
|
499
542
|
(seriesHighlight.length === 0 && !legend.dynamicLegend) ||
|
|
@@ -541,6 +584,7 @@ const LineChart = (props: LineChartProps) => {
|
|
|
541
584
|
<React.Fragment key={`series-${seriesKey}-point-${dataIndex}`}>
|
|
542
585
|
<LineChartCircle
|
|
543
586
|
mode='TOOLTIP_POINTS'
|
|
587
|
+
seriesColor={color}
|
|
544
588
|
dataIndex={dataIndex}
|
|
545
589
|
tooltipPoint={d}
|
|
546
590
|
tableData={tableData}
|