@cdc/chart 4.25.3-6 → 4.25.5-1

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-1a1724a1.es.js +4886 -0
  2. package/dist/cdcchart.js +50347 -75521
  3. package/index.html +1 -0
  4. package/package.json +22 -27
  5. package/src/CdcChart.tsx +1 -22
  6. package/src/CdcChartComponent.tsx +35 -21
  7. package/src/_stories/Chart.CI.stories.tsx +43 -0
  8. package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
  9. package/src/_stories/Chart.Legend.Gradient.stories.tsx +6 -0
  10. package/src/_stories/Chart.stories.tsx +7 -16
  11. package/src/_stories/_mock/bar_chart_ci_labels.json +620 -0
  12. package/src/_stories/_mock/barchart_labels.mock.json +612 -0
  13. package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
  14. package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
  15. package/{examples/private/line-issue.json → src/_stories/_mock/legend_groupBy_mock.json} +46 -69
  16. package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
  17. package/src/components/AreaChart/components/AreaChart.jsx +33 -5
  18. package/src/components/Axis/Categorical.Axis.tsx +2 -2
  19. package/src/components/BarChart/components/BarChart.Horizontal.tsx +51 -41
  20. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +19 -9
  21. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +20 -9
  22. package/src/components/BarChart/components/BarChart.Vertical.tsx +48 -31
  23. package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
  24. package/src/components/BarChart/components/context.tsx +20 -2
  25. package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
  26. package/src/components/BarChart/helpers/index.ts +5 -2
  27. package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
  28. package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +11 -47
  29. package/src/components/BoxPlot/BoxPlot.tsx +2 -1
  30. package/src/components/DeviationBar.jsx +2 -1
  31. package/src/components/EditorPanel/EditorPanel.tsx +60 -24
  32. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +34 -34
  33. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +51 -25
  34. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +10 -3
  35. package/src/components/EditorPanel/helpers/updateFieldRankByValue.ts +4 -3
  36. package/src/components/EditorPanel/useEditorPermissions.ts +1 -4
  37. package/src/components/ForestPlot/ForestPlot.tsx +2 -2
  38. package/src/components/Legend/Legend.Component.tsx +69 -58
  39. package/src/components/Legend/Legend.Suppression.tsx +12 -22
  40. package/src/components/Legend/Legend.tsx +3 -1
  41. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +40 -0
  42. package/src/components/Legend/LegendGroup/LegendGroup.tsx +103 -0
  43. package/src/components/Legend/LegendGroup/index.tsx +3 -0
  44. package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
  45. package/src/components/LineChart/LineChartProps.ts +3 -1
  46. package/src/components/LineChart/components/LineChart.Circle.tsx +77 -119
  47. package/src/components/LineChart/helpers.ts +133 -56
  48. package/src/components/LineChart/index.tsx +125 -60
  49. package/src/components/LinearChart.tsx +74 -115
  50. package/src/components/PairedBarChart.jsx +3 -2
  51. package/src/components/PieChart/PieChart.tsx +71 -91
  52. package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
  53. package/src/components/Sparkline/components/SparkLine.tsx +80 -18
  54. package/src/components/ZoomBrush.tsx +4 -4
  55. package/src/data/initial-state.js +4 -2
  56. package/src/helpers/countNumOfTicks.ts +1 -1
  57. package/src/helpers/dataHelpers.ts +31 -0
  58. package/src/helpers/sizeHelpers.ts +23 -0
  59. package/src/hooks/useMinMax.ts +21 -28
  60. package/src/hooks/useRightAxis.ts +4 -2
  61. package/src/hooks/useScales.ts +12 -14
  62. package/src/hooks/useTooltip.tsx +204 -203
  63. package/src/index.jsx +2 -2
  64. package/src/scss/main.scss +13 -6
  65. package/src/store/chart.actions.ts +1 -1
  66. package/src/types/ChartConfig.ts +7 -1
  67. package/LICENSE +0 -201
  68. package/examples/private/DEV-8850-2.json +0 -493
  69. package/examples/private/DEV-9822.json +0 -574
  70. package/examples/private/DEV-9840.json +0 -553
  71. package/examples/private/DEV-9850-3.json +0 -461
  72. package/examples/private/chart.json +0 -1084
  73. package/examples/private/ci_formatted.json +0 -202
  74. package/examples/private/ci_issue.json +0 -3016
  75. package/examples/private/completed.json +0 -634
  76. package/examples/private/dem-data-long.csv +0 -20
  77. package/examples/private/dem-data-long.json +0 -36
  78. package/examples/private/demographic_data.csv +0 -157
  79. package/examples/private/demographic_data.json +0 -2654
  80. package/examples/private/demographic_dynamic.json +0 -443
  81. package/examples/private/demographic_standard.json +0 -560
  82. package/examples/private/ehdi.json +0 -29939
  83. package/examples/private/not-loading.json +0 -360
  84. package/examples/private/test.json +0 -493
@@ -906,7 +906,7 @@ const EditorPanel = () => {
906
906
  return Object.keys(columns)
907
907
  }
908
908
 
909
- const getLegendStyleOptions = (option: 'style' | 'subStyle' | 'shapes'): string[] => {
909
+ const getLegendStyleOptions = (option: 'style' | 'subStyle' | 'shapes' | 'groupBy'): string[] => {
910
910
  const options: string[] = []
911
911
 
912
912
  switch (option) {
@@ -929,6 +929,9 @@ const EditorPanel = () => {
929
929
  options.push('linear blocks', 'smooth')
930
930
  }
931
931
 
932
+ break
933
+ case 'groupBy':
934
+ options.push(...getColumns())
932
935
  break
933
936
  }
934
937
  return options
@@ -962,7 +965,7 @@ const EditorPanel = () => {
962
965
  }
963
966
 
964
967
  const convertStateToConfig = () => {
965
- let strippedState = JSON.parse(JSON.stringify(config))
968
+ let strippedState = _.cloneDeep(config)
966
969
  if (false === missingRequiredSections(config)) {
967
970
  delete strippedState.newViz
968
971
  }
@@ -2698,7 +2701,7 @@ const EditorPanel = () => {
2698
2701
  </>
2699
2702
  )}
2700
2703
 
2701
- {isDateScale(config.xAxis) && (
2704
+ {(isDateScale(config.xAxis) || config?.visualizationType === 'Bump Chart') && (
2702
2705
  <>
2703
2706
  <p style={{ padding: '1.5em 0 0.5em', fontSize: '.9rem', lineHeight: '1rem' }}>
2704
2707
  Format how charts should parse and display your dates using{' '}
@@ -2909,27 +2912,26 @@ const EditorPanel = () => {
2909
2912
  </>
2910
2913
  )}
2911
2914
 
2912
- {config.xAxis.type === 'date' ||
2913
- (config.xAxis.type === 'date-time' && (
2914
- <>
2915
- <TextField
2916
- type='date'
2917
- section='exclusions'
2918
- fieldName='dateStart'
2919
- label='Start Date'
2920
- updateField={updateField}
2921
- value={config.exclusions.dateStart || ''}
2922
- />
2923
- <TextField
2924
- type='date'
2925
- section='exclusions'
2926
- fieldName='dateEnd'
2927
- label='End Date'
2928
- updateField={updateField}
2929
- value={config.exclusions.dateEnd || ''}
2930
- />
2931
- </>
2932
- ))}
2915
+ {(config.xAxis.type === 'date' || config.xAxis.type === 'date-time') && (
2916
+ <>
2917
+ <TextField
2918
+ type='date'
2919
+ section='exclusions'
2920
+ fieldName='dateStart'
2921
+ label='Start Date'
2922
+ updateField={updateField}
2923
+ value={config.exclusions.dateStart || ''}
2924
+ />
2925
+ <TextField
2926
+ type='date'
2927
+ section='exclusions'
2928
+ fieldName='dateEnd'
2929
+ label='End Date'
2930
+ updateField={updateField}
2931
+ value={config.exclusions.dateEnd || ''}
2932
+ />
2933
+ </>
2934
+ )}
2933
2935
  </>
2934
2936
  )}
2935
2937
 
@@ -3691,6 +3693,17 @@ const EditorPanel = () => {
3691
3693
  updateField={updateField}
3692
3694
  options={getLegendStyleOptions('style')}
3693
3695
  />
3696
+
3697
+ <Select
3698
+ value={config.legend.groupBy}
3699
+ section='legend'
3700
+ fieldName='groupBy'
3701
+ initial='Select'
3702
+ label='Legend Group By:'
3703
+ updateField={updateField}
3704
+ options={getLegendStyleOptions('groupBy')}
3705
+ />
3706
+
3694
3707
  <CheckBox
3695
3708
  tooltip={
3696
3709
  <Tooltip style={{ textTransform: 'none' }}>
@@ -3986,6 +3999,29 @@ const EditorPanel = () => {
3986
3999
  fieldName='description'
3987
4000
  label='Legend Description'
3988
4001
  />
4002
+ <CheckBox
4003
+ value={config.legend.unified}
4004
+ section='legend'
4005
+ fieldName='unified'
4006
+ label='Unified Legend'
4007
+ updateField={updateField}
4008
+ tooltip={
4009
+ <Tooltip style={{ textTransform: 'none' }}>
4010
+ <Tooltip.Target>
4011
+ <Icon
4012
+ display='question'
4013
+ style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }}
4014
+ />
4015
+ </Tooltip.Target>
4016
+ <Tooltip.Content>
4017
+ <p>
4018
+ For a chart with filters, check this option if you want the legend to contain an item for
4019
+ every series in the data set, including those that are filtered.
4020
+ </p>
4021
+ </Tooltip.Content>
4022
+ </Tooltip>
4023
+ }
4024
+ />
3989
4025
  </AccordionItemPanel>
3990
4026
  </AccordionItem>
3991
4027
  )}
@@ -113,7 +113,7 @@ const PanelGeneral: FC<PanelProps> = props => {
113
113
  )}
114
114
  </div>
115
115
  )}
116
- {(visualizationType === 'Bar' || visualizationType === 'Combo' || visualizationType === 'Area Chart') && (
116
+ {(visualizationType === 'Bar' || visualizationType === 'Combo') && (
117
117
  <Select
118
118
  value={visualizationSubType || 'Regular'}
119
119
  fieldName='visualizationSubType'
@@ -209,10 +209,8 @@ const PanelGeneral: FC<PanelProps> = props => {
209
209
  <Icon display='question' style={{ marginLeft: '0.5rem' }} />
210
210
  </Tooltip.Target>
211
211
  <Tooltip.Content>
212
- <p>
213
- Recommended set to display for Section 508 compliance.
214
- </p>
215
- <hr/>
212
+ <p>Recommended set to display for Section 508 compliance.</p>
213
+ <hr />
216
214
  <p>
217
215
  Selecting this option will <i> not </i> hide the display of "zero value", "suppressed data", or
218
216
  "missing data" indicators on the chart (if applicable).
@@ -245,35 +243,7 @@ const PanelGeneral: FC<PanelProps> = props => {
245
243
  label='Display "Zero Data" Label'
246
244
  updateField={updateField}
247
245
  />
248
- <CheckBox
249
- tooltip={
250
- <Tooltip style={{ textTransform: 'none' }}>
251
- <Tooltip.Target>
252
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
253
- </Tooltip.Target>
254
- <Tooltip.Content>
255
- {config.visualizationSubType === 'stacked' && (
256
- <p>
257
- We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose
258
- to proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g.
259
- nothing will display in chart).
260
- </p>
261
- )}
262
- {config.visualizationSubType !== 'stacked' && (
263
- <p>
264
- Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in
265
- the data table to indicate missing or undefined data values.
266
- </p>
267
- )}
268
- </Tooltip.Content>
269
- </Tooltip>
270
- }
271
- value={config.general.showMissingDataLabel}
272
- section='general'
273
- fieldName='showMissingDataLabel'
274
- label='Display "Missing Data" Label'
275
- updateField={updateField}
276
- />
246
+
277
247
  <CheckBox
278
248
  display={config.visualizationType === 'Bar' || config.visualizationType === 'Combo'}
279
249
  tooltip={
@@ -329,6 +299,36 @@ const PanelGeneral: FC<PanelProps> = props => {
329
299
  </>
330
300
  )}
331
301
 
302
+ <CheckBox
303
+ tooltip={
304
+ <Tooltip style={{ textTransform: 'none' }}>
305
+ <Tooltip.Target>
306
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
307
+ </Tooltip.Target>
308
+ <Tooltip.Content>
309
+ {config.visualizationSubType === 'stacked' && (
310
+ <p>
311
+ We do not recommend using stacked vertical/horizontal bar charts for missing data. If you choose to
312
+ proceed, selecting this option will display 'N/A' in the tooltip hover and data table (e.g. nothing
313
+ will display in chart).
314
+ </p>
315
+ )}
316
+ {config.visualizationSubType !== 'stacked' && (
317
+ <p>
318
+ Selecting this option will display 'N/A' on the Date/Category Axis, in the tooltip hover, and in the
319
+ data table to indicate missing or undefined data values.
320
+ </p>
321
+ )}
322
+ </Tooltip.Content>
323
+ </Tooltip>
324
+ }
325
+ value={config.general.showMissingDataLabel}
326
+ section='general'
327
+ fieldName='showMissingDataLabel'
328
+ label='Display "Missing Data" Label'
329
+ updateField={updateField}
330
+ />
331
+
332
332
  {visualizationType === 'Pie' && (
333
333
  <Select fieldName='pieType' label='Pie Chart Type' updateField={updateField} options={['Regular', 'Donut']} />
334
334
  )}
@@ -1,4 +1,4 @@
1
- import React, { useContext, useMemo } from 'react'
1
+ import React, { useContext } from 'react'
2
2
  import ConfigContext from '../../../../ConfigContext'
3
3
 
4
4
  // Core
@@ -17,7 +17,7 @@ import {
17
17
  AccordionItemPanel,
18
18
  AccordionItemButton
19
19
  } from 'react-accessible-accordion'
20
- import { Draggable } from '@hello-pangea/dnd'
20
+ import { Draggable, DragDropContext, Droppable } from '@hello-pangea/dnd'
21
21
  import Tooltip from '@cdc/core/components/ui/Tooltip'
22
22
 
23
23
  const SeriesContext = React.createContext({})
@@ -609,9 +609,20 @@ const SeriesButtonRemove = props => {
609
609
  }
610
610
 
611
611
  const SeriesItem = props => {
612
- const { config } = useContext(ConfigContext)
612
+ const { config, updateConfig } = useContext(ConfigContext)
613
613
  const { updateSeries, getColumns } = useContext(SeriesContext)
614
614
  const { series, getItemStyle, sortableItemStyles, chartsWithOptions, index: i } = props
615
+ const legendOrderOptions: { label: string; value: string }[] = [
616
+ { label: 'Order By Data Column', value: 'dataColumn' },
617
+ {
618
+ label: 'Ascending Alphanumeric',
619
+ value: 'asc'
620
+ },
621
+ {
622
+ label: 'Descending Alphanumeric',
623
+ value: 'desc'
624
+ }
625
+ ]
615
626
  const showDynamicCategory =
616
627
  ['Bar', 'Line'].includes(config.visualizationType) &&
617
628
  config.visualizationSubType !== 'Stacked' &&
@@ -646,28 +657,43 @@ const SeriesItem = props => {
646
657
  <AccordionItemPanel>
647
658
  <Series.Input.Name series={series} index={i} />
648
659
  {showDynamicCategory && (
649
- <Select
650
- label='Dynamic Category'
651
- value={series.dynamicCategory}
652
- options={['- Select - ', ...getColumns().filter(col => series.dataKey !== col)]}
653
- updateField={(_section, _subsection, _fieldName, value) => {
654
- if (value === '- Select -') value = ''
655
- updateSeries(i, value, 'dynamicCategory')
656
- }}
657
- tooltip={
658
- <Tooltip style={{ textTransform: 'none' }}>
659
- <Tooltip.Target>
660
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
661
- </Tooltip.Target>
662
- <Tooltip.Content>
663
- <p>
664
- This field is Optional. If you have a dynamic data series you can select the category
665
- field here. You can only add one dynamic category per visualization.
666
- </p>
667
- </Tooltip.Content>
668
- </Tooltip>
669
- }
670
- />
660
+ <>
661
+ <Select
662
+ label='Dynamic Category'
663
+ value={series.dynamicCategory}
664
+ options={['- Select - ', ...getColumns().filter(col => series.dataKey !== col)]}
665
+ updateField={(_section, _subsection, _fieldName, value) => {
666
+ if (value === '- Select -') value = ''
667
+ updateSeries(i, value, 'dynamicCategory')
668
+ }}
669
+ tooltip={
670
+ <Tooltip style={{ textTransform: 'none' }}>
671
+ <Tooltip.Target>
672
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
673
+ </Tooltip.Target>
674
+ <Tooltip.Content>
675
+ <p>
676
+ This field is Optional. If you have a dynamic data series you can select the category
677
+ field here. You can only add one dynamic category per visualization.
678
+ </p>
679
+ </Tooltip.Content>
680
+ </Tooltip>
681
+ }
682
+ />
683
+
684
+ <>
685
+ <Select
686
+ value={config.legend.order}
687
+ options={legendOrderOptions}
688
+ section='legend'
689
+ fieldName='order'
690
+ updateField={(_section, _subsection, _fieldName, value) => {
691
+ updateConfig({ ...config, legend: { ...config.legend, order: value } })
692
+ }}
693
+ label='Dynamic Series Order'
694
+ />
695
+ </>
696
+ </>
671
697
  )}
672
698
  <Series.Input.Weight series={series} index={i} />
673
699
  <Series.Dropdown.SeriesType series={series} index={i} />
@@ -22,6 +22,7 @@ import { useEditorPermissions } from '../../useEditorPermissions.js'
22
22
  import { useEditorPanelContext } from '../../EditorPanelContext.js'
23
23
  import ConfigContext from '../../../../ConfigContext.js'
24
24
  import { PanelProps } from '../PanelProps'
25
+ import { LineChartConfig } from '../../../../types/ChartConfig'
25
26
 
26
27
  const PanelVisual: FC<PanelProps> = props => {
27
28
  const { config, updateConfig, colorPalettes, twoColorPalette } = useContext<ChartContext>(ConfigContext)
@@ -147,9 +148,7 @@ const PanelVisual: FC<PanelProps> = props => {
147
148
  />
148
149
  </Tooltip.Target>
149
150
  <Tooltip.Content>
150
- <p>
151
- Recommended set to display for Section 508 compliance.
152
- </p>
151
+ <p>Recommended set to display for Section 508 compliance.</p>
153
152
  </Tooltip.Content>
154
153
  </Tooltip>
155
154
  }
@@ -222,6 +221,14 @@ const PanelVisual: FC<PanelProps> = props => {
222
221
  updateField={updateField}
223
222
  options={['Same as Line', 'Lighter than Line']}
224
223
  />
224
+ <CheckBox
225
+ value={!(config as LineChartConfig).isolatedDotsSameSize}
226
+ fieldName='isolatedDotsSameSize'
227
+ label='Accentuate isolated data points'
228
+ updateField={(section, subsection, fieldname, value) =>
229
+ updateField(section, subsection, fieldname, !value)
230
+ }
231
+ />
225
232
  </>
226
233
  )}
227
234
  {/* eslint-disable */}
@@ -30,9 +30,10 @@ export const updateFieldRankByValue = (
30
30
  newConfig.rankByValue = newValue
31
31
 
32
32
  if (config.rankByValue && !newValue) {
33
- const CIkeys: string[] = Object.values(config.confidenceKeys) as string[]
34
- const seriesKeys: string[] = config.series.map(s => s.dataKey)
35
- const keysToClean: string[] = seriesKeys.concat(CIkeys)
33
+ const CIkeys: string[] = Object.values(_.get(config, 'confidenceKeys', {})) as string[]
34
+ const seriesKeys: string[] = _.get(config, 'series', []).map((s: any) => s.dataKey)
35
+ const keysToClean: string[] = [...(seriesKeys ?? []), ...(CIkeys ?? [])]
36
+
36
37
  const cleanData = config?.xAxis?.dataKey
37
38
  ? transform.cleanData(config.data, config.xAxis.dataKey, keysToClean)
38
39
  : config.data
@@ -358,10 +358,7 @@ export const useEditorPermissions = () => {
358
358
 
359
359
  const visSupportsReactTooltip = () => {
360
360
  if (config.yAxis.type === 'categorical') return true
361
- if (
362
- ['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType) ||
363
- (visualizationType === 'Bar' && config.tooltips.singleSeries)
364
- ) {
361
+ if (['Deviation Bar', 'Box Plot', 'Scatter Plot', 'Paired Bar'].includes(visualizationType)) {
365
362
  return true
366
363
  }
367
364
  }
@@ -14,7 +14,7 @@ import { type ChartContext } from '@cdc/chart/src/types/ChartContext'
14
14
 
15
15
  // cdc
16
16
  import ConfigContext from '../../ConfigContext'
17
- import { appFontSize } from '@cdc/core/helpers/cove/fontSettings'
17
+ import { APP_FONT_SIZE } from '@cdc/core/helpers/constants'
18
18
 
19
19
  const ForestPlot = ({
20
20
  xScale,
@@ -254,7 +254,7 @@ const ForestPlot = ({
254
254
  <LinePath
255
255
  data={regressionPoints}
256
256
  x={d => d.x}
257
- y={d => d.y - appFontSize / 2}
257
+ y={d => d.y - APP_FONT_SIZE / 2}
258
258
  stroke='black'
259
259
  strokeWidth={2}
260
260
  fill={'black'}
@@ -15,6 +15,8 @@ import LegendGradient from '@cdc/core/components/Legend/Legend.Gradient'
15
15
  import { DimensionsType } from '@cdc/core/types/Dimensions'
16
16
  import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
17
17
  import LegendLineShape from './LegendLine.Shape'
18
+ import LegendGroup from './LegendGroup'
19
+ import { getSeriesWithData } from '../../helpers/dataHelpers'
18
20
 
19
21
  const LEGEND_PADDING = 36
20
22
 
@@ -29,6 +31,7 @@ export interface LegendProps {
29
31
  seriesHighlight: string[]
30
32
  skipId: string
31
33
  dimensions: DimensionsType // for responsive width legend
34
+ transformedData: any
32
35
  }
33
36
 
34
37
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
@@ -43,12 +46,17 @@ const Legend: React.FC<LegendProps> = forwardRef(
43
46
  currentViewport,
44
47
  formatLabels,
45
48
  skipId = 'legend',
46
- dimensions
49
+ dimensions,
50
+ transformedData: data
47
51
  },
48
52
  ref
49
53
  ) => {
50
54
  const { innerClasses, containerClasses } = getLegendClasses(config)
51
55
  const { runtime, legend } = config
56
+ const { series } = runtime
57
+
58
+ const seriesWithData = getSeriesWithData(config)
59
+ const dontFilterLegendItems = !series.length || legend.unified || !seriesWithData.length
52
60
 
53
61
  const isLegendBottom =
54
62
  legend?.position === 'bottom' ||
@@ -84,77 +92,80 @@ const Legend: React.FC<LegendProps> = forwardRef(
84
92
  dimensions={dimensions}
85
93
  parentPaddingToSubtract={legend.hideBorder ? 0 : LEGEND_PADDING}
86
94
  />
95
+ <LegendGroup formatLabels={formatLabels} />
87
96
 
88
97
  <LegendOrdinal scale={colorScale} itemDirection='row' labelMargin='0 20px 0 0' shapeMargin='0 10px 0'>
89
98
  {labels => {
90
99
  return (
91
100
  <>
92
101
  <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
- }
102
+ {formatLabels(labels as Label[])
103
+ .filter(label => dontFilterLegendItems || seriesWithData.includes(label.datum))
104
+ .map((label, i) => {
105
+ let className = ['legend-item', `legend-text--${label.text.replace(' ', '').toLowerCase()}`]
106
+ let itemName = label.datum
107
+
108
+ // Filter excluded data keys from legend
109
+ if (config.exclusions.active && config.exclusions.keys?.includes(itemName)) {
110
+ return null
111
+ }
101
112
 
102
- if (runtime.seriesLabels) {
103
- let index = config.runtime.seriesLabelsAll.indexOf(itemName)
104
- itemName = config.runtime.seriesKeys[index]
113
+ if (runtime.seriesLabels) {
114
+ let index = config.runtime.seriesLabelsAll.indexOf(itemName)
115
+ itemName = config.runtime.seriesKeys[index]
105
116
 
106
- if (runtime?.forecastingSeriesKeys?.length > 0) {
107
- itemName = label.text
117
+ if (runtime?.forecastingSeriesKeys?.length > 0) {
118
+ itemName = label.text
119
+ }
108
120
  }
109
- }
110
121
 
111
- if (seriesHighlight.length) {
112
- if (!seriesHighlight.includes(itemName)) {
113
- className.push('inactive')
114
- } else className.push('highlighted')
115
- }
122
+ if (seriesHighlight.length) {
123
+ if (!seriesHighlight.includes(itemName)) {
124
+ className.push('inactive')
125
+ } else className.push('highlighted')
126
+ }
116
127
 
117
- if (config.legend.style === 'gradient') {
118
- return <></>
119
- }
128
+ if (config.legend.style === 'gradient' || config.legend.groupBy) {
129
+ return <></>
130
+ }
120
131
 
121
- return (
122
- <LegendItem
123
- className={className.join(' ')}
124
- tabIndex={0}
125
- key={`legend-quantile-${i}`}
126
- onKeyDown={e => {
127
- if (e.key === 'Enter') {
132
+ return (
133
+ <LegendItem
134
+ className={className.join(' ')}
135
+ tabIndex={0}
136
+ key={`legend-quantile-${i}`}
137
+ onKeyDown={e => {
138
+ if (e.key === 'Enter') {
139
+ e.preventDefault()
140
+ highlight(label)
141
+ }
142
+ }}
143
+ onClick={e => {
128
144
  e.preventDefault()
129
145
  highlight(label)
130
- }
131
- }}
132
- onClick={e => {
133
- e.preventDefault()
134
- highlight(label)
135
- }}
136
- role='button'
137
- >
138
- <>
139
- {config.visualizationType === 'Line' && config.legend.style === 'lines' ? (
140
- <React.Fragment>
141
- <LegendLineShape index={i} label={label} config={config} />
142
- </React.Fragment>
143
- ) : (
144
- <>
145
- <LegendShape
146
- shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
147
- fill={label.value}
148
- />
149
- </>
150
- )}
151
- </>
152
- <LegendLabel align='left' className='m-0'>
153
- {label.text}
154
- </LegendLabel>
155
- </LegendItem>
156
- )
157
- })}
146
+ }}
147
+ role='button'
148
+ >
149
+ <>
150
+ {config.visualizationType === 'Line' && config.legend.style === 'lines' ? (
151
+ <React.Fragment>
152
+ <LegendLineShape index={i} label={label} config={config} />
153
+ </React.Fragment>
154
+ ) : (
155
+ <>
156
+ <LegendShape
157
+ shape={config.legend.style === 'boxes' ? 'square' : 'circle'}
158
+ fill={label.value}
159
+ />
160
+ </>
161
+ )}
162
+ </>
163
+ <LegendLabel align='left' className='m-0'>
164
+ {parse(label.text)}
165
+ </LegendLabel>
166
+ </LegendItem>
167
+ )
168
+ })}
158
169
 
159
170
  {highLightedLegendItems.map((bar, i) => {
160
171
  // if duplicates only return first item
@@ -1,7 +1,6 @@
1
1
  import React from 'react'
2
2
  import { ChartConfig } from '../../types/ChartConfig'
3
- import Icon from '@cdc/core/components/ui/Icon'
4
- import { Tooltip as ReactTooltip } from 'react-tooltip'
3
+ import RichTooltip from '@cdc/core/components/RichTooltip/RichTooltip'
5
4
  interface LegendProps {
6
5
  config: ChartConfig
7
6
  isLegendBottom: boolean
@@ -35,10 +34,6 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
35
34
  </div>
36
35
  )
37
36
  )
38
- const handleLinkClick = event => {
39
- // prevent defintion link to change URl
40
- event.preventDefault()
41
- }
42
37
 
43
38
  const renderSuppressedItems = () => {
44
39
  const getStyle = displayGray => {
@@ -121,27 +116,22 @@ const LegendSuppression: React.FC<LegendProps> = ({ config, isLegendBottom }) =>
121
116
  </React.Fragment>
122
117
  )}
123
118
  {shouldShowSuppressedInfo() && (
124
- <div className='legend-container__outer definition-link'>
125
- <Icon alt='info-icon' display='info' />
119
+ <div className='legend-container__outer link-container'>
126
120
  <p>
127
121
  This chart contains
128
- <a // prettier-ignore
129
- onClick={handleLinkClick}
130
- data-tooltip-content='Data is suppressed to maintain statistical reliability. This occurs when the number of respondents or reported values does not meet the minimum reporting threshold.'
131
- data-tooltip-id='my-tooltip'
132
- href='no-router-link'
133
- >
134
- suppressed data
135
- </a>
122
+ <RichTooltip
123
+ tooltipContent={`Data is
124
+ suppressed to maintain statistical reliability.
125
+ This occurs when the number of respondents or
126
+ reported values does not meet the minimum
127
+ reporting threshold.`}
128
+ linkText='suppressed data'
129
+ href={null}
130
+ tooltipOpacity={config.tooltips.opacity}
131
+ />
136
132
  </p>
137
133
  </div>
138
134
  )}
139
-
140
- <ReactTooltip // prettier-ignore
141
- id='my-tooltip'
142
- variant='light'
143
- style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black', maxWidth: '100%' }}
144
- />
145
135
  </React.Fragment>
146
136
  )
147
137
  }