@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.
Files changed (276) hide show
  1. package/CONFIG.md +504 -368
  2. package/dist/cdcchart-CYc-Kayd.es.js +22488 -0
  3. package/dist/cdcchart-DCKn8NmI.es.js +20695 -0
  4. package/dist/cdcchart-Dyui9nk_.es.js +45828 -0
  5. package/dist/cdcchart.js +4 -96498
  6. package/examples/__data__/vega-remote-data.json +6 -0
  7. package/examples/dendrogram.json +18 -0
  8. package/examples/feature/bar/bar-chart-race-annual-change.json +68 -0
  9. package/examples/feature/heatmap/average-age-categorical.json +2 -2
  10. package/examples/feature/heatmap/calendar-heatmap.json +2 -2
  11. package/examples/feature/heatmap/cell-values-bottom-axis.json +2 -2
  12. package/examples/feature/heatmap/sparse-aggregation.json +2 -2
  13. package/examples/feature/line/line-chart-race.json +50 -0
  14. package/examples/feature/sankey/sankey-example-data.json +40 -1441
  15. package/examples/feature/sankey/sankey_chart_data.csv +9 -20
  16. package/examples/network.json +31 -0
  17. package/examples/private/box-plot.json +573 -0
  18. package/examples/private/custom-color-issue.json +625 -0
  19. package/examples/private/custom-colors.json +522 -0
  20. package/examples/private/cutoff.json +10750 -0
  21. package/examples/private/flueview.json +22051 -0
  22. package/examples/private/overlay-legend.json +485 -0
  23. package/examples/private/pie-test.json +469 -0
  24. package/examples/vega-remote.json +22 -0
  25. package/package.json +11 -13
  26. package/src/CdcChart.tsx +2 -2
  27. package/src/CdcChartComponent.tsx +444 -217
  28. package/src/_stories/Chart.BarRace.stories.tsx +443 -0
  29. package/src/_stories/Chart.BoxPlotNumericSort.stories.tsx +189 -0
  30. package/src/_stories/Chart.BoxPlotStatsParity.stories.tsx +207 -0
  31. package/src/_stories/Chart.ColorDistribution.stories.tsx +299 -0
  32. package/src/_stories/Chart.DataCutoff.stories.tsx +147 -0
  33. package/src/_stories/Chart.DataTableDownloadFilename.stories.tsx +60 -0
  34. package/src/_stories/Chart.Dendrogram.stories.tsx +373 -0
  35. package/src/_stories/Chart.Forecast.CILegend.stories.tsx +236 -0
  36. package/src/_stories/Chart.HeatMap.stories.tsx +170 -4
  37. package/src/_stories/Chart.Legend.AxisAlign.stories.tsx +119 -0
  38. package/src/_stories/Chart.LineRace.stories.tsx +149 -0
  39. package/src/_stories/Chart.Network.stories.tsx +432 -0
  40. package/src/_stories/Chart.PaletteConversion.Editor.stories.tsx +208 -0
  41. package/src/_stories/Chart.Patterns.smoke.stories.tsx +31 -0
  42. package/src/_stories/Chart.PercentageValue.stories.tsx +311 -0
  43. package/src/_stories/Chart.PieLabels.stories.tsx +49 -0
  44. package/src/_stories/Chart.RankByValueMutation.stories.tsx +206 -0
  45. package/src/_stories/Chart.RequiredFields.stories.tsx +681 -0
  46. package/src/_stories/Chart.Sankey.stories.tsx +417 -0
  47. package/src/_stories/Chart.SeriesColorAssignments.stories.tsx +47 -0
  48. package/src/_stories/Chart.SmallMultiplesNumericTileOrder.stories.tsx +137 -0
  49. package/src/_stories/Chart.TooltipSeriesLabels.stories.tsx +43 -0
  50. package/src/_stories/Chart.VegaRemoteData.stories.tsx +31 -0
  51. package/src/_stories/Chart.smoke.stories.tsx +14 -1
  52. package/src/_stories/ChartAnnotation.Editor.stories.tsx +78 -0
  53. package/src/_stories/ChartAxisTitles.smoke.stories.tsx +48 -0
  54. package/src/_stories/ChartBar.Editor.stories.tsx +499 -57
  55. package/src/_stories/ChartBrush.Editor.stories.tsx +46 -67
  56. package/src/_stories/ChartBrush.Regression.stories.tsx +135 -0
  57. package/src/_stories/ChartEditor.Editor.stories.tsx +33 -3
  58. package/src/_stories/ChartLine.EffectCompatibility.stories.tsx +120 -0
  59. package/src/_stories/ChartPrefixSuffix.smoke.stories.tsx +28 -1
  60. package/src/_stories/_mock/annotation_event-line_mock.json +2 -2
  61. package/src/_stories/_mock/editor-test-configs.json +20 -10
  62. package/src/_stories/_mock/editor-test-datasets.json +38 -42
  63. package/src/_stories/_mock/heatmap-average-age-categorical.json +2 -2
  64. package/src/_stories/_mock/heatmap.json +2 -2
  65. package/src/_stories/_mock/legend-axis-align-stacked-bar.json +125 -0
  66. package/src/_stories/_mock/sankey_mock.json +23 -27
  67. package/src/components/Annotations/components/AnnotationDraggable.styles.css +52 -0
  68. package/src/components/Annotations/components/AnnotationDraggable.tsx +639 -139
  69. package/src/components/Annotations/components/AnnotationDropdown.styles.css +44 -4
  70. package/src/components/Annotations/components/AnnotationDropdown.tsx +10 -32
  71. package/src/components/Annotations/helpers/resolveAnnotationLayout.ts +212 -0
  72. package/src/components/Annotations/tests/AnnotationDraggable.test.tsx +612 -19
  73. package/src/components/Annotations/tests/AnnotationDropdown.test.tsx +50 -0
  74. package/src/components/Annotations/tests/resolveAnnotationLayout.test.ts +203 -0
  75. package/src/components/Axis/BottomAxis.tsx +1 -1
  76. package/src/components/Axis/Categorical.Axis.tsx +3 -3
  77. package/src/components/Axis/RightAxis.tsx +21 -13
  78. package/src/components/BarChart/components/BarChart.Horizontal.tsx +39 -8
  79. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +13 -4
  80. package/src/components/BarChart/components/BarChart.StackedVertical.test.ts +28 -0
  81. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +30 -26
  82. package/src/components/BarChart/components/BarChart.Vertical.tsx +38 -28
  83. package/src/components/BarChart/components/PortionPatternOverlay.tsx +61 -0
  84. package/src/components/BarChart/components/context.tsx +6 -0
  85. package/src/components/BarChart/helpers/getBarData.ts +28 -28
  86. package/src/components/BarChart/helpers/getBarHeights.ts +2 -2
  87. package/src/components/BarChart/helpers/getPatternUrl.ts +2 -5
  88. package/src/components/BarChart/helpers/index.ts +8 -0
  89. package/src/components/BarChart/helpers/portionPattern.ts +243 -0
  90. package/src/components/BarChart/helpers/tests/addMinimumBarHeights.test.ts +42 -0
  91. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +13 -0
  92. package/src/components/BarChart/helpers/tests/portionPattern.test.ts +280 -0
  93. package/src/components/BarChart/helpers/tests/useBarChart.test.tsx +202 -0
  94. package/src/components/BarChart/helpers/useBarChart.ts +60 -60
  95. package/src/components/BarChartRace/BarChartRace.test.tsx +199 -0
  96. package/src/components/BarChartRace/BarChartRace.tsx +230 -0
  97. package/src/components/BarChartRace/BarChartRaceFallback.test.tsx +61 -0
  98. package/src/components/BarChartRace/BarChartRaceFallback.tsx +48 -0
  99. package/src/components/BarChartRace/bar-chart-race.css +160 -0
  100. package/src/components/BarChartRace/helpers.test.ts +114 -0
  101. package/src/components/BarChartRace/helpers.ts +185 -0
  102. package/src/components/BarChartRace/index.ts +3 -0
  103. package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +7 -6
  104. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +9 -7
  105. package/src/components/BoxPlot/helpers/index.ts +13 -83
  106. package/src/components/Brush/BrushSelector.tsx +10 -24
  107. package/src/components/Brush/MiniChartPreview.tsx +33 -1
  108. package/src/components/Dendrogram/components/Dendrogram.tsx +374 -0
  109. package/src/components/Dendrogram/dendrogram.css +113 -0
  110. package/src/components/Dendrogram/helpers/layoutDendrogram.test.ts +95 -0
  111. package/src/components/Dendrogram/helpers/layoutDendrogram.ts +56 -0
  112. package/src/components/Dendrogram/helpers/prepareDendrogramData.test.ts +135 -0
  113. package/src/components/Dendrogram/helpers/prepareDendrogramData.ts +166 -0
  114. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.test.ts +196 -0
  115. package/src/components/Dendrogram/helpers/resolveDendrogramLabels.ts +418 -0
  116. package/src/components/Dendrogram/index.tsx +1 -0
  117. package/src/components/Dendrogram/types/index.ts +66 -0
  118. package/src/components/EditorPanel/EditorPanel.tsx +1111 -805
  119. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +50 -5
  120. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.css +10 -0
  121. package/src/components/EditorPanel/components/Panels/Panel.Dendrogram.tsx +211 -0
  122. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +19 -8
  123. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +104 -61
  124. package/src/components/EditorPanel/components/Panels/Panel.Network.css +9 -0
  125. package/src/components/EditorPanel/components/Panels/Panel.Network.tsx +202 -0
  126. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +236 -36
  127. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +6 -1
  128. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +89 -105
  129. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +21 -6
  130. package/src/components/EditorPanel/components/Panels/Panel.SmallMultiples.tsx +1 -1
  131. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +8 -66
  132. package/src/components/EditorPanel/components/Panels/RacingControls.test.tsx +131 -0
  133. package/src/components/EditorPanel/components/Panels/RacingControls.tsx +185 -0
  134. package/src/components/EditorPanel/components/Panels/SeriesColorAssignments.tsx +2 -2
  135. package/src/components/EditorPanel/components/Panels/index.tsx +4 -0
  136. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +20 -7
  137. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +25 -2
  138. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +5 -4
  139. package/src/components/EditorPanel/useEditorPermissions.test.tsx +94 -0
  140. package/src/components/EditorPanel/useEditorPermissions.ts +75 -25
  141. package/src/components/Forecasting/Forecasting.tsx +16 -7
  142. package/src/components/HeatMap/components/HeatMap.tsx +511 -178
  143. package/src/components/HeatMap/heatmap.constants.ts +7 -3
  144. package/src/components/HeatMap/heatmap.css +101 -2
  145. package/src/components/HeatMap/helpers/layout.ts +80 -0
  146. package/src/components/HeatMap/tests/HeatMap.test.tsx +339 -16
  147. package/src/components/HeatMap/tests/helpers.test.ts +79 -0
  148. package/src/components/Legend/Legend.Component.tsx +163 -148
  149. package/src/components/Legend/Legend.Suppression.tsx +3 -3
  150. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +7 -0
  151. package/src/components/Legend/LegendGroup/LegendGroup.tsx +47 -35
  152. package/src/components/Legend/helpers/createFormatLabels.test.ts +226 -0
  153. package/src/components/Legend/helpers/createFormatLabels.tsx +45 -51
  154. package/src/components/LineChart/components/LineChart.Circle.tsx +3 -3
  155. package/src/components/LineChart/helpers.test.ts +126 -38
  156. package/src/components/LineChart/helpers.ts +9 -6
  157. package/src/components/LineChart/index.tsx +71 -27
  158. package/src/components/LineChart/tests/LineChart.test.tsx +630 -0
  159. package/src/components/LineChartRace/LineChartRace.test.tsx +275 -0
  160. package/src/components/LineChartRace/LineChartRace.tsx +134 -0
  161. package/src/components/LineChartRace/LineChartRaceFallback.tsx +28 -0
  162. package/src/components/LineChartRace/helpers.test.ts +88 -0
  163. package/src/components/LineChartRace/helpers.ts +157 -0
  164. package/src/components/LineChartRace/index.ts +3 -0
  165. package/src/components/LineChartRace/line-chart-race.css +16 -0
  166. package/src/components/LinearChart/ValueAxisAnchors.tsx +58 -0
  167. package/src/components/LinearChart/VisualizationRenderer.tsx +18 -23
  168. package/src/components/LinearChart/tests/LinearChart.test.tsx +412 -13
  169. package/src/components/LinearChart/tests/VisualizationRenderer.test.tsx +141 -0
  170. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -1
  171. package/src/components/LinearChart.tsx +85 -54
  172. package/src/components/Network/components/Network.tsx +372 -0
  173. package/src/components/Network/helpers/layoutNetwork.test.ts +68 -0
  174. package/src/components/Network/helpers/layoutNetwork.ts +87 -0
  175. package/src/components/Network/helpers/prepareNetworkData.test.ts +116 -0
  176. package/src/components/Network/helpers/prepareNetworkData.ts +101 -0
  177. package/src/components/Network/helpers/resolveNetworkLabels.test.ts +112 -0
  178. package/src/components/Network/helpers/resolveNetworkLabels.ts +206 -0
  179. package/src/components/Network/index.tsx +3 -0
  180. package/src/components/Network/network.css +107 -0
  181. package/src/components/Network/types/index.ts +56 -0
  182. package/src/components/PairedBarChart.jsx +5 -10
  183. package/src/components/PairedBarChart.test.tsx +71 -0
  184. package/src/components/PieChart/PieChart.tsx +121 -13
  185. package/src/components/PieChart/helpers/labelPlacement.test.ts +88 -0
  186. package/src/components/PieChart/helpers/labelPlacement.ts +95 -0
  187. package/src/components/RacingChartRenderer.tsx +44 -0
  188. package/src/components/Regions/components/Regions.test.tsx +179 -0
  189. package/src/components/Regions/components/Regions.tsx +104 -69
  190. package/src/components/Regions/index.tsx +2 -3
  191. package/src/components/Sankey/components/Sankey.tsx +264 -437
  192. package/src/components/Sankey/helpers/getSankeyTooltip.ts +12 -0
  193. package/src/components/Sankey/helpers/prepareSankeyData.test.ts +71 -0
  194. package/src/components/Sankey/helpers/prepareSankeyData.ts +90 -0
  195. package/src/components/Sankey/sankey.scss +57 -116
  196. package/src/components/Sankey/types/index.ts +41 -4
  197. package/src/components/ScatterPlot/ScatterPlot.jsx +28 -12
  198. package/src/components/ScatterPlot/ScatterPlot.test.tsx +60 -0
  199. package/src/components/SmallMultiples/SmallMultiples.tsx +12 -12
  200. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +7 -5
  201. package/src/components/raceTiming.ts +18 -0
  202. package/src/data/initial-state.js +79 -31
  203. package/src/helpers/applyChartTypeContract.ts +71 -0
  204. package/src/helpers/axisLabelFontSize.ts +2 -2
  205. package/src/helpers/boxPlotStats.ts +60 -0
  206. package/src/helpers/bumpChartTooltip.ts +2 -1
  207. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.test.ts +37 -0
  208. package/src/helpers/calculateHorizontalBarCategoryLabelWidth.ts +3 -2
  209. package/src/helpers/calculateRightYAxisWidth.ts +60 -0
  210. package/src/helpers/categoryOrder.ts +60 -0
  211. package/src/helpers/colorAssignmentHelpers.ts +2 -2
  212. package/src/helpers/createNewChartConfig.ts +56 -0
  213. package/src/helpers/dataDrivenYAxisCategories.ts +18 -0
  214. package/src/helpers/forecastLegend.ts +88 -0
  215. package/src/helpers/getBoxPlotConfig.ts +26 -38
  216. package/src/helpers/getChartTypeDefaultPalette.ts +29 -0
  217. package/src/helpers/getColorCodeCategoryColorMap.ts +26 -0
  218. package/src/helpers/getColorScale.ts +31 -22
  219. package/src/helpers/getComboChartConfig.ts +0 -1
  220. package/src/helpers/getDynamicYAxisCategories.ts +69 -0
  221. package/src/helpers/getExcludedData.ts +0 -1
  222. package/src/helpers/getHasBoundarySuppression.ts +4 -3
  223. package/src/helpers/getMinMax.ts +11 -8
  224. package/src/helpers/getNewRuntime.ts +4 -3
  225. package/src/helpers/getPiePercent.ts +3 -3
  226. package/src/helpers/getStackedSeriesMax.ts +51 -0
  227. package/src/helpers/getV21ChartDistributionColors.ts +57 -0
  228. package/src/helpers/handleRankByValue.ts +15 -15
  229. package/src/helpers/isConvertLineToBarGraph.ts +2 -2
  230. package/src/helpers/seriesColumnSettings.ts +31 -3
  231. package/src/helpers/sizeHelpers.ts +4 -5
  232. package/src/helpers/smallMultiplesHelpers.ts +10 -13
  233. package/src/helpers/tests/applyChartTypeContract.test.ts +37 -0
  234. package/src/helpers/tests/boxPlotStats.test.ts +31 -0
  235. package/src/helpers/tests/bumpChartTooltip.test.ts +29 -0
  236. package/src/helpers/tests/calculateRightYAxisWidth.test.ts +105 -0
  237. package/src/helpers/tests/categoryOrder.test.ts +57 -0
  238. package/src/helpers/tests/countNumOfTicks.test.ts +11 -0
  239. package/src/helpers/tests/createNewChartConfig.test.ts +182 -0
  240. package/src/helpers/tests/getBoxPlotConfig.test.ts +70 -0
  241. package/src/helpers/tests/getColorCodeCategoryColorMap.test.ts +113 -0
  242. package/src/helpers/tests/getColorScale.test.ts +226 -1
  243. package/src/helpers/tests/getDynamicYAxisCategories.test.ts +61 -0
  244. package/src/helpers/tests/getMinMax.test.ts +41 -0
  245. package/src/helpers/tests/getStackedSeriesMax.test.ts +26 -0
  246. package/src/helpers/tests/handleRankByValue.test.ts +55 -37
  247. package/src/helpers/tests/seriesColumnSettings.test.ts +83 -0
  248. package/src/helpers/tests/smallMultiplesHelpers.test.ts +37 -1
  249. package/src/helpers/tests/tooltipHelpers.test.ts +80 -0
  250. package/src/helpers/tooltipHelpers.ts +16 -0
  251. package/src/hooks/tests/useRightAxis.test.tsx +143 -2
  252. package/src/hooks/tests/useScales.test.tsx +3 -6
  253. package/src/hooks/tests/useTooltip.test.tsx +484 -0
  254. package/src/hooks/usePrefersReducedMotion.ts +22 -0
  255. package/src/hooks/useReduceData.ts +4 -36
  256. package/src/hooks/useRightAxis.ts +19 -26
  257. package/src/hooks/useScales.ts +15 -0
  258. package/src/hooks/useTooltip.tsx +106 -71
  259. package/src/scss/main.scss +74 -2
  260. package/src/store/chart.reducer.ts +4 -4
  261. package/src/test/CdcChart.dataTable.test.tsx +582 -14
  262. package/src/test/CdcChart.dendrogram.test.tsx +277 -0
  263. package/src/test/CdcChart.finalization.test.tsx +143 -0
  264. package/src/test/CdcChart.network.test.tsx +271 -0
  265. package/src/types/ChartConfig.ts +91 -54
  266. package/src/types/ChartContext.ts +5 -0
  267. package/src/types/Horizon.ts +1 -1
  268. package/LICENSE +0 -201
  269. package/dist/cdcchart-BWVIUcAF.es.js +0 -4802
  270. package/src/_stories/Chart.Sankey.smoke.stories.tsx +0 -62
  271. package/src/components/Sankey/components/ColumnList.tsx +0 -19
  272. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +0 -34
  273. package/src/components/Sankey/useSankeyAlert.tsx +0 -60
  274. package/src/data/legacy-defaults.ts +0 -18
  275. package/src/helpers/ensureSpecialChartAxisTypes.ts +0 -11
  276. 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: 'Dotted' },
62
- { Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: '' },
63
- { Date: '10/19/2025', Category: 'COVID-19', Value: '42.6', Attribute: 'Dotted' }
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 dashed style when Attribute matches effect value', () => {
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
- // First point has Attribute="Dotted" - should be dashed
81
- expect(styles[0].strokeDasharray).toBe('5 5')
82
- // Second point has Attribute="" - but previous point was dashed, so it inherits
83
- // Actually, the logic applies dashed to current AND previous point when effect is found
84
- // Let's check what happens
85
- expect(styles[1].strokeDasharray).toBe('5 5') // Inherits from next point's effect
86
- // Third point has Attribute="Dotted" - should be dashed
87
- expect(styles[2].strokeDasharray).toBe('5 5')
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 Influenza series key with COVID data should not match
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: 'Influenza', // Different series
201
+ seriesKey: 'RSV', // Series not configured for an effect
123
202
  dynamicCategory: 'Category',
124
203
  originalSeriesKey: 'Value'
125
204
  })
126
205
 
127
- // No effect should match since seriesKeys is ['COVID-19'] not ['Influenza']
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': 'Dotted' },
157
- { Date: '10/12/2025', 'COVID-19': '40.7', 'COVID-19-Attribute': '' },
158
- { Date: '10/19/2025', 'COVID-19': '42.6', 'COVID-19-Attribute': 'Dotted' }
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 dashed style when attribute column matches', () => {
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
- // First point has attribute - dashed
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: 'Dotted' },
210
- { Date: '10/12/2025', Category: 'COVID-19', Value: '40.7', Attribute: '' },
211
- { Date: '10/19/2025', Category: 'COVID-19', Value: '42.6', Attribute: 'Dotted' }
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
- // First point has effect with custom weight
228
- expect(styles[0].strokeWidth).toBe(5)
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 = [{ Date: '10/5/2025', Category: 'COVID-19', Value: '43.6', Attribute: 'Dotted' }]
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(1)
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: PreliminaryDataItem = getMatchingPd(d)
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
- let style: Style = matchingPd
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
- // If matchingPd exists, update the previous style if there is a previous element
69
- if (matchingPd && index > 0) {
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: tableData,
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
- tableData,
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
- config.visualizationType == 'Bump Chart'
111
- ? _data
112
- : config.xAxis.type === 'date-time' || config.xAxis.type === 'date'
113
- ? _data.sort((d1, d2) => {
114
- let x1 = getXAxisData(d1)
115
- let x2 = getXAxisData(d2)
116
- if (x1 < x2) return -1
117
- if (x2 < x1) return 1
118
- return 0
119
- })
120
- : _data
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
- {_data.map((d, dataIndex) => {
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 = _data
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={_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={_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={_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
- {config.confidenceKeys &&
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}