@cdc/chart 4.24.7 → 4.24.9

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 (51) hide show
  1. package/dist/cdcchart.js +40313 -37543
  2. package/examples/cases-year.json +13379 -0
  3. package/examples/gallery/bar-chart-vertical/combo-line-chart.json +76 -15
  4. package/examples/gallery/bar-chart-vertical/vertical-bar-chart-stacked.json +5 -5
  5. package/index.html +17 -8
  6. package/package.json +2 -2
  7. package/src/CdcChart.tsx +383 -133
  8. package/src/_stories/Chart.Legend.Gradient.tsx +19 -0
  9. package/src/_stories/_mock/legend.gradient_mock.json +236 -0
  10. package/src/components/Annotations/components/AnnotationDraggable.tsx +64 -11
  11. package/src/components/Axis/Categorical.Axis.tsx +145 -0
  12. package/src/components/BarChart/components/BarChart.Horizontal.tsx +4 -3
  13. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -1
  14. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +2 -5
  15. package/src/components/BarChart/components/BarChart.Vertical.tsx +17 -8
  16. package/src/components/BarChart/helpers/index.ts +5 -16
  17. package/src/components/BrushChart.tsx +205 -0
  18. package/src/components/EditorPanel/EditorPanel.tsx +1766 -509
  19. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +19 -5
  20. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +190 -37
  21. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +43 -7
  22. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +4 -4
  23. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -11
  24. package/src/components/EditorPanel/editor-panel.scss +16 -3
  25. package/src/components/EditorPanel/{useEditorPermissions.js → useEditorPermissions.ts} +90 -19
  26. package/src/components/Legend/Legend.Component.tsx +185 -193
  27. package/src/components/Legend/Legend.Suppression.tsx +146 -0
  28. package/src/components/Legend/Legend.tsx +21 -5
  29. package/src/components/Legend/helpers/index.ts +33 -3
  30. package/src/components/LegendWrapper.tsx +26 -0
  31. package/src/components/LineChart/LineChartProps.ts +1 -18
  32. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +103 -0
  33. package/src/components/LineChart/components/LineChart.Circle.tsx +47 -8
  34. package/src/components/LineChart/helpers.ts +55 -11
  35. package/src/components/LineChart/index.tsx +113 -38
  36. package/src/components/LinearChart.tsx +1366 -0
  37. package/src/components/PieChart/PieChart.tsx +74 -17
  38. package/src/components/Sankey/index.tsx +22 -16
  39. package/src/components/Sparkline/components/SparkLine.tsx +2 -2
  40. package/src/data/initial-state.js +13 -3
  41. package/src/hooks/useLegendClasses.ts +52 -15
  42. package/src/hooks/useMinMax.ts +4 -4
  43. package/src/hooks/useScales.ts +34 -24
  44. package/src/hooks/useTooltip.tsx +85 -22
  45. package/src/scss/DataTable.scss +2 -1
  46. package/src/scss/main.scss +107 -14
  47. package/src/types/ChartConfig.ts +34 -8
  48. package/src/types/ChartContext.ts +5 -4
  49. package/examples/feature/line/line-chart.json +0 -449
  50. package/src/components/BrushHandle.jsx +0 -17
  51. package/src/components/LineChart/index.scss +0 -1
@@ -11,6 +11,7 @@ export const useEditorPermissions = () => {
11
11
  'Area Chart',
12
12
  'Bar',
13
13
  'Box Plot',
14
+ 'Bump Chart',
14
15
  'Combo',
15
16
  'Deviation Bar',
16
17
  'Forecasting',
@@ -23,7 +24,19 @@ export const useEditorPermissions = () => {
23
24
  'Sankey'
24
25
  ]
25
26
 
26
- const headerColors = ['theme-blue', 'theme-purple', 'theme-brown', 'theme-teal', 'theme-pink', 'theme-orange', 'theme-slate', 'theme-indigo', 'theme-cyan', 'theme-green', 'theme-amber']
27
+ const headerColors = [
28
+ 'theme-blue',
29
+ 'theme-purple',
30
+ 'theme-brown',
31
+ 'theme-teal',
32
+ 'theme-pink',
33
+ 'theme-orange',
34
+ 'theme-slate',
35
+ 'theme-indigo',
36
+ 'theme-cyan',
37
+ 'theme-green',
38
+ 'theme-amber'
39
+ ]
27
40
 
28
41
  const visSupportsDateCategoryAxis = () => {
29
42
  const disabledCharts = ['Forest Plot', 'Sankey']
@@ -44,13 +57,30 @@ export const useEditorPermissions = () => {
44
57
  }
45
58
 
46
59
  const visHasLabelOnData = () => {
47
- const disabledCharts = ['Area Chart', 'Box Plot', 'Pie', 'Scatter Plot', 'Forest Plot', 'Spark Line', 'Sankey']
60
+ const disabledCharts = [
61
+ 'Area Chart',
62
+ 'Box Plot',
63
+ 'Pie',
64
+ 'Scatter Plot',
65
+ 'Forest Plot',
66
+ 'Spark Line',
67
+ 'Sankey',
68
+ 'Bump Chart'
69
+ ]
48
70
  if (disabledCharts.includes(visualizationType)) return false
49
71
  return true
50
72
  }
51
73
 
52
74
  const visCanAnimate = () => {
53
- const disabledCharts = ['Area Chart', 'Scatter Plot', 'Box Plot', 'Forest Plot', 'Spark Line', 'Sankey']
75
+ const disabledCharts = [
76
+ 'Area Chart',
77
+ 'Scatter Plot',
78
+ 'Box Plot',
79
+ 'Forest Plot',
80
+ 'Spark Line',
81
+ 'Sankey',
82
+ 'Bump Chart'
83
+ ]
54
84
  if (disabledCharts.includes(visualizationType)) return false
55
85
  return true
56
86
  }
@@ -72,7 +102,14 @@ export const useEditorPermissions = () => {
72
102
 
73
103
  const visHasNumbersOnBars = () => {
74
104
  if (visualizationType === 'Forest Plot') return false
75
- if (config.orientation === 'horizontal' && (config.yAxis.labelPlacement === 'Below Bar' || config.yAxis.labelPlacement === 'On Date/Category Axis' || config.visualizationType === 'Paired Bar' || config.visualizationType === 'Deviation Bar')) return true
105
+ if (
106
+ config.orientation === 'horizontal' &&
107
+ (config.yAxis.labelPlacement === 'Below Bar' ||
108
+ config.yAxis.labelPlacement === 'On Date/Category Axis' ||
109
+ config.visualizationType === 'Paired Bar' ||
110
+ config.visualizationType === 'Deviation Bar')
111
+ )
112
+ return true
76
113
  return false
77
114
  }
78
115
 
@@ -104,13 +141,17 @@ export const useEditorPermissions = () => {
104
141
  }
105
142
  }
106
143
  const visHasBrushChart = () => {
144
+ return false
145
+ if (config.xAxis.type === 'categorical') return false
107
146
  return ['Line', 'Bar', 'Area Chart', 'Combo'].includes(visualizationType) && orientation === 'vertical'
108
147
  }
109
148
 
110
149
  const visHasBarBorders = () => {
111
150
  const disabledCharts = ['Box Plot', 'Scatter Plot', 'Pie', 'Line']
112
151
  if (disabledCharts.includes(visualizationType)) return false
113
- return series?.some(series => series.type === 'Bar' || series.type === 'Paired Bar' || series.type === 'Deviation Bar')
152
+ return series?.some(
153
+ series => series.type === 'Bar' || series.type === 'Paired Bar' || series.type === 'Deviation Bar'
154
+ )
114
155
  }
115
156
 
116
157
  const visHasDataCutoff = () => {
@@ -134,6 +175,9 @@ export const useEditorPermissions = () => {
134
175
  const visHasLegendAxisAlign = () => {
135
176
  return visualizationType === 'Bar' && visualizationSubType === 'stacked' && config.legend.behavior === 'isolate'
136
177
  }
178
+ const visHasLegendColorCategory = () => {
179
+ return visualizationType === 'Bar' && visualizationSubType === 'regular' && config.series?.length === 1
180
+ }
137
181
 
138
182
  const visSupportsTooltipOpacity = () => {
139
183
  const disabledCharts = ['Spark Line', 'Sankey']
@@ -166,7 +210,7 @@ export const useEditorPermissions = () => {
166
210
  }
167
211
 
168
212
  const visSupportsDateCategoryAxisLabel = () => {
169
- const disabledCharts = ['Forest Plot', 'Spark Line']
213
+ const disabledCharts = ['Forest Plot', 'Spark Line', 'Bump Chart']
170
214
  if (disabledCharts.includes(visualizationType)) return false
171
215
  return true
172
216
  }
@@ -248,7 +292,7 @@ export const useEditorPermissions = () => {
248
292
 
249
293
  // implement later
250
294
  const visSupportsValueAxisLabels = () => {
251
- const disabledCharts = ['Forest Plot']
295
+ const disabledCharts = ['Forest Plot', 'Bump Chart']
252
296
  if (disabledCharts.includes(visualizationType)) return false
253
297
  return true
254
298
  }
@@ -271,6 +315,12 @@ export const useEditorPermissions = () => {
271
315
  if (disabledCharts.includes(visualizationType)) return false
272
316
  return true
273
317
  }
318
+ const visSupportsMobileChartHeight = () => {
319
+ // TODO: this is a soft release. Support should eventually match visSupportsChartHeight
320
+ const enabledCharts = ['Bar', 'Line', 'Combo', 'Area Chart']
321
+ if (enabledCharts.includes(visualizationType)) return true
322
+ return false
323
+ }
274
324
 
275
325
  const visSupportsLeftValueAxis = () => {
276
326
  const disabledCharts = ['Spark Line', 'Sankey']
@@ -285,7 +335,7 @@ export const useEditorPermissions = () => {
285
335
  }
286
336
 
287
337
  const visSupportsDateCategoryHeight = () => {
288
- const disabledCharts = ['Spark Line', 'Sankey']
338
+ const disabledCharts = ['Spark Line', 'Sankey', 'Bump Chart']
289
339
  if (disabledCharts.includes(visualizationType)) return false
290
340
  return true
291
341
  }
@@ -295,27 +345,32 @@ export const useEditorPermissions = () => {
295
345
  }
296
346
 
297
347
  const visSupportsReactTooltip = () => {
298
- if (['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType) || (visualizationType === 'Bar' && config.tooltips.singleSeries)) {
348
+ if (config.yAxis.type === 'categorical') return true
349
+ if (
350
+ ['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType) ||
351
+ (visualizationType === 'Bar' && config.tooltips.singleSeries)
352
+ ) {
299
353
  return true
300
354
  }
301
355
  }
302
356
 
303
357
  const visSupportsPreliminaryData = () => {
304
- // check if Line added in Combo
305
- const lineExist = config?.series.some(item => ['Line', 'dashed-sm', 'dashed-md', 'dashed-lg'].includes(item?.type))
306
- if (visualizationType === 'Line') {
307
- return true
308
- }
309
- if (visualizationType === 'Bar' && visualizationSubType === 'regular') {
358
+ if (['Line', 'Bar', 'Combo'].includes(visualizationType)) {
310
359
  return true
311
360
  }
312
361
 
313
- if (visualizationType === 'Combo') {
314
- return true
315
- }
316
362
  return false
317
363
  }
318
364
 
365
+ const visSupportsDynamicSeries = () => {
366
+ return (
367
+ visualizationType === 'Line' ||
368
+ visualizationType === 'Bar' ||
369
+ visualizationType === 'Scatter Plot' ||
370
+ visualizationType === 'Area Chart'
371
+ )
372
+ }
373
+
319
374
  const visHasSingleSeriesTooltip = () => {
320
375
  if (visualizationType === 'Bar' || visualizationType === 'Line') {
321
376
  return true
@@ -326,6 +381,18 @@ export const useEditorPermissions = () => {
326
381
  return false
327
382
  }
328
383
 
384
+ const visHasCategoricalAxis = () => {
385
+ if (
386
+ (visualizationType === 'Line' ||
387
+ visualizationType === 'Bar' ||
388
+ visualizationType === 'Combo' ||
389
+ visualizationType === 'Area Chart') &&
390
+ config.yAxis.type === 'categorical' &&
391
+ orientation === 'vertical'
392
+ )
393
+ return true
394
+ }
395
+
329
396
  return {
330
397
  enabledChartTypes,
331
398
  headerColors,
@@ -337,12 +404,14 @@ export const useEditorPermissions = () => {
337
404
  visHasDataSuppression,
338
405
  visHasLegend,
339
406
  visHasLegendAxisAlign,
407
+ visHasLegendColorCategory,
340
408
  visHasBrushChart,
341
409
  visHasNumbersOnBars,
342
410
  visHasaAdditionalLabelsOnBars,
343
411
  visSupportsBarSpace,
344
412
  visSupportsBarThickness,
345
413
  visSupportsChartHeight,
414
+ visSupportsMobileChartHeight,
346
415
  visSupportsDateCategoryAxis,
347
416
  visSupportsDateCategoryAxisLabel,
348
417
  visSupportsDateCategoryAxisLine,
@@ -372,6 +441,8 @@ export const useEditorPermissions = () => {
372
441
  visSupportsReactTooltip,
373
442
  visSupportsValueAxisMax,
374
443
  visSupportsValueAxisMin,
375
- visHasSingleSeriesTooltip
444
+ visSupportsDynamicSeries,
445
+ visHasSingleSeriesTooltip,
446
+ visHasCategoricalAxis
376
447
  }
377
448
  }
@@ -1,16 +1,20 @@
1
1
  import parse from 'html-react-parser'
2
2
  import { LegendOrdinal, LegendItem, LegendLabel } from '@visx/legend'
3
- import LegendCircle from '@cdc/core/components/LegendCircle'
3
+ import LegendShape from '@cdc/core/components/LegendShape'
4
4
  import Button from '@cdc/core/components/elements/Button'
5
5
  import useLegendClasses from '../../hooks/useLegendClasses'
6
6
  import { useHighlightedBars } from '../../hooks/useHighlightedBars'
7
7
  import { handleLineType } from '../../helpers/handleLineType'
8
- import { getMarginTop } from './helpers/index'
8
+
9
+ import { getMarginTop, getGradientConfig, getMarginBottom } from './helpers/index'
9
10
  import { Line } from '@visx/shape'
10
11
  import { Label } from '../../types/Label'
11
12
  import { ChartConfig } from '../../types/ChartConfig'
12
13
  import { ColorScale } from '../../types/ChartContext'
13
14
  import { forwardRef } from 'react'
15
+ import LegendSuppression from './Legend.Suppression'
16
+ import LegendGradient from '@cdc/core/components/Legend/Legend.Gradient'
17
+ import { DimensionsType } from '@cdc/core/types/Dimensions'
14
18
 
15
19
  export interface LegendProps {
16
20
  colorScale: ColorScale
@@ -22,209 +26,197 @@ export interface LegendProps {
22
26
  ref: React.Ref<() => void>
23
27
  seriesHighlight: string[]
24
28
  skipId: string
29
+ dimensions: DimensionsType // for responsive width legend
30
+ getTextWidth: (text: string, font: string) => string
25
31
  }
26
32
 
27
33
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
28
- const Legend: React.FC<LegendProps> = forwardRef(({ config, colorScale, seriesHighlight, highlight, highlightReset, currentViewport, formatLabels, skipId = 'legend' }, ref) => {
29
- const { innerClasses, containerClasses } = useLegendClasses(config)
30
- const { runtime, legend } = config
31
-
32
- if (!legend) return null
33
- const isBottomOrSmallViewport = legend?.position === 'bottom' || (['sm', 'xs', 'xxs'].includes(currentViewport) && !legend.hide)
34
-
35
- const legendClasses = {
36
- marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
37
- marginTop: isBottomOrSmallViewport && config.orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : getMarginTop(isBottomOrSmallViewport, config.brush.active)
38
- }
39
-
40
- const { HighLightedBarUtils } = useHighlightedBars(config)
41
-
42
- let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
43
-
44
- return (
45
- <aside ref={ref} style={legendClasses} id={skipId || 'legend'} className={containerClasses.join(' ')} role='region' aria-label='legend' tabIndex={0}>
46
- {legend.label && <h3>{parse(legend.label)}</h3>}
47
- {legend.description && <p>{parse(legend.description)}</p>}
34
+ const Legend: React.FC<LegendProps> = forwardRef(
35
+ (
36
+ {
37
+ config,
38
+ colorScale,
39
+ seriesHighlight,
40
+ highlight,
41
+ highlightReset,
42
+ currentViewport,
43
+ formatLabels,
44
+ skipId = 'legend',
45
+ dimensions,
46
+ getTextWidth
47
+ },
48
+ ref
49
+ ) => {
50
+ const { innerClasses, containerClasses } = useLegendClasses(config)
51
+ const { runtime, legend } = config
52
+
53
+ const isBottomOrSmallViewport =
54
+ legend?.position === 'bottom' || (['sm', 'xs', 'xxs'].includes(currentViewport) && !legend.hide)
55
+
56
+ const legendClasses = {
57
+ marginBottom: getMarginBottom(isBottomOrSmallViewport, config),
58
+
59
+ marginTop:
60
+ isBottomOrSmallViewport && config.orientation === 'horizontal'
61
+ ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px`
62
+ : getMarginTop(isBottomOrSmallViewport, config.brush.active, legend)
63
+ }
64
+
65
+ const { HighLightedBarUtils } = useHighlightedBars(config)
66
+ let highLightedLegendItems = HighLightedBarUtils.findDuplicates(config.highlightedBarValues)
67
+ if (!legend) return null
68
+ return (
69
+ <aside
70
+ ref={ref}
71
+ style={legendClasses}
72
+ id={skipId || 'legend'}
73
+ className={containerClasses.join(' ')}
74
+ role='region'
75
+ aria-label='legend'
76
+ tabIndex={0}
77
+ >
78
+ {legend.label && <h3>{parse(legend.label)}</h3>}
79
+ {legend.description && <p>{parse(legend.description)}</p>}
80
+ <LegendGradient
81
+ getTextWidth={getTextWidth}
82
+ config={config}
83
+ {...getGradientConfig(config, formatLabels, colorScale)}
84
+ dimensions={dimensions}
85
+ currentViewport={currentViewport}
86
+ />
87
+
88
+ <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
89
+ {labels => {
90
+ return (
91
+ <>
92
+ <div className={innerClasses.join(' ')}>
93
+ {formatLabels(labels as Label[]).map((label, i) => {
94
+ let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
95
+ let itemName = label.datum
96
+
97
+ // Filter excluded data keys from legend
98
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
99
+ return null
100
+ }
48
101
 
49
- <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
50
- {labels => {
51
- return (
52
- <>
53
- <div className={innerClasses.join(' ')}>
54
- {formatLabels(labels as Label[]).map((label, i) => {
55
- let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
56
- let itemName = label.datum
102
+ if (runtime.seriesLabels) {
103
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
104
+ itemName = config.runtime.seriesKeys[index]
57
105
 
58
- // Filter excluded data keys from legend
59
- if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
60
- return null
61
- }
106
+ if (runtime?.forecastingSeriesKeys?.length > 0) {
107
+ itemName = label.text
108
+ }
109
+ }
62
110
 
63
- if (runtime.seriesLabels) {
64
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
65
- itemName = config.runtime.seriesKeys[index]
111
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
112
+ className.push('inactive')
113
+ }
66
114
 
67
- if (runtime?.forecastingSeriesKeys?.length > 0) {
68
- itemName = label.text
115
+ if (config.legend.style === 'gradient') {
116
+ return <></>
69
117
  }
70
- }
71
-
72
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
73
- className.push('inactive')
74
- }
75
-
76
- return (
77
- <LegendItem
78
- className={className.join(' ')}
79
- tabIndex={0}
80
- key={`legend-quantile-${i}`}
81
- onKeyDown={e => {
82
- if (e.key === 'Enter') {
118
+
119
+ return (
120
+ <LegendItem
121
+ className={className.join(' ')}
122
+ tabIndex={0}
123
+ key={`legend-quantile-${i}`}
124
+ onKeyDown={e => {
125
+ if (e.key === 'Enter') {
126
+ e.preventDefault()
127
+ highlight(label)
128
+ }
129
+ }}
130
+ onClick={e => {
83
131
  e.preventDefault()
84
132
  highlight(label)
85
- }
86
- }}
87
- onClick={e => {
88
- e.preventDefault()
89
- highlight(label)
90
- }}
91
- role='button'
92
- >
93
- <div>
94
- {config.visualizationType === 'Line' && config.legend.lineMode ? (
95
- <svg width={40} height={20}>
96
- <Line from={{ x: 10, y: 10 }} to={{ x: 40, y: 10 }} stroke={label.value} strokeWidth={2} strokeDasharray={handleLineType(config.series[i]?.type ? config.series[i]?.type : '')} />
97
- </svg>
98
- ) : (
99
- <div style={{ display: 'flex', flexDirection: 'column' }}>
100
- <LegendCircle viewport={currentViewport} margin='0' fill={label.value} display={true} />
101
- </div>
102
- )}
103
- </div>
104
-
105
- <LegendLabel align='left' margin='0 0 0 4px'>
106
- {label.text}
107
- </LegendLabel>
108
- </LegendItem>
109
- )
110
- })}
111
-
112
- {highLightedLegendItems.map((bar, i) => {
113
- // if duplicates only return first item
114
- let className = 'legend-item'
115
- let itemName = bar.legendLabel
116
-
117
- if (!itemName) return false
118
- if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
119
- className += ' inactive'
120
- }
121
- return (
122
- <LegendItem
123
- className={className}
124
- tabIndex={0}
125
- key={`legend-quantile-${i}`}
126
- onKeyDown={e => {
127
- if (e.key === 'Enter') {
133
+ }}
134
+ role='button'
135
+ >
136
+ <div>
137
+ {config.visualizationType === 'Line' && config.legend.style === 'lines' ? (
138
+ <svg width={40} height={20}>
139
+ <Line
140
+ from={{ x: 10, y: 10 }}
141
+ to={{ x: 40, y: 10 }}
142
+ stroke={label.value}
143
+ strokeWidth={2}
144
+ strokeDasharray={handleLineType(config.series[i]?.type ? config.series[i]?.type : '')}
145
+ />
146
+ </svg>
147
+ ) : (
148
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
149
+ <LegendShape
150
+ shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
151
+ viewport={currentViewport}
152
+ margin='0'
153
+ fill={label.value}
154
+ display={true}
155
+ />
156
+ </div>
157
+ )}
158
+ </div>
159
+
160
+ <LegendLabel align='left' margin='0 0 0 4px'>
161
+ {label.text}
162
+ </LegendLabel>
163
+ </LegendItem>
164
+ )
165
+ })}
166
+
167
+ {highLightedLegendItems.map((bar, i) => {
168
+ // if duplicates only return first item
169
+ let className = 'legend-item'
170
+ let itemName = bar.legendLabel
171
+
172
+ if (!itemName) return false
173
+ if (seriesHighlight.length > 0 && false === seriesHighlight.includes(itemName)) {
174
+ className += ' inactive'
175
+ }
176
+
177
+ return (
178
+ <LegendItem
179
+ className={className}
180
+ tabIndex={0}
181
+ key={`legend-quantile-${i}`}
182
+ onKeyDown={e => {
183
+ if (e.key === 'Enter') {
184
+ e.preventDefault()
185
+ highlight(bar.legendLabel)
186
+ }
187
+ }}
188
+ onClick={e => {
128
189
  e.preventDefault()
129
190
  highlight(bar.legendLabel)
130
- }
131
- }}
132
- onClick={e => {
133
- e.preventDefault()
134
- highlight(bar.legendLabel)
135
- }}
136
- >
137
- <LegendCircle fill='transparent' borderColor={bar.color ? bar.color : `rgba(255, 102, 1)`} />{' '}
138
- <LegendLabel align='left' margin='0 0 0 4px'>
139
- {bar.legendLabel ? bar.legendLabel : bar.value}
140
- </LegendLabel>
141
- </LegendItem>
142
- )
143
- })}
144
- </div>
145
-
146
- <>
147
- {config?.preliminaryData?.some(pd => pd.label && pd.type === 'effect' && pd.style === 'Open Circles') && ['Line', 'Combo'].includes(config.visualizationType) && (
148
- <>
149
- <hr></hr>
150
- <div className={config.legend.singleRow && isBottomOrSmallViewport ? 'legend-container__inner bottom single-row' : ''}>
151
- {config?.preliminaryData?.map((pd, index) => {
152
- return (
153
- <>
154
- {pd.label && pd.type === 'effect' && pd.style && (
155
- <div key={index} className='legend-preliminary'>
156
- <span className={pd.symbol}>{pd.lineCode}</span>
157
- <p> {pd.label}</p>
158
- </div>
159
- )}
160
- </>
161
- )
162
- })}
163
- </div>
164
- </>
165
- )}
166
- {!config.legend.hideSuppressedLabels &&
167
- config?.preliminaryData?.some(pd => pd.label && pd.displayLegend && pd.type === 'suppression' && pd.value && (pd?.style || pd.symbol)) &&
168
- ((config.visualizationType === 'Bar' && config.visualizationSubType === 'regular') || config.visualizationType === 'Line' || config.visualizationType === 'Combo') && (
169
- <>
170
- <hr></hr>
171
- <div className={config.legend.singleRow && isBottomOrSmallViewport ? 'legend-container__inner bottom single-row' : ''}>
172
- {config?.preliminaryData?.map(
173
- (pd, index) =>
174
- pd.displayLegend &&
175
- pd.type === 'suppression' && (
176
- <>
177
- {config.visualizationType === 'Bar' && (
178
- <>
179
- <div key={index + 'Bar'} className={`legend-preliminary ${pd.symbol}`}>
180
- <span className={pd.symbol}>{pd.iconCode}</span>
181
- <p className={pd.type}>{pd.label}</p>
182
- </div>
183
- </>
184
- )}
185
- {config.visualizationType === 'Line' && (
186
- <>
187
- <div key={index + 'Line'} className={`legend-preliminary `}>
188
- <span>{pd.lineCode}</span>
189
- <p className={pd.type}>{pd.label}</p>
190
- </div>
191
- </>
192
- )}
193
- {config.visualizationType === 'Combo' && (
194
- <>
195
- {pd.symbol && pd.iconCode && (
196
- <div key={index + 'Combo'} className={`legend-preliminary ${pd.symbol}`}>
197
- <span className={pd.symbol}>{pd.iconCode}</span>
198
- <p className={pd.type}>{pd.label}</p>
199
- </div>
200
- )}
201
-
202
- {pd.style && pd.lineCode && (
203
- <div key={index + 'Combo'} className='legend-preliminary'>
204
- <span>{pd.lineCode}</span>
205
- <p>{pd.label}</p>
206
- </div>
207
- )}
208
- </>
209
- )}
210
- </>
211
- )
212
- )}
213
- </div>
214
- </>
215
- )}
191
+ }}
192
+ >
193
+ <LegendShape
194
+ shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
195
+ style={{ borderRadius: '0px' }}
196
+ fill='transparent'
197
+ borderColor={bar.color ? bar.color : `rgba(255, 102, 1)`}
198
+ />{' '}
199
+ <LegendLabel align='left' margin='0 0 0 4px'>
200
+ {bar.legendLabel ? bar.legendLabel : bar.value}
201
+ </LegendLabel>
202
+ </LegendItem>
203
+ )
204
+ })}
205
+ </div>
206
+
207
+ <LegendSuppression config={config} isBottomOrSmallViewport={isBottomOrSmallViewport} />
216
208
  </>
217
- </>
218
- )
219
- }}
220
- </LegendOrdinal>
221
- {seriesHighlight.length > 0 && (
222
- <Button onClick={labels => highlightReset(labels)} style={{ marginTop: '1rem' }}>
223
- Reset
224
- </Button>
225
- )}
226
- </aside>
227
- )
228
- })
209
+ )
210
+ }}
211
+ </LegendOrdinal>
212
+ {seriesHighlight.length > 0 && (
213
+ <Button onClick={labels => highlightReset(labels)} style={{ marginTop: '1rem' }}>
214
+ Reset
215
+ </Button>
216
+ )}
217
+ </aside>
218
+ )
219
+ }
220
+ )
229
221
 
230
222
  export default Legend