@cdc/chart 4.23.5 → 4.23.7

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.
@@ -0,0 +1,84 @@
1
+ import React, { useContext } from 'react'
2
+
3
+ // cdc
4
+ import ConfigContext from '../ConfigContext'
5
+ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
+ import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
7
+
8
+ // visx & d3
9
+ import { curveMonotoneX } from '@visx/curve'
10
+ import { Bar, Area, LinePath } from '@visx/shape'
11
+ import { Group } from '@visx/group'
12
+
13
+ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, handleTooltipMouseOff }) => {
14
+ const { transformedData: data, rawData, config, seriesHighlight } = useContext(ConfigContext)
15
+ const { xAxis, yAxis, legend, runtime } = config
16
+ const DEBUG = false
17
+
18
+ return (
19
+ data && (
20
+ <ErrorBoundary component='ForecastingChart'>
21
+ <Group className='forecasting-items' key='forecasting-items-wrapper' left={Number(yAxis.size)}>
22
+ {runtime.forecastingSeriesKeys?.map((group, index) => {
23
+ if (!group || !group.stages) return false
24
+ return group.stages.map((stage, stageIndex) => {
25
+ const { behavior } = legend
26
+ const groupData = rawData.filter(d => d[group.stageColumn] === stage.key)
27
+ let transparentArea = behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(stage.key) === -1
28
+ let displayArea = behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(stage.key) !== -1
29
+
30
+ return (
31
+ <Group className={`forecasting-areas-combo-${index}`} key={`forecasting-areas--stage-${stage.key.replaceAll(' ', '-')}-${index}`}>
32
+ {group.confidenceIntervals?.map((ciGroup, ciGroupIndex) => {
33
+ const palette = colorPalettesChart[stage.color]
34
+
35
+ const getFill = () => {
36
+ if (displayArea) return palette[ciGroupIndex] ? palette[ciGroupIndex] : 'transparent'
37
+ return 'transparent'
38
+ }
39
+
40
+ const getStroke = () => {
41
+ if (displayArea) return palette[2] ? palette[2] : 'transparent'
42
+ return 'transparent'
43
+ }
44
+
45
+ if (ciGroup.high === '' || ciGroup.low === '') return
46
+ return (
47
+ <Group key={`forecasting-areas--stage-${stage.key.replaceAll(' ', '-')}--group-${stageIndex}-${ciGroupIndex}`}>
48
+ {/* prettier-ignore */}
49
+ <Area
50
+ curve={curveMonotoneX}
51
+ data={groupData}
52
+ fill={getFill()}
53
+ opacity={transparentArea ? 0.1 : 0.5}
54
+ x={d => xScale(Date.parse(d[xAxis.dataKey]))}
55
+ y0={d => yScale(d[ciGroup.low])}
56
+ y1={d => yScale(d[ciGroup.high])}
57
+ />
58
+
59
+ {ciGroupIndex === 0 && (
60
+ <>
61
+ {/* prettier-ignore */}
62
+ <LinePath data={groupData} x={d => Number(xScale(Date.parse(d[xAxis.dataKey])))} y={d => Number(yScale(d[ciGroup.high]))} curve={curveMonotoneX} stroke={getStroke()} strokeWidth={1} strokeOpacity={1} />
63
+
64
+ {/* prettier-ignore */}
65
+ <LinePath data={groupData} x={d => Number(xScale(Date.parse(d[xAxis.dataKey])))} y={d => Number(yScale(d[ciGroup.low]))} curve={curveMonotoneX} stroke={getStroke()} strokeWidth={1} strokeOpacity={1} />
66
+ </>
67
+ )}
68
+ </Group>
69
+ )
70
+ })}
71
+ </Group>
72
+ )
73
+ })
74
+ })}
75
+ <Group key='tooltip-hover-section'>
76
+ <Bar key={'bars'} width={Number(width)} height={Number(height)} fill={DEBUG ? 'red' : 'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} />
77
+ </Group>
78
+ </Group>
79
+ </ErrorBoundary>
80
+ )
81
+ )
82
+ }
83
+
84
+ export default Forecasting
@@ -7,84 +7,38 @@ import LegendCircle from '@cdc/core/components/LegendCircle'
7
7
  import useLegendClasses from './../hooks/useLegendClasses'
8
8
  import { useHighlightedBars } from '../hooks/useHighlightedBars'
9
9
 
10
- const Legend = () => {
11
- const { config, legend, colorScale, seriesHighlight, highlight, twoColorPalette, tableData, highlightReset, setSeriesHighlight, dynamicLegendItems, setDynamicLegendItems, transformedData: data, colorPalettes, rawData, setConfig, currentViewport } = useContext(ConfigContext)
12
-
13
- const { innerClasses, containerClasses } = useLegendClasses(config)
14
-
15
- useEffect(() => {
16
- if (dynamicLegendItems.length === 0) return
17
-
18
- let itemsToHighlight = dynamicLegendItems.map(item => item.text)
19
-
20
- setSeriesHighlight(itemsToHighlight)
10
+ // * FILE REVIEW *
11
+ // TODO: fix eslint-disable jsxa11y issues
21
12
 
22
- let colsToKeep = [...itemsToHighlight]
23
- let tmpLabels = []
24
-
25
- rawData.map(dataItem => {
26
- let tmp = {}
27
- colsToKeep.map(col => {
28
- tmp[col] = isNaN(dataItem[col]) ? dataItem[col] : dataItem[col]
29
- return null
30
- })
31
- return tmp
32
- })
33
-
34
- colsToKeep.map(col => {
35
- tmpLabels[col] = col
36
- return null
37
- })
38
-
39
- if (dynamicLegendItems.length > 0) {
40
- setConfig({
41
- ...config,
42
- runtime: {
43
- ...config.runtime,
44
- seriesKeys: colsToKeep,
45
- seriesLabels: tmpLabels
46
- }
47
- })
48
- }
49
- }, [dynamicLegendItems]) // eslint-disable-line
50
-
51
- useEffect(() => {
52
- if (dynamicLegendItems.length === 0) {
53
- // loop through all labels and add keys
54
- let resetSeriesNames = [...config.runtime.seriesLabelsAll]
55
- let tmpLabels = []
56
- config.runtime.seriesLabelsAll.map(item => {
57
- resetSeriesNames.map(col => {
58
- tmpLabels[col] = col
59
- return null
60
- })
61
- return null
62
- })
13
+ // * ADDITIONAL NOTES *
14
+ // > recently removed dynamic legend items as they weren't used
15
+ // > recently removed boxplots, they don't provide any legend settings
63
16
 
64
- setConfig({
65
- ...config,
66
- runtime: {
67
- ...config.runtime,
68
- seriesKeys: config.runtime.seriesLabelsAll,
69
- seriesLabels: tmpLabels
70
- }
71
- })
72
- }
73
- }, [dynamicLegendItems]) // eslint-disable-line
17
+ /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
18
+ const Legend = () => {
19
+ // prettier-ignore
20
+ const {
21
+ config,
22
+ legend,
23
+ colorScale,
24
+ seriesHighlight,
25
+ highlight,
26
+ twoColorPalette,
27
+ tableData,
28
+ highlightReset,
29
+ transformedData: data,
30
+ colorPalettes,
31
+ currentViewport
32
+ } = useContext(ConfigContext)
74
33
 
75
- const removeDynamicLegendItem = label => {
76
- let newLegendItems = dynamicLegendItems.filter(item => item.text !== label.text)
77
- let newLegendItemsText = newLegendItems.map(item => item.text)
78
- setDynamicLegendItems(newLegendItems)
79
- setSeriesHighlight(newLegendItemsText)
80
- }
81
- const handleDynamicLegendChange = e => {
82
- setDynamicLegendItems([...dynamicLegendItems, JSON.parse(e.target.value)])
83
- }
34
+ const { innerClasses, containerClasses } = useLegendClasses(config)
35
+ const { visualizationType, visualizationSubType, series, runtime, orientation } = config
36
+ // create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default.
37
+ const reverseLabels = labels => (config.legend.reverseLabelOrder && config.legend.position === 'bottom' ? labels.reverse() : labels)
84
38
 
85
39
  const createLegendLabels = defaultLabels => {
86
40
  const colorCode = config.legend?.colorCode
87
- if (config.visualizationType === 'Deviation Bar') {
41
+ if (visualizationType === 'Deviation Bar') {
88
42
  const [belowColor, aboveColor] = twoColorPalette[config.twoColor.palette]
89
43
  const labelBelow = {
90
44
  datum: 'X',
@@ -99,22 +53,22 @@ const Legend = () => {
99
53
  value: aboveColor
100
54
  }
101
55
 
102
- return [labelBelow, labelAbove]
56
+ return reverseLabels([labelBelow, labelAbove])
103
57
  }
104
- if (config.visualizationType === 'Bar' && config.visualizationSubType === 'regular' && colorCode && config.series?.length === 1) {
58
+ if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
105
59
  let palette = colorPalettes[config.palette]
106
60
 
107
61
  while (tableData.length > palette.length) {
108
62
  palette = palette.concat(palette)
109
63
  }
110
64
  palette = palette.slice(0, data.length)
111
- //store uniq values to Set by colorCode
65
+ //store unique values to Set by colorCode
112
66
  const set = new Set()
113
67
 
114
68
  tableData.forEach(d => set.add(d[colorCode]))
115
69
 
116
- // create labels with uniq values
117
- const uniqeLabels = Array.from(set).map((val, i) => {
70
+ // create labels with unique values
71
+ const uniqueLabels = Array.from(set).map((val, i) => {
118
72
  const newLabel = {
119
73
  datum: val,
120
74
  index: i,
@@ -124,226 +78,188 @@ const Legend = () => {
124
78
  return newLabel
125
79
  })
126
80
 
127
- return uniqeLabels
81
+ return reverseLabels(uniqueLabels)
128
82
  }
129
- return defaultLabels
130
- }
131
83
 
132
- const isBottomOrSmallViewport = config.legend.position === 'bottom' || currentViewport === 'sm' || currentViewport === 'xs' || currentViewport === 'xxs'
133
- const isHorizontal = config.orientation === 'horizontal'
134
- const marginTop = isBottomOrSmallViewport && isHorizontal ? `${config.runtime.xAxis.size}px` : '0px'
135
- const marginBottom = isBottomOrSmallViewport ? '15px' : '0px'
84
+ // get forecasting items inside of combo
85
+ if (runtime?.forecastingSeriesKeys?.length > 0) {
86
+ let seriesLabels = []
136
87
 
137
- const { HighLightedBarUtils } = useHighlightedBars(config)
88
+ //store unique values to Set by colorCode
138
89
 
139
- let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
90
+ // loop through each stage/group/area on the chart and create a label
91
+ config.runtime?.forecastingSeriesKeys?.map((outerGroup, index) => {
92
+ return outerGroup?.stages?.map((stage, index) => {
93
+ let colorValue = colorPalettes[stage.color]?.[2] ? colorPalettes[stage.color]?.[2] : '#ccc'
140
94
 
141
- if (!legend) return null
95
+ const newLabel = {
96
+ datum: stage.key,
97
+ index: index,
98
+ text: stage.key,
99
+ value: colorValue
100
+ }
142
101
 
143
- if (!legend.dynamicLegend)
144
- return config.visualizationType !== 'Box Plot' ? (
145
- <aside style={{ marginTop, marginBottom }} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
146
- {legend.label && <h2>{parse(legend.label)}</h2>}
147
- {legend.description && <p>{parse(legend.description)}</p>}
148
- <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
149
- {labels => (
150
- <div className={innerClasses.join(' ')}>
151
- {createLegendLabels(labels).map((label, i) => {
152
- let className = 'legend-item'
153
- let itemName = label.datum
154
-
155
- // Filter excluded data keys from legend
156
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
157
- return null
158
- }
159
-
160
- if (config.runtime.seriesLabels) {
161
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
162
- itemName = config.runtime.seriesKeys[index]
163
- }
164
-
165
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
166
- className += ' inactive'
167
- }
168
-
169
- return (
170
- <LegendItem
171
- className={className}
172
- tabIndex={0}
173
- key={`legend-quantile-${i}`}
174
- onKeyPress={e => {
175
- if (e.key === 'Enter') {
176
- highlight(label)
177
- }
178
- }}
179
- onClick={() => {
180
- highlight(label)
181
- }}
182
- >
183
- <LegendCircle fill={label.value} />
184
- <LegendLabel align='left' margin='0 0 0 4px'>
185
- {label.text}
186
- </LegendLabel>
187
- </LegendItem>
188
- )
189
- })}
190
-
191
- {highLightedLegendItems.map((bar, i) => {
192
- // if duplicates only return first item
193
- let className = 'legend-item'
194
- let itemName = bar.legendLabel
195
-
196
- if (!itemName) return
197
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
198
- className += ' inactive'
199
- }
200
- return (
201
- <LegendItem
202
- className={className}
203
- tabIndex={0}
204
- key={`legend-quantile-${i}`}
205
- onKeyPress={e => {
206
- if (e.key === 'Enter') {
207
- highlight(bar.legendLabel)
208
- }
209
- }}
210
- onClick={() => {
211
- highlight(bar.legendLabel)
212
- }}
213
- >
214
- <LegendCircle fill='transparent' borderColor={bar.color ? bar.color : `rgba(255, 102, 1)`} />{' '}
215
- <LegendLabel align='left' margin='0 0 0 4px'>
216
- {bar.legendLabel ? bar.legendLabel : bar.value}
217
- </LegendLabel>
218
- </LegendItem>
219
- )
220
- })}
221
- {seriesHighlight.length > 0 && (
222
- <button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
223
- Reset
224
- </button>
225
- )}
226
- </div>
227
- )}
228
- </LegendOrdinal>
229
- </aside>
230
- ) : (
231
- <aside id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
232
- {config.boxplot.legend.displayHowToReadText && <h3>{config.boxplot.legend.howToReadText}</h3>}
233
- </aside>
234
- )
235
- return (
236
- config.visualizationType !== 'Box Plot' && (
237
- <aside id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
238
- {legend.label && <h2>{parse(legend.label)}</h2>}
239
- {legend.description && <p>{parse(legend.description)}</p>}
102
+ seriesLabels.push(newLabel)
103
+ })
104
+ })
240
105
 
241
- <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
242
- {labels => {
243
- if (
244
- Number(config.legend.dynamicLegendItemLimit) > dynamicLegendItems.length && // legend items are less than limit
245
- dynamicLegendItems.length !== config.runtime.seriesLabelsAll.length
246
- ) {
247
- // legend items are equal to series length
248
- return (
249
- <select className='dynamic-legend-dropdown' onChange={e => handleDynamicLegendChange(e)}>
250
- <option className={'all'} tabIndex={0} value={JSON.stringify({ text: config.legend.dynamicLegendDefaultText })}>
251
- {config.legend.dynamicLegendDefaultText}
252
- </option>
253
- {labels.map((label, i) => {
254
- let className = 'legend-item'
255
- let itemName = label.datum
256
- let inDynamicList = false
257
-
258
- // Filter excluded data keys from legend
259
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
260
- return null
261
- }
106
+ // loop through bars for now to meet requirements.
107
+ config.runtime.barSeriesKeys &&
108
+ config.runtime.barSeriesKeys.forEach((bar, index) => {
109
+ let colorValue = colorPalettes[config.palette][index] ? colorPalettes[config.palette][index] : '#ccc'
262
110
 
263
- if (config.runtime.seriesLabels) {
264
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
265
- itemName = config.runtime.seriesKeys[index]
266
- }
111
+ const newLabel = {
112
+ datum: bar,
113
+ index: index,
114
+ text: bar,
115
+ value: colorValue
116
+ }
267
117
 
268
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
269
- className += ' inactive'
270
- }
118
+ seriesLabels.push(newLabel)
119
+ })
271
120
 
272
- dynamicLegendItems.map(listItem => {
273
- if (listItem.text === label.text) {
274
- inDynamicList = true
275
- }
276
- return null
277
- })
121
+ return reverseLabels(seriesLabels)
122
+ }
278
123
 
279
- if (inDynamicList) return true
280
- let palette = colorPalettes[config.palette]
124
+ // DEV-4161: replaceable series name in the legend
125
+ const hasNewSeriesName = config.series.map(s => s.name).filter(item => item).length > 0
126
+ if (hasNewSeriesName) {
127
+ let palette = colorPalettes[config.palette]
281
128
 
282
- label.value = palette[dynamicLegendItems.length]
129
+ while (tableData.length > palette.length) {
130
+ palette = palette.concat(palette)
131
+ }
283
132
 
284
- return (
285
- <option className={className} tabIndex={0} value={JSON.stringify(label)}>
286
- {label.text}
287
- </option>
288
- )
289
- })}
290
- </select>
291
- )
292
- } else {
293
- return config.legend.dynamicLegendItemLimitMessage
294
- }
295
- }}
296
- </LegendOrdinal>
133
+ palette = palette.slice(0, data.length)
134
+ //store unique values to Set by colorCode
135
+ const set = new Set()
297
136
 
298
- <div className='dynamic-legend-list'>
299
- {dynamicLegendItems.map((label, i) => {
300
- let className = ['legend-item']
301
- let itemName = label.text
302
- let palette = colorPalettes[config.palette]
137
+ config.series.forEach(d => {
138
+ set.add(d['name'] ? d['name'] : d['dataKey'])
139
+ })
303
140
 
304
- // Filter excluded data keys from legend
305
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
306
- return null
307
- }
141
+ // create labels with unique values
142
+ const uniqueLabels = Array.from(set).map((val, i) => {
143
+ const newLabel = {
144
+ datum: val,
145
+ index: i,
146
+ text: val,
147
+ value: palette[i]
148
+ }
149
+ return newLabel
150
+ })
308
151
 
309
- if (config.runtime.seriesLabels && !config.legend.dynamicLegend) {
310
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
311
- itemName = config.runtime.seriesKeys[index]
312
- }
152
+ return reverseLabels(uniqueLabels)
153
+ }
313
154
 
314
- if (seriesHighlight.length > 0 && !seriesHighlight.includes(itemName)) {
315
- className.push('inactive')
316
- }
155
+ return reverseLabels(defaultLabels)
156
+ }
157
+
158
+ const isBottomOrSmallViewport = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
159
+
160
+ const legendClasses = {
161
+ marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
162
+ marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `0px`
163
+ }
317
164
 
318
- if (seriesHighlight.length === 0 && config.legend.dynamicLegend) {
319
- className.push('inactive')
320
- }
165
+ const { HighLightedBarUtils } = useHighlightedBars(config)
321
166
 
167
+ let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
168
+
169
+ if (!legend) return null
170
+
171
+ return (
172
+ config.visualizationType !== 'Box Plot' && (
173
+ <aside style={legendClasses} id='legend' className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
174
+ {legend.label && <h2>{parse(legend.label)}</h2>}
175
+ {legend.description && <p>{parse(legend.description)}</p>}
176
+ <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
177
+ {labels => {
322
178
  return (
323
- <>
324
- <LegendItem className={className.join(' ')} tabIndex={0} key={`dynamic-legend-item-${i}`} alignItems='center'>
325
- <button
326
- className='btn-wrapper'
327
- onClick={() => {
328
- highlight(label)
329
- }}
330
- >
331
- <LegendCircle fill={palette[i]} config={config} />
332
- <LegendLabel align='space-between' margin='4px 0 0 4px'>
333
- {label.text}
334
- </LegendLabel>
179
+ <div className={innerClasses.join(' ')}>
180
+ {createLegendLabels(labels).map((label, i) => {
181
+ let className = 'legend-item'
182
+ let itemName = label.datum
183
+
184
+ // Filter excluded data keys from legend
185
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
186
+ return null
187
+ }
188
+
189
+ if (runtime.seriesLabels) {
190
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
191
+ itemName = config.runtime.seriesKeys[index]
192
+
193
+ if (runtime?.forecastingSeriesKeys?.length > 0) {
194
+ itemName = label.text
195
+ }
196
+ }
197
+
198
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
199
+ className += ' inactive'
200
+ }
201
+
202
+ return (
203
+ <LegendItem
204
+ className={className}
205
+ tabIndex={0}
206
+ key={`legend-quantile-${i}`}
207
+ onKeyPress={e => {
208
+ if (e.key === 'Enter') {
209
+ highlight(label)
210
+ }
211
+ }}
212
+ onClick={() => {
213
+ highlight(label)
214
+ }}
215
+ >
216
+ <LegendCircle fill={label.value} />
217
+ <LegendLabel align='left' margin='0 0 0 4px'>
218
+ {label.text}
219
+ </LegendLabel>
220
+ </LegendItem>
221
+ )
222
+ })}
223
+
224
+ {highLightedLegendItems.map((bar, i) => {
225
+ // if duplicates only return first item
226
+ let className = 'legend-item'
227
+ let itemName = bar.legendLabel
228
+
229
+ if (!itemName) return false
230
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
231
+ className += ' inactive'
232
+ }
233
+ return (
234
+ <LegendItem
235
+ className={className}
236
+ tabIndex={0}
237
+ key={`legend-quantile-${i}`}
238
+ onKeyPress={e => {
239
+ if (e.key === 'Enter') {
240
+ highlight(bar.legendLabel)
241
+ }
242
+ }}
243
+ onClick={() => {
244
+ highlight(bar.legendLabel)
245
+ }}
246
+ >
247
+ <LegendCircle fill='transparent' borderColor={bar.color ? bar.color : `rgba(255, 102, 1)`} />{' '}
248
+ <LegendLabel align='left' margin='0 0 0 4px'>
249
+ {bar.legendLabel ? bar.legendLabel : bar.value}
250
+ </LegendLabel>
251
+ </LegendItem>
252
+ )
253
+ })}
254
+ {seriesHighlight.length > 0 && (
255
+ <button className={`legend-reset ${config.theme}`} onClick={labels => highlightReset(labels)} tabIndex={0}>
256
+ Reset
335
257
  </button>
336
- <button onClick={() => removeDynamicLegendItem(label)}>x</button>
337
- </LegendItem>
338
- </>
258
+ )}
259
+ </div>
339
260
  )
340
- })}
341
- </div>
342
- {seriesHighlight.length < dynamicLegendItems.length && (
343
- <button className={`legend-reset legend-reset--dynamic ${config.theme}`} onClick={highlightReset} tabIndex={0}>
344
- Reset
345
- </button>
346
- )}
261
+ }}
262
+ </LegendOrdinal>
347
263
  </aside>
348
264
  )
349
265
  )