@cdc/chart 4.24.12 → 4.25.2-25

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 (84) hide show
  1. package/dist/cdcchart.js +79900 -78999
  2. package/examples/feature/boxplot/boxplot.json +2 -157
  3. package/examples/feature/boxplot/testing.csv +23 -38
  4. package/examples/feature/tests-non-numerics/example-combo-bar-nonnumeric.json +579 -49
  5. package/examples/private/ehdi.json +29939 -0
  6. package/examples/private/line-issue.json +497 -0
  7. package/examples/private/not-loading.json +360 -0
  8. package/index.html +11 -15
  9. package/package.json +2 -2
  10. package/src/CdcChart.tsx +92 -1512
  11. package/src/CdcChartComponent.tsx +1113 -0
  12. package/src/ConfigContext.tsx +6 -1
  13. package/src/_stories/Chart.Anchors.stories.tsx +1 -1
  14. package/src/_stories/Chart.CustomColors.stories.tsx +1 -1
  15. package/src/_stories/Chart.DynamicSeries.stories.tsx +17 -2
  16. package/src/_stories/Chart.Filters.stories.tsx +19 -0
  17. package/src/_stories/Chart.Legend.Gradient.stories.tsx +2 -2
  18. package/src/_stories/Chart.ScatterPlot.stories.tsx +19 -0
  19. package/src/_stories/Chart.tooltip.stories.tsx +1 -2
  20. package/src/_stories/ChartAnnotation.stories.tsx +1 -1
  21. package/src/_stories/ChartAxisLabels.stories.tsx +1 -1
  22. package/src/_stories/ChartAxisTitles.stories.tsx +1 -1
  23. package/src/_stories/ChartEditor.stories.tsx +1 -1
  24. package/src/_stories/ChartLine.Suppression.stories.tsx +1 -1
  25. package/src/_stories/ChartLine.Symbols.stories.tsx +18 -0
  26. package/src/_stories/ChartPrefixSuffix.stories.tsx +1 -1
  27. package/src/_stories/_mock/line_chart_symbols.json +437 -0
  28. package/src/_stories/_mock/scatterplot-image-download.json +1244 -0
  29. package/src/components/Annotations/components/AnnotationDraggable.tsx +3 -11
  30. package/src/components/Annotations/components/AnnotationDropdown.tsx +3 -3
  31. package/src/components/Axis/Categorical.Axis.tsx +3 -4
  32. package/src/components/BarChart/components/BarChart.Horizontal.tsx +14 -5
  33. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +10 -4
  34. package/src/components/BarChart/components/BarChart.Vertical.tsx +5 -7
  35. package/src/components/BarChart/components/BarChart.jsx +24 -4
  36. package/src/components/BarChart/components/context.tsx +1 -0
  37. package/src/components/BoxPlot/BoxPlot.tsx +34 -32
  38. package/src/components/BoxPlot/helpers/index.ts +108 -18
  39. package/src/components/BrushChart.tsx +44 -24
  40. package/src/components/DeviationBar.jsx +2 -6
  41. package/src/components/EditorPanel/EditorPanel.tsx +64 -8
  42. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +4 -0
  43. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +3 -1
  44. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +44 -7
  45. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +6 -1
  46. package/src/components/ForestPlot/ForestPlot.tsx +176 -26
  47. package/src/components/Legend/Legend.Component.tsx +29 -38
  48. package/src/components/Legend/Legend.Suppression.tsx +3 -5
  49. package/src/components/Legend/Legend.tsx +2 -2
  50. package/src/components/Legend/LegendLine.Shape.tsx +51 -0
  51. package/src/components/Legend/helpers/createFormatLabels.tsx +29 -26
  52. package/src/components/Legend/helpers/getLegendClasses.ts +20 -38
  53. package/src/components/Legend/helpers/index.ts +22 -9
  54. package/src/components/Legend/tests/getLegendClasses.test.ts +3 -20
  55. package/src/components/LineChart/components/LineChart.Circle.tsx +104 -94
  56. package/src/components/LineChart/index.tsx +6 -2
  57. package/src/components/LinearChart.tsx +77 -43
  58. package/src/components/PairedBarChart.jsx +2 -9
  59. package/src/components/ZoomBrush.tsx +5 -7
  60. package/src/data/initial-state.js +6 -3
  61. package/src/helpers/getBoxPlotConfig.ts +68 -0
  62. package/src/helpers/getColorScale.ts +24 -0
  63. package/src/helpers/getComboChartConfig.ts +42 -0
  64. package/src/helpers/getExcludedData.ts +37 -0
  65. package/src/helpers/getTopAxis.ts +7 -0
  66. package/src/helpers/isConvertLineToBarGraph.ts +10 -3
  67. package/src/hooks/useBarChart.ts +40 -13
  68. package/src/hooks/{useHighlightedBars.js → useHighlightedBars.ts} +2 -1
  69. package/src/hooks/useIntersectionObserver.ts +37 -0
  70. package/src/hooks/useMinMax.ts +11 -8
  71. package/src/hooks/useReduceData.ts +1 -1
  72. package/src/hooks/useScales.ts +10 -0
  73. package/src/hooks/useTooltip.tsx +21 -2
  74. package/src/index.jsx +1 -0
  75. package/src/scss/DataTable.scss +0 -5
  76. package/src/scss/main.scss +31 -116
  77. package/src/store/chart.actions.ts +40 -0
  78. package/src/store/chart.reducer.ts +83 -0
  79. package/src/types/ChartConfig.ts +6 -3
  80. package/src/types/ChartContext.ts +1 -3
  81. package/src/helpers/getQuartiles.ts +0 -27
  82. package/src/hooks/useColorScale.ts +0 -50
  83. package/src/hooks/useIntersectionObserver.jsx +0 -29
  84. package/src/hooks/useTopAxis.js +0 -6
@@ -1,54 +1,36 @@
1
1
  import { ChartConfig } from './../../../types/ChartConfig'
2
2
 
3
3
  export const getLegendClasses = (config: ChartConfig) => {
4
- const { position, singleRow, reverseLabelOrder, verticalSorted, hideBorder } = config.legend
5
- const containerClasses = ['legend-container']
6
- const innerClasses = ['legend-container__inner']
7
-
8
- // Handle legend positioning
9
- switch (position) {
10
- case 'left':
11
- containerClasses.push('left')
12
- break
13
- case 'right':
14
- containerClasses.push('right')
15
- break
16
- case 'bottom':
17
- containerClasses.push('bottom')
18
- innerClasses.push('double-column', 'bottom')
19
- break
20
- case 'top':
21
- containerClasses.push('top')
22
- innerClasses.push('double-column', 'top')
23
- break
24
- }
4
+ const { position, singleRow, verticalSorted, hideBorder } = config.legend
25
5
 
26
- // Handle single row configuration for 'bottom' and 'top' positions
27
- if (['bottom', 'top'].includes(position) && singleRow) {
28
- innerClasses.push('single-row')
6
+ const containerClassMap = {
7
+ left: 'left',
8
+ right: 'right',
9
+ bottom: 'bottom',
10
+ top: 'top'
29
11
  }
30
12
 
31
- // Reverse label order
32
- if (reverseLabelOrder) {
33
- innerClasses.push('d-flex', 'flex-column-reverse')
13
+ const innerClassMap = {
14
+ bottom: singleRow ? ['single-row', 'bottom'] : ['double-column', 'bottom'],
15
+ top: singleRow ? ['single-row', 'top'] : ['double-column', 'top']
34
16
  }
35
17
 
36
- // Vertical sorting for 'bottom' and 'top' positions
18
+ const containerClasses = ['legend-container', containerClassMap[position]].filter(Boolean)
19
+ const innerClasses = ['legend-container__inner', ...(innerClassMap[position] || [])]
20
+
21
+ // Add vertical sorting class for 'bottom' and 'top' positions
37
22
  if (['bottom', 'top'].includes(position) && verticalSorted) {
38
23
  innerClasses.push('vertical-sorted')
39
24
  }
40
25
 
41
- // Configure border classes
42
- if (hideBorder.side && (['right', 'left'].includes(position) || !position)) {
43
- containerClasses.push('border-0')
44
- }
45
-
46
- if (hideBorder.topBottom && ['top', 'bottom'].includes(position)) {
47
- containerClasses.push('border-0')
48
- }
26
+ // Add border and padding classes based on position and hideBorder
27
+ const shouldHideBorder = (['right', 'left'].includes(position) || !position) && hideBorder.side
28
+ const shouldHideTopBottomBorder = ['top', 'bottom'].includes(position) && hideBorder.topBottom
49
29
 
50
- if (hideBorder.topBottom && ['top'].includes(position)) {
51
- containerClasses.push('p-0')
30
+ if (shouldHideBorder || shouldHideTopBottomBorder) {
31
+ containerClasses.push('border-0', 'p-0')
32
+ } else {
33
+ containerClasses.push('p-3')
52
34
  }
53
35
 
54
36
  return {
@@ -10,22 +10,35 @@ export const getGradientConfig = (config, formatLabels, colorScale) => {
10
10
  return { colors, labels }
11
11
  }
12
12
 
13
- export const getMarginTop = (isBottomOrSmallViewport, config) => {
14
- if (!isBottomOrSmallViewport) {
13
+ export const getMarginTop = (isLegendBottom, config) => {
14
+ // margin between charts xAxis legend not to overlap axis labels,ticks.
15
+ const DEFAULT_MARGIN_TOP = 27
16
+ if (isLegendBottom && config.legend.hide) {
15
17
  return '0px'
16
18
  }
17
- if (isBottomOrSmallViewport && config.brush?.active) {
18
- const BRUSH_HEIGHT_MULTIPLIER = 1.5
19
- return `${config.brush.height * BRUSH_HEIGHT_MULTIPLIER}px`
19
+ if (!isLegendBottom) {
20
+ return '0px'
21
+ }
22
+ if (isLegendBottom && config.brush.active && !config.legend.hide) {
23
+ const additiolMargin = 25
24
+ return `${DEFAULT_MARGIN_TOP + config.brush.height + additiolMargin}px`
25
+ } else {
26
+ return `${DEFAULT_MARGIN_TOP}px`
20
27
  }
21
- return '20px'
22
28
  }
23
- export const getMarginBottom = (config, hasSuppression) => {
29
+ export const getMarginBottom = (isLegendBottom, config) => {
24
30
  const isLegendTop = config.legend?.position === 'top' && !config.legend.hide
25
-
31
+ const hasSuppression =
32
+ !config.legend.hideSuppressionLink &&
33
+ config.visualizationSubType !== 'stacked' &&
34
+ config.preliminaryData?.some(pd => pd.label && pd.type === 'suppression' && pd.value && (pd?.style || pd.symbol))
26
35
  let marginBottom = 0
27
36
 
28
- if (isLegendTop) marginBottom = config.legend.hideBorder.topBottom ? 15 : 25
37
+ if (isLegendTop) marginBottom = 27
38
+
39
+ if (isLegendTop && config.dataFormat?.onlyShowTopPrefixSuffix) marginBottom += 9
40
+
41
+ if (isLegendBottom) marginBottom += 9
29
42
 
30
43
  if (hasSuppression) marginBottom += 40
31
44
 
@@ -47,41 +47,24 @@ describe('getLegendClasses', () => {
47
47
  }
48
48
  const result = getLegendClasses(config)
49
49
  expect(result.containerClasses).toContain('bottom')
50
- expect(result.innerClasses).toContain('double-column')
51
50
  expect(result.innerClasses).toContain('bottom')
52
51
  expect(result.innerClasses).toContain('single-row')
53
52
  })
54
53
 
55
- it('should return correct classes for top position with vertical sorting', () => {
54
+ it('should return correct classes for TOP position with double column', () => {
56
55
  const config: ChartConfig = {
57
56
  legend: {
58
57
  position: 'top',
59
58
  singleRow: false,
60
59
  reverseLabelOrder: false,
61
- verticalSorted: true,
60
+ verticalSorted: false,
62
61
  hideBorder: { side: false, topBottom: false }
63
62
  }
64
63
  }
65
64
  const result = getLegendClasses(config)
66
65
  expect(result.containerClasses).toContain('top')
67
- expect(result.innerClasses).toContain('double-column')
68
66
  expect(result.innerClasses).toContain('top')
69
- expect(result.innerClasses).toContain('vertical-sorted')
70
- })
71
-
72
- it('should return correct classes for reverse label order', () => {
73
- const config: ChartConfig = {
74
- legend: {
75
- position: 'bottom',
76
- singleRow: false,
77
- reverseLabelOrder: true,
78
- verticalSorted: false,
79
- hideBorder: { side: false, topBottom: false }
80
- }
81
- }
82
- const result = getLegendClasses(config)
83
- expect(result.innerClasses).toContain('d-flex')
84
- expect(result.innerClasses).toContain('flex-column-reverse')
67
+ expect(result.innerClasses).toContain('double-column')
85
68
  })
86
69
 
87
70
  it('should return correct classes for hide border side', () => {
@@ -1,8 +1,9 @@
1
1
  import React from 'react'
2
2
  import chroma from 'chroma-js'
3
3
  import { type ChartConfig } from '../../../types/ChartConfig'
4
+ import { GlyphDiamond, GlyphCircle, GlyphSquare, GlyphTriangle, GlyphCross, Glyph as CustomGlyph } from '@visx/glyph'
5
+ import { Text } from '@visx/text'
4
6
 
5
- // todo: change this config obj to ChartConfig once its created
6
7
  type LineChartCircleProps = {
7
8
  circleData: object[]
8
9
  config: ChartConfig
@@ -23,9 +24,25 @@ type LineChartCircleProps = {
23
24
  parseDate: any
24
25
  seriesAxis: string
25
26
  dataIndex: number
27
+ seriesIndex: number
26
28
  mode: 'ISOLATED_POINTS' | 'HOVER_POINTS' | 'ALWAYS_SHOW_POINTS'
27
29
  }
28
-
30
+ const Glyphs = [
31
+ GlyphCircle,
32
+ GlyphSquare,
33
+ GlyphTriangle,
34
+ GlyphDiamond,
35
+ GlyphTriangle,
36
+ GlyphCross,
37
+ ({ fill }: { fill: string }) => (
38
+ <CustomGlyph>
39
+ {/* Render Filled Pentagon */}
40
+ <Text fill={fill} fontSize={14} textAnchor='middle' verticalAnchor='middle'>
41
+ &#x2B1F;
42
+ </Text>
43
+ </CustomGlyph>
44
+ )
45
+ ]
29
46
  const LineChartCircle = (props: LineChartCircleProps) => {
30
47
  const {
31
48
  config,
@@ -42,10 +59,18 @@ const LineChartCircle = (props: LineChartCircleProps) => {
42
59
  data,
43
60
  circleData,
44
61
  dataIndex,
45
- mode
62
+ mode,
63
+ seriesIndex
46
64
  } = props
47
- const { lineDatapointStyle } = config
48
- const filtered = config?.runtime?.series.filter(s => s.dataKey === seriesKey)?.[0]
65
+ const { lineDatapointStyle, visual } = config
66
+ const filtered = config?.series.filter(s => s.dataKey === seriesKey)?.[0]
67
+ const Shape =
68
+ Glyphs[
69
+ config.visual.lineDatapointSymbol === 'standard' && seriesIndex < visual.maximumShapeAmount ? seriesIndex : 0
70
+ ]
71
+ const isReversedTriangle = seriesIndex === 4
72
+ const transformShape = (top, left) => `translate(${left}, ${top})${isReversedTriangle ? ' rotate(180)' : ''}`
73
+
49
74
  // If we're not showing the circle, simply return
50
75
  const getColor = (
51
76
  displayArea: boolean,
@@ -55,14 +80,8 @@ const LineChartCircle = (props: LineChartCircleProps) => {
55
80
  seriesKey: string
56
81
  ) => {
57
82
  const seriesLabels = config.runtime.seriesLabels || []
58
- let color
59
-
60
- if (displayArea) {
61
- color = colorScale(seriesLabels[hoveredKey] || seriesKey)
62
- } else {
63
- color = 'transparent'
64
- }
65
-
83
+ const seriesLabelsAll = config.runtime.seriesLabelsAll || []
84
+ let color = displayArea ? colorScale(seriesLabels[hoveredKey] || seriesLabelsAll[seriesIndex]) : ' transparent'
66
85
  if (config.lineDatapointColor === 'Lighter than Line' && color !== 'transparent' && color) {
67
86
  color = chroma(color).brighten(1)
68
87
  }
@@ -74,27 +93,30 @@ const LineChartCircle = (props: LineChartCircleProps) => {
74
93
  (xScale.bandwidth ? xScale.bandwidth() / 2 : 0)
75
94
  )
76
95
  }
77
- if (mode === 'ALWAYS_SHOW_POINTS') {
78
- if (lineDatapointStyle === 'hidden') return <></>
79
- const getIndex = seriesKey => config.runtime.seriesLabelsAll.indexOf(seriesKey)
80
-
96
+ if (mode === 'ALWAYS_SHOW_POINTS' && lineDatapointStyle !== 'hidden') {
81
97
  if (lineDatapointStyle === 'always show') {
82
98
  const isMatch = circleData?.some(
83
99
  cd => cd[config.xAxis.dataKey] === d[config.xAxis.dataKey] && cd[seriesKey] === d[seriesKey]
84
100
  )
85
- if (isMatch || !filtered) {
101
+
102
+ if (
103
+ isMatch ||
104
+ !filtered ||
105
+ (visual.maximumShapeAmount === seriesIndex && visual.lineDatapointSymbol === 'standard')
106
+ )
86
107
  return <></>
87
- }
108
+ const positionLeft = getXPos(d[config.xAxis.dataKey])
109
+ const positionTop = filtered.axis === 'Right' ? yScaleRight(d[filtered.dataKey]) : yScale(d[filtered.dataKey])
110
+
88
111
  return (
89
- <circle
90
- cx={getXPos(d[config.xAxis.dataKey])}
91
- cy={filtered.axis === 'Right' ? yScaleRight(d[filtered.dataKey]) : yScale(d[filtered.dataKey])}
92
- r={4.5}
93
- opacity={d[seriesKey] ? 1 : 0}
94
- fillOpacity={1}
95
- fill={getColor(displayArea, colorScale, config, seriesKey, seriesKey)}
96
- style={{ filter: 'unset', opacity: 1 }}
97
- />
112
+ <g transform={transformShape(positionTop, positionLeft)}>
113
+ <Shape
114
+ opacity={d[seriesKey] ? 1 : 0}
115
+ fillOpacity={1}
116
+ fill={getColor(displayArea, colorScale, config, seriesKey, seriesKey)}
117
+ style={{ filter: 'unset', opacity: 1 }}
118
+ />
119
+ </g>
98
120
  )
99
121
  }
100
122
  }
@@ -108,96 +130,84 @@ const LineChartCircle = (props: LineChartCircleProps) => {
108
130
  if (!hoveredXValue) return
109
131
 
110
132
  let hoveredSeriesValue
111
- let hoveredSeriesIndex
112
133
  let hoveredSeriesData = tooltipData.data.filter(d => d[0] === seriesKey)
113
134
  let hoveredSeriesKey = hoveredSeriesData?.[0]?.[0]
114
135
  let hoveredSeriesAxis = hoveredSeriesData?.[0]?.[2]
136
+ const dynamicSeriesConfig = config.runtime.series.find(s => s.dynamicCategory)
137
+ const originalDataKey = dynamicSeriesConfig?.originalDataKey ?? seriesKey
138
+
115
139
  if (!hoveredSeriesKey) return
116
- hoveredSeriesIndex = tooltipData?.data.indexOf(hoveredSeriesKey)
117
140
  hoveredSeriesValue = tableData?.find(d => {
118
- return d[config?.xAxis.dataKey] === hoveredXValue
119
- })?.[seriesKey]
141
+ const dynamicCategory = dynamicSeriesConfig?.dynamicCategory
142
+ const matchingXValue = d[config.xAxis.dataKey] === hoveredXValue
143
+ if (!matchingXValue) return false
144
+ if (dynamicCategory) {
145
+ const match = d[dynamicCategory] === hoveredSeriesKey
146
+ return match
147
+ }
148
+ return true
149
+ })?.[originalDataKey]
120
150
 
121
151
  // hoveredSeriesValue = extractNumber(hoveredSeriesValue)
122
152
  return tooltipData?.data.map((tooltipItem, index) => {
123
- let seriesIndex = config.runtime.seriesLabelsAll.indexOf(hoveredXValue)
124
-
125
153
  if (isNaN(hoveredSeriesValue)) return <></>
126
154
  const isMatch = circleData?.some(cd => cd[config.xAxis.dataKey] === hoveredXValue)
127
155
 
128
- if (isMatch || !hoveredSeriesValue) {
156
+ if (
157
+ isMatch ||
158
+ !hoveredSeriesValue ||
159
+ (visual.maximumShapeAmount === seriesIndex && visual.lineDatapointSymbol === 'standard')
160
+ ) {
129
161
  return <></>
130
162
  }
131
163
 
164
+ const positionTop = hoveredSeriesAxis === 'right' ? yScaleRight(hoveredSeriesValue) : yScale(hoveredSeriesValue)
165
+ const positionLeft = getXPos(hoveredXValue)
132
166
  return (
133
- <circle
134
- cx={getXPos(hoveredXValue)}
135
- cy={hoveredSeriesAxis === 'right' ? yScaleRight(hoveredSeriesValue) : yScale(hoveredSeriesValue)}
136
- r={4.5}
137
- opacity={1}
138
- fillOpacity={1}
139
- fill={getColor(displayArea, colorScale, config, hoveredSeriesKey, seriesKey)}
140
- style={{ filter: 'unset', opacity: 1 }}
141
- key={`line-chart-circle--${JSON.stringify(tooltipItem)}--${index}`}
142
- />
167
+ <g transform={transformShape(positionTop, positionLeft)}>
168
+ <Shape
169
+ size={55}
170
+ opacity={1}
171
+ fillOpacity={1}
172
+ fill={getColor(displayArea, colorScale, config, hoveredSeriesKey, seriesKey)}
173
+ style={{ filter: 'unset', opacity: 1 }}
174
+ />
175
+ </g>
143
176
  )
144
177
  })
145
178
  }
146
179
  }
147
-
148
180
  if (mode === 'ISOLATED_POINTS') {
149
181
  const drawIsolatedPoints = (currentIndex, seriesKey) => {
150
- let isMatch = false
151
182
  const currentPoint = data[currentIndex]
152
- const previousPoint = currentIndex > 0 ? data[currentIndex - 1] : null
153
- const nextPoint = currentIndex < data.length - 1 ? data[currentIndex + 1] : null
154
- let res = false
155
- // check if isolated points has overlap with circle effect
156
- circleData.forEach(item => {
157
- if (item?.data[seriesKey] === currentPoint[seriesKey]) {
158
- isMatch = true
159
- }
160
- })
161
-
162
- // Handle the first point in the array
163
- if (currentIndex === 0 && nextPoint && !nextPoint[seriesKey]) {
164
- res = true
165
- }
166
- // Handle the last point in the array
167
- if (currentIndex === data.length - 1 && previousPoint && !previousPoint[seriesKey]) {
168
- res = true
169
- }
170
- // Handle points in the middle
171
- if (currentIndex > 0 && currentIndex < data.length - 1) {
172
- if (
173
- currentPoint &&
174
- currentPoint[seriesKey] &&
175
- (!previousPoint || !previousPoint[seriesKey]) &&
176
- (!nextPoint || !nextPoint[seriesKey])
177
- ) {
178
- res = true
179
- }
180
- }
181
- if (isMatch) {
182
- res = false
183
- }
184
-
185
- return res
183
+ const previousPoint = data[currentIndex - 1] || {}
184
+ const nextPoint = data[currentIndex + 1] || {}
185
+
186
+ const isMatch = circleData.some(item => item?.data[seriesKey] === currentPoint[seriesKey])
187
+ if (isMatch) return false
188
+
189
+ const isFirstPoint = currentIndex === 0 && !nextPoint[seriesKey]
190
+ const isLastPoint = currentIndex === data.length - 1 && !previousPoint[seriesKey]
191
+ const isMiddlePoint =
192
+ currentIndex > 0 &&
193
+ currentIndex < data.length - 1 &&
194
+ currentPoint[seriesKey] &&
195
+ !previousPoint[seriesKey] &&
196
+ !nextPoint[seriesKey]
197
+
198
+ return isFirstPoint || isLastPoint || isMiddlePoint
186
199
  }
187
200
 
188
- if (mode) {
189
- if (drawIsolatedPoints(dataIndex, seriesKey)) {
190
- return (
191
- <circle
192
- cx={getXPos(d[config.xAxis?.dataKey])}
193
- cy={filtered?.axis === 'Right' ? yScaleRight(d[filtered?.dataKey]) : yScale(d[filtered?.dataKey])}
194
- r={5.3}
195
- strokeWidth={2}
196
- stroke={colorScale(config.runtime.seriesLabels[seriesKey])}
197
- fill={colorScale(config.runtime?.seriesLabels[seriesKey])}
198
- />
199
- )
200
- }
201
+ if (drawIsolatedPoints(dataIndex, seriesKey)) {
202
+ const positionTop = filtered?.axis === 'Right' ? yScaleRight(d[filtered?.dataKey]) : yScale(d[filtered?.dataKey])
203
+ const positionLeft = getXPos(d[config.xAxis?.dataKey])
204
+ const color = colorScale(config.runtime.seriesLabelsAll[seriesIndex])
205
+
206
+ return (
207
+ <g transform={transformShape(positionTop, positionLeft)}>
208
+ <Shape size={124} stroke={color} fill={color} />
209
+ </g>
210
+ )
201
211
  }
202
212
  }
203
213
 
@@ -89,14 +89,14 @@ const LineChart = (props: LineChartProps) => {
89
89
  opacity={
90
90
  legend.behavior === 'highlight' &&
91
91
  seriesHighlight.length > 0 &&
92
- seriesHighlight.indexOf(_seriesKey) === -1
92
+ seriesHighlight.indexOf(seriesKey) === -1
93
93
  ? 0.5
94
94
  : 1
95
95
  }
96
96
  display={
97
97
  legend.behavior === 'highlight' ||
98
98
  (seriesHighlight.length === 0 && !legend.dynamicLegend) ||
99
- seriesHighlight.indexOf(_seriesKey) !== -1
99
+ seriesHighlight.indexOf(seriesKey) !== -1
100
100
  ? 'block'
101
101
  : 'none'
102
102
  }
@@ -150,12 +150,14 @@ const LineChart = (props: LineChartProps) => {
150
150
  parseDate={parseDate}
151
151
  yScaleRight={yScaleRight}
152
152
  seriesAxis={seriesAxis}
153
+ seriesIndex={index}
153
154
  key={`line-circle--${dataIndex}`}
154
155
  />
155
156
  )}
156
157
 
157
158
  <LineChartCircle
158
159
  mode='ISOLATED_POINTS'
160
+ seriesIndex={index}
159
161
  dataIndex={dataIndex}
160
162
  tableData={tableData}
161
163
  circleData={circleData}
@@ -180,6 +182,7 @@ const LineChart = (props: LineChartProps) => {
180
182
  <>
181
183
  {lineDatapointStyle === 'hover' && (
182
184
  <LineChartCircle
185
+ seriesIndex={index}
183
186
  tableData={tableData}
184
187
  dataIndex={0}
185
188
  mode='HOVER_POINTS'
@@ -198,6 +201,7 @@ const LineChart = (props: LineChartProps) => {
198
201
  />
199
202
  )}
200
203
  </>
204
+
201
205
  {/* SPLIT LINE */}
202
206
  {isSplitLine ? (
203
207
  <>