@cdc/chart 4.23.1 → 4.23.3

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 (74) hide show
  1. package/dist/cdcchart.js +56289 -702
  2. package/examples/Barchart_with_negative.json +34 -0
  3. package/examples/area-chart.json +187 -0
  4. package/examples/big-small-test-bar.json +328 -0
  5. package/examples/big-small-test-line.json +328 -0
  6. package/examples/big-small-test-negative.json +328 -0
  7. package/examples/box-plot.json +1 -2
  8. package/examples/dynamic-legends.json +1 -1
  9. package/examples/example-bar-chart-nonnumeric.json +36 -0
  10. package/examples/example-bar-chart.json +36 -0
  11. package/examples/example-combo-bar-nonnumeric.json +105 -0
  12. package/examples/example-sparkline.json +76 -0
  13. package/examples/gallery/bar-chart-horizontal/horizontal-bar-chart.json +31 -172
  14. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-categorical.json +1 -1
  15. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-confidence.json +1 -0
  16. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-with-confidence.json +96 -14
  17. package/examples/gallery/bar-chart-vertical/vertical-bar-chart.json +2 -2
  18. package/examples/gallery/line/line.json +1 -0
  19. package/examples/gallery/paired-bar/paired-bar-chart.json +65 -13
  20. package/examples/horizontal-chart-max-increase.json +38 -0
  21. package/examples/line-chart-max-increase.json +32 -0
  22. package/examples/line-chart-nonnumeric.json +32 -0
  23. package/examples/line-chart.json +21 -63
  24. package/examples/newdata.json +1 -1
  25. package/examples/planet-combo-example-config.json +143 -20
  26. package/examples/planet-deviation-config.json +168 -0
  27. package/examples/planet-deviation-data.json +38 -0
  28. package/examples/planet-example-config.json +139 -20
  29. package/examples/planet-example-data-max-increase.json +56 -0
  30. package/examples/planet-example-data-nonnumeric.json +56 -0
  31. package/examples/planet-example-data.json +9 -9
  32. package/examples/planet-pie-example-config-nonnumeric.json +30 -0
  33. package/examples/scatterplot-continuous.csv +17 -0
  34. package/examples/scatterplot.json +136 -0
  35. package/examples/sparkline-chart-nonnumeric.json +76 -0
  36. package/examples/stacked-vertical-bar-example-negative.json +154 -0
  37. package/examples/stacked-vertical-bar-example-nonnumerics.json +154 -0
  38. package/index.html +91 -0
  39. package/package.json +33 -24
  40. package/src/{CdcChart.tsx → CdcChart.jsx} +196 -124
  41. package/src/components/AreaChart.jsx +198 -0
  42. package/src/components/{BarChart.tsx → BarChart.jsx} +154 -122
  43. package/src/components/BoxPlot.jsx +101 -0
  44. package/src/components/{DataTable.tsx → DataTable.jsx} +109 -28
  45. package/src/components/DeviationBar.jsx +191 -0
  46. package/src/components/{EditorPanel.js → EditorPanel.jsx} +676 -157
  47. package/src/components/{Filters.js → Filters.jsx} +6 -11
  48. package/src/components/Legend.jsx +316 -0
  49. package/src/components/{LineChart.tsx → LineChart.jsx} +22 -26
  50. package/src/components/{LinearChart.tsx → LinearChart.jsx} +214 -91
  51. package/src/components/{PairedBarChart.tsx → PairedBarChart.jsx} +44 -78
  52. package/src/components/{PieChart.tsx → PieChart.jsx} +26 -44
  53. package/src/components/ScatterPlot.jsx +51 -0
  54. package/src/components/SparkLine.jsx +218 -0
  55. package/src/components/{useIntersectionObserver.tsx → useIntersectionObserver.jsx} +2 -2
  56. package/src/data/initial-state.js +51 -5
  57. package/src/hooks/useColorPalette.js +68 -0
  58. package/src/hooks/{useReduceData.ts → useReduceData.js} +26 -16
  59. package/src/hooks/useRightAxis.js +3 -1
  60. package/src/index.jsx +16 -0
  61. package/src/scss/DataTable.scss +22 -0
  62. package/src/scss/editor-panel.scss +5 -0
  63. package/src/scss/main.scss +30 -10
  64. package/src/test/CdcChart.test.jsx +6 -0
  65. package/vite.config.js +4 -0
  66. package/dist/495.js +0 -3
  67. package/dist/703.js +0 -1
  68. package/src/components/BoxPlot.js +0 -92
  69. package/src/components/Legend.js +0 -291
  70. package/src/components/SparkLine.js +0 -185
  71. package/src/hooks/useColorPalette.ts +0 -76
  72. package/src/index.html +0 -67
  73. package/src/index.tsx +0 -18
  74. /package/src/{context.tsx → ConfigContext.jsx} +0 -0
@@ -1,9 +1,9 @@
1
- import React, { useEffect, useState, useContext } from 'react'
2
- import Context from './../context'
1
+ import React, { useState, useContext } from 'react'
2
+ import ConfigContext from './../ConfigContext'
3
3
  import Button from '@cdc/core/components/elements/Button'
4
4
 
5
5
  const useFilters = () => {
6
- const { config, setConfig, filteredData, setFilteredData, excludedData, filterData, runtimeFilters } = useContext(Context)
6
+ const { config, setConfig, setFilteredData, excludedData, filterData } = useContext(ConfigContext)
7
7
  const [showApplyButton, setShowApplyButton] = useState(false)
8
8
 
9
9
  const sortAsc = (a, b) => {
@@ -38,6 +38,7 @@ const useFilters = () => {
38
38
  // reset to first item in values array.
39
39
  newFilters.map(filter => {
40
40
  filter.active = filter.values[0]
41
+ return null
41
42
  })
42
43
 
43
44
  setFilteredData(filterData(newFilters, excludedData))
@@ -48,7 +49,7 @@ const useFilters = () => {
48
49
  }
49
50
 
50
51
  const Filters = () => {
51
- const { config } = useContext(Context)
52
+ const { config } = useContext(ConfigContext)
52
53
  const { handleApplyButton, changeFilterActive, announceChange, sortAsc, sortDesc, showApplyButton, handleReset } = useFilters()
53
54
  const { filters } = config
54
55
  const buttonText = 'Apply Filters'
@@ -105,13 +106,7 @@ const Filters = () => {
105
106
 
106
107
  return (
107
108
  <section className={`filters-section`} style={{ display: 'block', width: '100%' }}>
108
- {config.filters.length > 0 && (
109
- <>
110
- <h3 className='filters-section__title'>Filters</h3>
111
- <hr />
112
- </>
113
- )}
114
- <div className='filters-section__wrapper' style={{ flexWrap: 'wrap', display: 'flex', gap: '7px 15px' }}>
109
+ <div className='filters-section__wrapper' style={{ flexWrap: 'wrap', display: 'flex', gap: '7px 15px', marginTop: '15px' }}>
115
110
  <FilterList />
116
111
  {config.filters.length > 0 && (
117
112
  <div className='filter-section__buttons' style={{ width: '100%' }}>
@@ -0,0 +1,316 @@
1
+ import React, { useContext, useEffect } from 'react'
2
+ import ConfigContext from '../ConfigContext'
3
+ import parse from 'html-react-parser'
4
+ import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
5
+ import LegendCircle from '@cdc/core/components/LegendCircle'
6
+
7
+ import useLegendClasses from './../hooks/useLegendClasses'
8
+
9
+ const Legend = () => {
10
+ const { config, legend, colorScale, seriesHighlight, highlight, twoColorPalette, highlightReset, setSeriesHighlight, dynamicLegendItems, setDynamicLegendItems, transformedData: data, colorPalettes, rawData, setConfig, currentViewport } = useContext(ConfigContext)
11
+
12
+ const { innerClasses, containerClasses } = useLegendClasses(config)
13
+
14
+ useEffect(() => {
15
+ if (dynamicLegendItems.length === 0) return
16
+
17
+ let itemsToHighlight = dynamicLegendItems.map(item => item.text)
18
+
19
+ setSeriesHighlight(itemsToHighlight)
20
+
21
+ let colsToKeep = [...itemsToHighlight]
22
+ let tmpLabels = []
23
+
24
+ rawData.map(dataItem => {
25
+ let tmp = {}
26
+ colsToKeep.map(col => {
27
+ tmp[col] = isNaN(dataItem[col]) ? dataItem[col] : dataItem[col]
28
+ return null
29
+ })
30
+ return tmp
31
+ })
32
+
33
+ colsToKeep.map(col => {
34
+ tmpLabels[col] = col
35
+ return null
36
+ })
37
+
38
+ if (dynamicLegendItems.length > 0) {
39
+ setConfig({
40
+ ...config,
41
+ runtime: {
42
+ ...config.runtime,
43
+ seriesKeys: colsToKeep,
44
+ seriesLabels: tmpLabels
45
+ }
46
+ })
47
+ }
48
+ }, [dynamicLegendItems]) // eslint-disable-line
49
+
50
+ useEffect(() => {
51
+ if (dynamicLegendItems.length === 0) {
52
+ // loop through all labels and add keys
53
+ let resetSeriesNames = [...config.runtime.seriesLabelsAll]
54
+ let tmpLabels = []
55
+ config.runtime.seriesLabelsAll.map(item => {
56
+ resetSeriesNames.map(col => {
57
+ tmpLabels[col] = col
58
+ return null
59
+ })
60
+ return null
61
+ })
62
+
63
+ setConfig({
64
+ ...config,
65
+ runtime: {
66
+ ...config.runtime,
67
+ seriesKeys: config.runtime.seriesLabelsAll,
68
+ seriesLabels: tmpLabels
69
+ }
70
+ })
71
+ }
72
+ }, [dynamicLegendItems]) // eslint-disable-line
73
+
74
+ const removeDynamicLegendItem = label => {
75
+ let newLegendItems = dynamicLegendItems.filter(item => item.text !== label.text)
76
+ let newLegendItemsText = newLegendItems.map(item => item.text)
77
+ setDynamicLegendItems(newLegendItems)
78
+ setSeriesHighlight(newLegendItemsText)
79
+ }
80
+ const handleDynamicLegendChange = e => {
81
+ setDynamicLegendItems([...dynamicLegendItems, JSON.parse(e.target.value)])
82
+ }
83
+
84
+ const createLegendLabels = defaultLabels => {
85
+ const colorCode = config.legend?.colorCode
86
+ if (config.visualizationType === 'Deviation Bar') {
87
+ const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
88
+ const labelBelow = {
89
+ datum: 'X',
90
+ index: 0,
91
+ text: `Below ${config.xAxis.targetLabel}`,
92
+ value: belowColor
93
+ }
94
+ const labelAbove = {
95
+ datum: 'X',
96
+ index: 1,
97
+ text: `Above ${config.xAxis.targetLabel}`,
98
+ value: aboveColor
99
+ }
100
+
101
+ return [labelBelow, labelAbove]
102
+ }
103
+ if (config.visualizationType === 'Bar' && config.visualizationSubType === 'regular' && colorCode && config.series?.length === 1) {
104
+ let palette = colorPalettes[config.palette]
105
+
106
+ while (data.length > palette.length) {
107
+ palette = palette.concat(palette)
108
+ }
109
+ palette = palette.slice(0, data.length)
110
+ //store uniq values to Set by colorCode
111
+ const set = new Set()
112
+
113
+ data.forEach(d => set.add(d[colorCode]))
114
+
115
+ // create labels with uniq values
116
+ const uniqeLabels = Array.from(set).map((val, i) => {
117
+ const newLabel = {
118
+ datum: val,
119
+ index: i,
120
+ text: val,
121
+ value: palette[i]
122
+ }
123
+ return newLabel
124
+ })
125
+
126
+ return uniqeLabels
127
+ }
128
+ return defaultLabels
129
+ }
130
+
131
+ const isBottomOrSmallViewport = config.legend.position === 'bottom' || currentViewport === 'sm' || currentViewport === 'xs'
132
+ const isHorizontal = config.orientation === 'horizontal'
133
+ const marginTop = isBottomOrSmallViewport && isHorizontal ? `${config.runtime.xAxis.size}px` : '0px'
134
+ const marginBottom = isBottomOrSmallViewport ? '15px' : '0px'
135
+
136
+ if (!legend) return null
137
+
138
+ if (!legend.dynamicLegend)
139
+ return config.visualizationType !== 'Box Plot' ? (
140
+ <aside style={{ marginTop, marginBottom }} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
141
+ {legend.label && <h2>{parse(legend.label)}</h2>}
142
+ {legend.description && <p>{parse(legend.description)}</p>}
143
+ <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
144
+ {labels => (
145
+ <div className={innerClasses.join(' ')}>
146
+ {createLegendLabels(labels).map((label, i) => {
147
+ let className = 'legend-item'
148
+ let itemName = label.datum
149
+
150
+ // Filter excluded data keys from legend
151
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
152
+ return null
153
+ }
154
+
155
+ if (config.runtime.seriesLabels) {
156
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
157
+ itemName = config.runtime.seriesKeys[index]
158
+ }
159
+
160
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
161
+ className += ' inactive'
162
+ }
163
+
164
+ return (
165
+ <LegendItem
166
+ className={className}
167
+ tabIndex={0}
168
+ key={`legend-quantile-${i}`}
169
+ onKeyPress={e => {
170
+ if (e.key === 'Enter') {
171
+ highlight(label)
172
+ }
173
+ }}
174
+ onClick={() => {
175
+ highlight(label)
176
+ }}
177
+ >
178
+ <LegendCircle fill={label.value} />
179
+ <LegendLabel align='left' margin='0 0 0 4px'>
180
+ {label.text}
181
+ </LegendLabel>
182
+ </LegendItem>
183
+ )
184
+ })}
185
+ {seriesHighlight.length > 0 && (
186
+ <button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
187
+ Reset
188
+ </button>
189
+ )}
190
+ </div>
191
+ )}
192
+ </LegendOrdinal>
193
+ </aside>
194
+ ) : (
195
+ <aside id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
196
+ {config.boxplot.legend.displayHowToReadText && <h3>{config.boxplot.legend.howToReadText}</h3>}
197
+ </aside>
198
+ )
199
+ return (
200
+ config.visualizationType !== 'Box Plot' && (
201
+ <aside id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
202
+ {legend.label && <h2>{parse(legend.label)}</h2>}
203
+ {legend.description && <p>{parse(legend.description)}</p>}
204
+
205
+ <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
206
+ {labels => {
207
+ if (
208
+ Number(config.legend.dynamicLegendItemLimit) > dynamicLegendItems.length && // legend items are less than limit
209
+ dynamicLegendItems.length !== config.runtime.seriesLabelsAll.length
210
+ ) {
211
+ // legend items are equal to series length
212
+ return (
213
+ <select className='dynamic-legend-dropdown' onChange={e => handleDynamicLegendChange(e)}>
214
+ <option className={'all'} tabIndex={0} value={JSON.stringify({ text: config.legend.dynamicLegendDefaultText })}>
215
+ {config.legend.dynamicLegendDefaultText}
216
+ </option>
217
+ {labels.map((label, i) => {
218
+ let className = 'legend-item'
219
+ let itemName = label.datum
220
+ let inDynamicList = false
221
+
222
+ // Filter excluded data keys from legend
223
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
224
+ return null
225
+ }
226
+
227
+ if (config.runtime.seriesLabels) {
228
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
229
+ itemName = config.runtime.seriesKeys[index]
230
+ }
231
+
232
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
233
+ className += ' inactive'
234
+ }
235
+
236
+ dynamicLegendItems.map(listItem => {
237
+ if (listItem.text === label.text) {
238
+ inDynamicList = true
239
+ }
240
+ return null
241
+ })
242
+
243
+ if (inDynamicList) return true
244
+ let palette = colorPalettes[config.palette]
245
+
246
+ label.value = palette[dynamicLegendItems.length]
247
+
248
+ return (
249
+ <option className={className} tabIndex={0} value={JSON.stringify(label)}>
250
+ {label.text}
251
+ </option>
252
+ )
253
+ })}
254
+ </select>
255
+ )
256
+ } else {
257
+ return config.legend.dynamicLegendItemLimitMessage
258
+ }
259
+ }}
260
+ </LegendOrdinal>
261
+
262
+ <div className='dynamic-legend-list'>
263
+ {dynamicLegendItems.map((label, i) => {
264
+ let className = ['legend-item']
265
+ let itemName = label.text
266
+ let palette = colorPalettes[config.palette]
267
+
268
+ // Filter excluded data keys from legend
269
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
270
+ return null
271
+ }
272
+
273
+ if (config.runtime.seriesLabels && !config.legend.dynamicLegend) {
274
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
275
+ itemName = config.runtime.seriesKeys[index]
276
+ }
277
+
278
+ if (seriesHighlight.length > 0 && !seriesHighlight.includes(itemName)) {
279
+ className.push('inactive')
280
+ }
281
+
282
+ if (seriesHighlight.length === 0 && config.legend.dynamicLegend) {
283
+ className.push('inactive')
284
+ }
285
+
286
+ return (
287
+ <>
288
+ <LegendItem className={className.join(' ')} tabIndex={0} key={`dynamic-legend-item-${i}`} alignItems='center'>
289
+ <button
290
+ className='btn-wrapper'
291
+ onClick={() => {
292
+ highlight(label)
293
+ }}
294
+ >
295
+ <LegendCircle fill={palette[i]} config={config} />
296
+ <LegendLabel align='space-between' margin='4px 0 0 4px'>
297
+ {label.text}
298
+ </LegendLabel>
299
+ </button>
300
+ <button onClick={() => removeDynamicLegendItem(label)}>x</button>
301
+ </LegendItem>
302
+ </>
303
+ )
304
+ })}
305
+ </div>
306
+ {seriesHighlight.length < dynamicLegendItems.length && (
307
+ <button className={`legend-reset legend-reset--dynamic ${config.theme}`} onClick={highlightReset} tabIndex={0}>
308
+ Reset
309
+ </button>
310
+ )}
311
+ </aside>
312
+ )
313
+ )
314
+ }
315
+
316
+ export default Legend
@@ -7,28 +7,21 @@ import { Text } from '@visx/text'
7
7
 
8
8
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
9
9
 
10
- import Context from '../context'
10
+ import ConfigContext from '../ConfigContext'
11
11
 
12
12
  import useRightAxis from '../hooks/useRightAxis'
13
13
 
14
14
  export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, seriesStyle = 'Line' }) {
15
- const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, updateConfig } = useContext<any>(Context)
15
+ const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, cleanData, updateConfig, handleLineType } = useContext(ConfigContext)
16
+ // Just do this once up front otherwise we end up
17
+ // calling clean several times on same set of data (TT)
18
+ const cleanedData = cleanData(data, config.xAxis.dataKey)
16
19
  const { yScaleRight } = useRightAxis({ config, yMax, data, updateConfig })
17
20
 
18
- const handleLineType = lineType => {
19
- switch (lineType) {
20
- case 'dashed-sm':
21
- return '5 5'
22
- case 'dashed-md':
23
- return '10 5'
24
- case 'dashed-lg':
25
- return '15 5'
26
- default:
27
- return 0
28
- }
29
- }
30
-
31
21
  const handleAxisFormating = (axis = 'left', label, value) => {
22
+ // if this is an x axis category/date value return without doing any formatting.
23
+ // if (label === config.runtime.xAxis.label) return value
24
+
32
25
  axis = String(axis).toLocaleLowerCase()
33
26
  if (label) {
34
27
  return `${label}: ${formatNumber(value, axis)}`
@@ -38,7 +31,9 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
38
31
 
39
32
  return (
40
33
  <ErrorBoundary component='LineChart'>
41
- <Group left={config.runtime.yAxis.size}>
34
+ <Group left={config.runtime.yAxis.size ? parseInt(config.runtime.yAxis.size) : 66}>
35
+ {' '}
36
+ {/* left - expects a number not a string */}
42
37
  {(config.runtime.lineSeriesKeys || config.runtime.seriesKeys).map((seriesKey, index) => {
43
38
  let lineType = config.series.filter(item => item.dataKey === seriesKey)[0].type
44
39
  const seriesData = config.series.filter(item => item.dataKey === seriesKey)
@@ -50,7 +45,7 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
50
45
  opacity={config.legend.behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(seriesKey) === -1 ? 0.5 : 1}
51
46
  display={config.legend.behavior === 'highlight' || (seriesHighlight.length === 0 && !config.legend.dynamicLegend) || seriesHighlight.indexOf(seriesKey) !== -1 ? 'block' : 'none'}
52
47
  >
53
- {data.map((d, dataIndex) => {
48
+ {cleanedData.map((d, dataIndex) => {
54
49
  // Find the series object from the config.series array that has a dataKey matching the seriesKey variable.
55
50
  const series = config.series.find(({ dataKey }) => dataKey === seriesKey)
56
51
  const { axis } = series
@@ -73,11 +68,13 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
73
68
  ${xAxisTooltip}
74
69
  </div>`
75
70
  let circleRadii = 4.5
71
+
76
72
  return (
77
73
  d[seriesKey] !== undefined &&
78
74
  d[seriesKey] !== '' &&
79
75
  d[seriesKey] !== null && (
80
76
  <Group key={`series-${seriesKey}-point-${dataIndex}`}>
77
+ {/* Render legend */}
81
78
  <Text
82
79
  display={config.labels ? 'block' : 'none'}
83
80
  x={xScale(getXAxisData(d))}
@@ -85,18 +82,18 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
85
82
  fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
86
83
  textAnchor='middle'
87
84
  >
88
- {formatNumber(d[seriesKey])}
85
+ {formatNumber(d[seriesKey], 'left')}
89
86
  </Text>
90
87
 
91
88
  <circle
92
89
  key={`${seriesKey}-${dataIndex}`}
93
90
  r={circleRadii}
94
- cx={xScale(getXAxisData(d))}
91
+ cx={Number(xScale(getXAxisData(d)))}
95
92
  cy={seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey))}
96
93
  fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
97
94
  style={{ fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000' }}
98
- data-tip={tooltip}
99
- data-for={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
95
+ data-tooltip-html={tooltip}
96
+ data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
100
97
  />
101
98
  </Group>
102
99
  )
@@ -105,7 +102,7 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
105
102
 
106
103
  <LinePath
107
104
  curve={allCurves.curveLinear}
108
- data={data}
105
+ data={cleanedData}
109
106
  x={d => xScale(getXAxisData(d))}
110
107
  y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
111
108
  stroke={
@@ -122,14 +119,14 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
122
119
  shapeRendering='geometricPrecision'
123
120
  strokeDasharray={lineType ? handleLineType(lineType) : 0}
124
121
  defined={(item, i) => {
125
- return item[config.runtime.seriesLabels[seriesKey]] !== '' && item[config.runtime.seriesLabels[seriesKey]] !== null
122
+ return item[config.runtime.seriesLabels[seriesKey]] !== '' && item[config.runtime.seriesLabels[seriesKey]] !== null && item[config.runtime.seriesLabels[seriesKey]] !== undefined
126
123
  }}
127
124
  />
128
125
  {config.animate && (
129
126
  <LinePath
130
127
  className='animation'
131
128
  curve={allCurves.curveLinear}
132
- data={data}
129
+ data={cleanedData}
133
130
  x={d => xScale(getXAxisData(d))}
134
131
  y={d => (seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey)))}
135
132
  stroke='#fff'
@@ -138,7 +135,7 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
138
135
  shapeRendering='geometricPrecision'
139
136
  strokeDasharray={lineType ? handleLineType(lineType) : 0}
140
137
  defined={(item, i) => {
141
- return item[config.runtime.seriesLabels[seriesKey]] !== '' && item[config.runtime.seriesLabels[seriesKey]] !== null
138
+ return isNumber(item[config.runtime.seriesLabels[seriesKey]])
142
139
  }}
143
140
  />
144
141
  )}
@@ -165,7 +162,6 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
165
162
  </Group>
166
163
  )
167
164
  })}
168
-
169
165
  {/* Message when dynamic legend and nothing has been picked */}
170
166
  {config.legend.dynamicLegend && seriesHighlight.length === 0 && (
171
167
  <Text x={xMax / 2} y={yMax / 2} fill='black' textAnchor='middle' color='black'>