@cdc/chart 4.25.6 → 4.25.8

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 (47) hide show
  1. package/dist/cdcchart.js +53500 -32825
  2. package/package.json +3 -2
  3. package/src/CdcChart.tsx +9 -2
  4. package/src/CdcChartComponent.tsx +30 -12
  5. package/src/_stories/Chart.BoxPlot.stories.tsx +35 -0
  6. package/src/_stories/Chart.stories.tsx +0 -7
  7. package/src/_stories/Chart.tooltip.stories.tsx +35 -275
  8. package/src/_stories/_mock/bar-chart-suppressed.json +2 -80
  9. package/src/_stories/_mock/boxplot_multiseries.json +252 -166
  10. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +1 -1
  11. package/src/components/AreaChart/components/AreaChart.jsx +4 -8
  12. package/src/components/BarChart/components/BarChart.Horizontal.tsx +45 -7
  13. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +1 -1
  14. package/src/components/BarChart/components/BarChart.Vertical.tsx +36 -4
  15. package/src/components/BoxPlot/BoxPlot.Horizontal.tsx +131 -0
  16. package/src/components/BoxPlot/{BoxPlot.tsx → BoxPlot.Vertical.tsx} +4 -4
  17. package/src/components/BoxPlot/helpers/index.ts +32 -12
  18. package/src/components/Brush/BrushChart.tsx +65 -10
  19. package/src/components/Brush/BrushController.tsx +71 -0
  20. package/src/components/Brush/types.tsx +8 -0
  21. package/src/components/BrushChart.tsx +1 -1
  22. package/src/components/EditorPanel/EditorPanel.tsx +19 -14
  23. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +2 -2
  24. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +2 -2
  25. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +2 -34
  26. package/src/components/Forecasting/{Forecasting.jsx → Forecasting.tsx} +32 -12
  27. package/src/components/Legend/Legend.Component.tsx +16 -1
  28. package/src/components/Legend/Legend.tsx +3 -1
  29. package/src/components/Legend/LegendGroup/LegendGroup.tsx +1 -0
  30. package/src/components/Legend/helpers/index.ts +2 -2
  31. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +27 -26
  32. package/src/components/LineChart/helpers.ts +7 -7
  33. package/src/components/LinearChart.tsx +130 -75
  34. package/src/data/initial-state.js +12 -15
  35. package/src/helpers/countNumOfTicks.ts +4 -19
  36. package/src/helpers/filterAndShiftLinearDateTicks.ts +58 -0
  37. package/src/helpers/getBridgedData.ts +13 -0
  38. package/src/helpers/tests/getBridgedData.test.ts +64 -0
  39. package/src/hooks/useScales.ts +42 -42
  40. package/src/hooks/useTooltip.tsx +3 -2
  41. package/src/index.jsx +6 -1
  42. package/src/scss/main.scss +2 -4
  43. package/src/store/chart.actions.ts +2 -2
  44. package/src/store/chart.reducer.ts +4 -12
  45. package/src/types/ChartConfig.ts +1 -6
  46. package/src/components/BoxPlot/index.tsx +0 -3
  47. package/src/components/Brush/BrushController..tsx +0 -39
@@ -0,0 +1,71 @@
1
+ import { useState, useEffect, useContext, useRef } from 'react'
2
+ import ConfigContext, { ChartDispatchContext } from '../../ConfigContext'
3
+ import BrushChart from './BrushChart'
4
+ import { isDateScale } from '@cdc/core/helpers/cove/date'
5
+ import { BrushRef } from './types'
6
+
7
+ const BrushController = ({ yMax, xMax }) => {
8
+ const { tableData, config, parseDate, dashboardConfig, formatDate } = useContext(ConfigContext)
9
+ const [brushHandleProps, setBrushHandleProps] = useState({
10
+ startPos: 0,
11
+ endPos: 0,
12
+ startValue: '',
13
+ endValue: '',
14
+ xMax: xMax
15
+ })
16
+ const dataKey = config.xAxis.dataKey
17
+ const [brushKey, setBrushKey] = useState(0)
18
+ const dispatch = useContext(ChartDispatchContext)
19
+ const sharedFilters = dashboardConfig?.dashboard?.sharedFilters ?? []
20
+ const isDashboardFilters = sharedFilters?.length > 0
21
+ const brushRef = useRef<BrushRef | null>(null)
22
+
23
+ const [brushPosition, setBrushPosition] = useState({
24
+ start: { x: 0 },
25
+ end: { x: xMax }
26
+ })
27
+
28
+ const handleBrushChange = (bounds: any) => {
29
+ if (!bounds) return dispatch({ type: 'SET_BRUSH_DATA', payload: [] })
30
+ const filteredValues = bounds?.xValues?.filter(val => val !== undefined)
31
+ if (filteredValues?.length === 0) dispatch({ type: 'SET_BRUSH_DATA', payload: [] })
32
+ const selected = bounds?.xValues || []
33
+
34
+ const filteredData = tableData.filter(row => selected.includes(row[dataKey]))
35
+ const endValue = filteredValues
36
+ .slice()
37
+ .reverse()
38
+ .find(item => item !== undefined)
39
+ const startValue = filteredValues.find(item => item !== undefined)
40
+ const formatIfDate = value => (isDateScale(config.runtime.xAxis) ? formatDate(parseDate(value)) : value)
41
+
42
+ setBrushHandleProps(prev => ({
43
+ ...prev,
44
+ startPos: brushRef.current?.state.start.x,
45
+ endPos: brushRef.current?.state.end.x,
46
+ endValue: formatIfDate(endValue),
47
+ startValue: formatIfDate(startValue)
48
+ }))
49
+ dispatch({ type: 'SET_BRUSH_DATA', payload: filteredData })
50
+ }
51
+
52
+ // whenever your other filters change:
53
+ useEffect(() => {
54
+ setBrushPosition({ start: { x: 0 }, end: { x: xMax } })
55
+ dispatch({ type: 'SET_BRUSH_DATA', payload: [] })
56
+ setBrushKey(k => k + 1)
57
+ }, [config.filters, config.exclusions, config.brush?.active, isDashboardFilters])
58
+
59
+ return (
60
+ <BrushChart
61
+ brushRef={brushRef}
62
+ brushHandleProps={brushHandleProps}
63
+ xMax={xMax}
64
+ yMax={yMax}
65
+ brushPosition={brushPosition}
66
+ onBrushChange={handleBrushChange}
67
+ brushKey={brushKey}
68
+ />
69
+ )
70
+ }
71
+ export default BrushController
@@ -0,0 +1,8 @@
1
+ export type BrushRef = {
2
+ reset: () => void
3
+ state: {
4
+ start: { x: number; y: number }
5
+ end: { x: number; y: number }
6
+ isBrushing: boolean
7
+ }
8
+ }
@@ -120,7 +120,7 @@ const BrushChart = ({ xMax, yMax }: BrushChartProps) => {
120
120
  svg.call(brushHandle, selection, formattedStartDate, formattedEndDate)
121
121
 
122
122
  const payload = {
123
- active: config.brush.active,
123
+ active: config.xAxis.brushActive,
124
124
  isBrushing: isUserBrushing,
125
125
  data: finalData
126
126
  }
@@ -20,6 +20,7 @@ import DataTableEditor from '@cdc/core/components/EditorPanel/DataTableEditor'
20
20
  import VizFilterEditor from '@cdc/core/components/EditorPanel/VizFilterEditor'
21
21
  import Tooltip from '@cdc/core/components/ui/Tooltip'
22
22
  import { Select, TextField, CheckBox } from '@cdc/core/components/EditorPanel/Inputs'
23
+ import MultiSelect from '@cdc/core/components/MultiSelect'
23
24
  import { viewports } from '@cdc/core/helpers/getViewport'
24
25
  import { approvedCurveTypes } from '@cdc/core/helpers/lineChartHelpers'
25
26
 
@@ -109,7 +110,7 @@ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, dat
109
110
  let preliminaryData = config.preliminaryData ? [...config.preliminaryData] : []
110
111
  const defaultValues = {
111
112
  type: defaultType,
112
- seriesKey: '',
113
+ seriesKeys: [],
113
114
  label: 'Suppressed',
114
115
  column: '',
115
116
  value: '',
@@ -159,7 +160,7 @@ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, dat
159
160
  displayTable,
160
161
  displayTooltip,
161
162
  label,
162
- seriesKey,
163
+ seriesKeys,
163
164
  style,
164
165
  symbol,
165
166
  type,
@@ -384,14 +385,18 @@ const PreliminaryData: React.FC<PreliminaryProps> = ({ config, updateConfig, dat
384
385
  </>
385
386
  ) : (
386
387
  <>
387
- <Select
388
- value={seriesKey}
389
- initial='Select'
390
- fieldName='seriesKey'
391
- label='ASSOCIATE TO SERIES'
392
- updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
393
- options={config.runtime.lineSeriesKeys ?? config.runtime?.seriesKeys}
394
- />
388
+ <label>
389
+ <span className='edit-label'>ASSOCIATE TO THESE SERIES</span>
390
+ <MultiSelect
391
+ fieldName='seriesKeys'
392
+ updateField={(_, __, fieldName, value) => update(fieldName, value, i)}
393
+ options={(config.runtime.lineSeriesKeys ?? config.runtime?.seriesKeys).map(c => ({
394
+ label: c,
395
+ value: c
396
+ }))}
397
+ selected={seriesKeys}
398
+ />
399
+ </label>
395
400
  <Select
396
401
  value={column}
397
402
  initial='Select'
@@ -1193,7 +1198,7 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
1193
1198
  if (isDebug && config?.series?.length === 0) {
1194
1199
  let setdatacol = setDataColumn()
1195
1200
  if (setdatacol !== '') addNewSeries(setdatacol)
1196
- if (isDebug) console.error('### COVE DEBUG: Chart: Setting default datacol=', setdatacol) // eslint-disable-line
1201
+ if (isDebug) console.log('### COVE DEBUG: Chart: Setting default datacol=', setdatacol) // eslint-disable-line
1197
1202
  }
1198
1203
 
1199
1204
  const chartsWithOptions = [
@@ -2926,9 +2931,9 @@ const EditorPanel: React.FC<ChartEditorPanelProps> = ({ datasets }) => {
2926
2931
  />
2927
2932
  {visHasBrushChart() && (
2928
2933
  <CheckBox
2929
- value={config.brush.active}
2930
- section='brush'
2931
- fieldName='active'
2934
+ value={config.xAxis.brushActive}
2935
+ section='xAxis'
2936
+ fieldName='brushActive'
2932
2937
  label='Brush Slider '
2933
2938
  updateField={updateFieldDeprecated}
2934
2939
  tooltip={
@@ -67,8 +67,8 @@ const PanelAnnotate: React.FC<PanelProps> = props => {
67
67
  config.xAxis.type === 'date'
68
68
  ? new Date(config?.data?.[0]?.[config.xAxis.dataKey]).getTime()
69
69
  : config.xAxis.type === 'categorical'
70
- ? '1/15/2016'
71
- : '',
70
+ ? '1/15/2016'
71
+ : '',
72
72
  yKey: '',
73
73
  dx: 20,
74
74
  dy: -20,
@@ -131,7 +131,7 @@ const PanelGeneral: FC<PanelProps> = props => {
131
131
  options={Object.keys(approvedCurveTypes)}
132
132
  />
133
133
  )}
134
- {visualizationType === 'Bar' && (
134
+ {(visualizationType === 'Bar' || visualizationType === 'Box Plot') && (
135
135
  <Select
136
136
  value={config.orientation || 'vertical'}
137
137
  fieldName='orientation'
@@ -178,7 +178,7 @@ const PanelGeneral: FC<PanelProps> = props => {
178
178
  options={['standard', 'shallow', 'finger']}
179
179
  />
180
180
  )}
181
- {visualizationType === 'Bar' && config.orientation === 'horizontal' && (
181
+ {(visualizationType === 'Bar' || visualizationType === 'Box Plot') && config.orientation === 'horizontal' && (
182
182
  <Select
183
183
  value={config.yAxis.labelPlacement || 'Below Bar'}
184
184
  section='yAxis'
@@ -281,6 +281,7 @@ const SeriesDropdownConfidenceInterval = props => {
281
281
  const { config, updateConfig } = useContext(ConfigContext)
282
282
  const { series, index } = props
283
283
  const { getColumns } = useContext(SeriesContext)
284
+
284
285
  if (series.type !== 'Forecasting') return
285
286
 
286
287
  return (
@@ -289,18 +290,6 @@ const SeriesDropdownConfidenceInterval = props => {
289
290
  <fieldset>
290
291
  <Accordion allowZeroExpanded>
291
292
  {series?.confidenceIntervals?.map((ciGroup, ciIndex) => {
292
- const showInTooltip = ciGroup.showInTooltip ? ciGroup.showInTooltip : false
293
-
294
- const updateShowInTooltip = (e, seriesIndex, ciIndex) => {
295
- e.preventDefault()
296
- let copiedSeries = [...config.series]
297
- copiedSeries[seriesIndex].confidenceIntervals[ciIndex].showInTooltip = !showInTooltip
298
- updateConfig({
299
- ...config,
300
- series: copiedSeries
301
- })
302
- }
303
-
304
293
  return (
305
294
  <AccordionItem className='series-item series-item--chart' key={`${ciIndex}`}>
306
295
  <AccordionItemHeading className='series-item__title'>
@@ -312,6 +301,7 @@ const SeriesDropdownConfidenceInterval = props => {
312
301
  onClick={e => {
313
302
  e.preventDefault()
314
303
  const copiedIndex = [...config.series[index].confidenceIntervals]
304
+
315
305
  copiedIndex.splice(ciIndex, 1)
316
306
  const copyOfSeries = [...config.series] // copy the entire series array
317
307
  copyOfSeries[index] = { ...copyOfSeries[index], confidenceIntervals: [...copiedIndex] }
@@ -327,28 +317,6 @@ const SeriesDropdownConfidenceInterval = props => {
327
317
  </>
328
318
  </AccordionItemHeading>
329
319
  <AccordionItemPanel>
330
- <div className='input-group'>
331
- <label htmlFor='showInTooltip'>Show In Tooltip</label>
332
- <div
333
- className={'cove-input__checkbox--small'}
334
- onClick={e => updateShowInTooltip(e, index, ciIndex)}
335
- >
336
- <div
337
- className={`cove-input__checkbox-box${'blue' ? ' custom-color' : ''}`}
338
- style={{ backgroundColor: '' }}
339
- >
340
- {showInTooltip && <Check className='' style={{ fill: '#025eaa' }} />}
341
- </div>
342
- <input
343
- className='cove-input--hidden'
344
- type='checkbox'
345
- name={'showInTooltip'}
346
- checked={showInTooltip ? showInTooltip : false}
347
- readOnly
348
- />
349
- </div>
350
- </div>
351
-
352
320
  <InputSelect
353
321
  initial='Select an option'
354
322
  value={
@@ -1,9 +1,10 @@
1
1
  import React, { useContext } from 'react'
2
-
2
+ import { replace } from 'lodash'
3
3
  // cdc
4
4
  import ConfigContext from '../../ConfigContext'
5
5
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
6
  import { colorPalettesChart, sequentialPalettes } from '@cdc/core/data/colorPalettes'
7
+ import { getBridgedData } from '../../helpers/getBridgedData'
7
8
 
8
9
  // visx & d3
9
10
  import { curveMonotoneX } from '@visx/curve'
@@ -11,7 +12,7 @@ import { Bar, Area, LinePath } from '@visx/shape'
11
12
  import { Group } from '@visx/group'
12
13
 
13
14
  const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, handleTooltipMouseOff }) => {
14
- const { transformedData: data, rawData, config, seriesHighlight } = useContext(ConfigContext)
15
+ const { transformedData: data, rawData, config, seriesHighlight, parseDate } = useContext(ConfigContext)
15
16
  const { xAxis, yAxis, legend, runtime } = config
16
17
  const DEBUG = false
17
18
 
@@ -23,12 +24,17 @@ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, ha
23
24
  if (!group || !group.stages) return false
24
25
  return group.stages.map((stage, stageIndex) => {
25
26
  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
27
+ let transparentArea =
28
+ behavior === 'highlight' && seriesHighlight.length > 0 && seriesHighlight.indexOf(stage.key) === -1
29
+ let displayArea =
30
+ behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(stage.key) !== -1
31
+ const bridgedData = getBridgedData(stage.key, group.stageColumn, rawData)
29
32
 
30
33
  return (
31
- <Group className={`forecasting-areas-combo-${index}`} key={`forecasting-areas--stage-${stage.key.replaceAll(' ', '-')}-${index}`}>
34
+ <Group
35
+ className={`forecasting-areas-combo-${index}`}
36
+ key={`forecasting-areas--stage-${replace(stage.key, / /g, '—')}-${index}`}
37
+ >
32
38
  {group.confidenceIntervals?.map((ciGroup, ciGroupIndex) => {
33
39
  const palette = sequentialPalettes[stage.color] || colorPalettesChart[stage.color] || false
34
40
 
@@ -44,13 +50,19 @@ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, ha
44
50
 
45
51
  if (ciGroup.high === '' || ciGroup.low === '') return
46
52
  return (
47
- <Group key={`forecasting-areas--stage-${stage.key.replaceAll(' ', '-')}--group-${stageIndex}-${ciGroupIndex}`}>
53
+ <Group
54
+ key={`forecasting-areas--stage-${replace(
55
+ stage.key,
56
+ / /g,
57
+ '—'
58
+ )}--group-${stageIndex}-${ciGroupIndex}`}
59
+ >
48
60
  {/* prettier-ignore */}
49
61
  <Area
50
62
  curve={curveMonotoneX}
51
- data={groupData}
63
+ data={bridgedData}
52
64
  fill={getFill()}
53
- opacity={transparentArea ? 0.1 : .5}
65
+ opacity={transparentArea? 0.1 : 0.5 }
54
66
  x={d => xScale(Date.parse(d[xAxis.dataKey]))}
55
67
  y0={d => yScale(d[ciGroup.low])}
56
68
  y1={d => yScale(d[ciGroup.high])}
@@ -59,10 +71,10 @@ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, ha
59
71
  {ciGroupIndex === 0 && (
60
72
  <>
61
73
  {/* 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} />
74
+ <LinePath data={bridgedData} 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
75
 
64
76
  {/* 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} />
77
+ <LinePath data={bridgedData} 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
78
  </>
67
79
  )}
68
80
  </Group>
@@ -73,7 +85,15 @@ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, ha
73
85
  })
74
86
  })}
75
87
  <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} />
88
+ <Bar
89
+ key={'bars'}
90
+ width={Number(width)}
91
+ height={Number(height)}
92
+ fill={DEBUG ? 'red' : 'transparent'}
93
+ fillOpacity={0.05}
94
+ onMouseMove={e => handleTooltipMouseOver(e, data)}
95
+ onMouseOut={handleTooltipMouseOff}
96
+ />
77
97
  </Group>
78
98
  </Group>
79
99
  </ErrorBoundary>
@@ -17,6 +17,7 @@ import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
17
17
  import LegendLineShape from './LegendLine.Shape'
18
18
  import LegendGroup from './LegendGroup'
19
19
  import { getSeriesWithData } from '../../helpers/dataHelpers'
20
+ import { publishAnalyticsEvent } from '@cdc/core/helpers/metrics/helpers'
20
21
 
21
22
  const LEGEND_PADDING = 36
22
23
 
@@ -32,6 +33,7 @@ export interface LegendProps {
32
33
  skipId: string
33
34
  dimensions: DimensionsType // for responsive width legend
34
35
  transformedData: any
36
+ interactionLabel: string
35
37
  }
36
38
 
37
39
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex, jsx-a11y/no-static-element-interactions */
@@ -47,7 +49,8 @@ const Legend: React.FC<LegendProps> = forwardRef(
47
49
  formatLabels,
48
50
  skipId = 'legend',
49
51
  dimensions,
50
- transformedData: data
52
+ transformedData: data,
53
+ interactionLabel = ''
51
54
  },
52
55
  ref
53
56
  ) => {
@@ -137,11 +140,23 @@ const Legend: React.FC<LegendProps> = forwardRef(
137
140
  onKeyDown={e => {
138
141
  if (e.key === 'Enter') {
139
142
  e.preventDefault()
143
+ publishAnalyticsEvent(
144
+ `chart_legend_item_toggled--${legend.behavior}-mode`,
145
+ 'keydown',
146
+ `${interactionLabel}|${label.text}`,
147
+ 'chart'
148
+ )
140
149
  highlight(label)
141
150
  }
142
151
  }}
143
152
  onClick={e => {
144
153
  e.preventDefault()
154
+ publishAnalyticsEvent(
155
+ `chart_legend_item_toggled--${legend.behavior}-mode`,
156
+ 'click',
157
+ `${interactionLabel}|${label.text}`,
158
+ 'chart'
159
+ )
145
160
  highlight(label)
146
161
  }}
147
162
  role='button'
@@ -21,7 +21,8 @@ const Legend = forwardRef((props, ref) => {
21
21
  transformedData
22
22
  } = useContext(ConfigContext)
23
23
  if (!config.legend) return null
24
- // create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default.
24
+ // create fn to reverse labels while legend is Bottom. Legend-right , legend-left works by default
25
+ const { interactionLabel } = props
25
26
 
26
27
  const createLegendLabels = createFormatLabels(config, tableData, data, colorScale)
27
28
 
@@ -40,6 +41,7 @@ const Legend = forwardRef((props, ref) => {
40
41
  handleShowAll={handleShowAll}
41
42
  currentViewport={currentViewport}
42
43
  formatLabels={createLegendLabels}
44
+ interactionLabel={interactionLabel}
43
45
  />
44
46
  </Fragment>
45
47
  )
@@ -83,6 +83,7 @@ const LegendGroup = ({ formatLabels }) => {
83
83
  }}
84
84
  key={`legend-item-${i}`}
85
85
  tabIndex={0}
86
+ role='button'
86
87
  >
87
88
  <LegendShape shape={config.legend.style === 'boxes' ? 'square' : 'circle'} fill={label.value} />
88
89
  <LegendLabel align='left' margin='0'>
@@ -19,9 +19,9 @@ export const getMarginTop = (isLegendBottom, config) => {
19
19
  if (!isLegendBottom) {
20
20
  return '0px'
21
21
  }
22
- if (isLegendBottom && config.brush.active && !config.legend.hide) {
22
+ if (isLegendBottom && config.xAxis.brushActive && !config.legend.hide) {
23
23
  const additiolMargin = 25
24
- return `${DEFAULT_MARGIN_TOP + config.brush.height + additiolMargin}px`
24
+ return `${DEFAULT_MARGIN_TOP + config.brush?.height + additiolMargin}px`
25
25
  } else {
26
26
  return `${DEFAULT_MARGIN_TOP}px`
27
27
  }
@@ -3,13 +3,14 @@ import { Group } from '@visx/group'
3
3
  import { type Column } from '@cdc/core/types/Column'
4
4
  import React from 'react'
5
5
  import { type ChartConfig } from '../../../types/ChartConfig'
6
+ import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
6
7
 
7
8
  type LineChartBumpCircleProp = {
8
- config: ChartConfig,
9
- xScale: any,
10
- yScale: any,
11
- parseDate: any
12
- }
9
+ config: ChartConfig
10
+ xScale: any
11
+ yScale: any
12
+ parseDate: any
13
+ }
13
14
 
14
15
  const LineChartBumpCircle = (props: LineChartBumpCircleProp) => {
15
16
  const { config, xScale, yScale, parseDate } = props
@@ -33,47 +34,47 @@ const LineChartBumpCircle = (props: LineChartBumpCircleProp) => {
33
34
  return xScale.bandwidth ? xScale.bandwidth() / 2 + Number(xValue) : Number(xValue)
34
35
  }
35
36
 
36
-
37
37
  const getListItems = dataRow => {
38
38
  return Object.values(config.columns)
39
- ?.filter(column => column.tooltips).map(column => {
40
- const label = column.label || column.name;
41
- return `
39
+ ?.filter(column => column.tooltips)
40
+ .map(column => {
41
+ const label = column.label || column.name
42
+ return `
42
43
  <li className='tooltip-body'>
43
44
  <strong>${label}</strong>: ${dataRow[column.name]}
44
- </li>`;
45
- })
46
- .join(' ');
47
- }
45
+ </li>`
46
+ })
47
+ .join(' ')
48
+ }
48
49
 
49
50
  const getTooltip = dataRow => `<ul> ${getListItems(dataRow)} </ul>`
50
51
 
51
- const circles = config.runtime?.series.map((series) => {
52
+ const circles = config.runtime?.series.map(series => {
52
53
  return config.data.map((d, dataIndex) => {
53
54
  let series_dataKey = d[series.dataKey]
54
55
  let axis_dataKey = d[config.xAxis.dataKey]
55
56
  return (
56
57
  <React.Fragment key={`bump-circle-${series_dataKey}-${dataIndex}`}>
57
- <Group left={Number(config.runtime.yAxis.size)}>
58
+ <Group left={Number(config.runtime.yAxis.size)}>
58
59
  {series_dataKey && (
59
60
  <>
60
- <circle
61
+ <circle
61
62
  key={`bump-circle-${series_dataKey}-${dataIndex}`}
62
- data-tooltip-html={getTooltip(d)}
63
- data-tooltip-id={`bump-chart`}
64
- r={10}
65
- cx={Number(checkBandScale(xScale(handleX(axis_dataKey))))}
66
- cy={Number(yScale(series_dataKey))}
67
- stroke='#CACACA'
68
- strokeWidth={1}
69
- fill='#E5E4E2'
63
+ data-tooltip-html={getTooltip(d)}
64
+ data-tooltip-id={`bump-chart`}
65
+ r={10}
66
+ cx={Number(checkBandScale(xScale(handleX(axis_dataKey))))}
67
+ cy={Number(yScale(series_dataKey))}
68
+ stroke='#CACACA'
69
+ strokeWidth={1}
70
+ fill='#E5E4E2'
70
71
  />
71
72
  {series_dataKey.toString().length === 2 ? (
72
73
  // prettier-ignore
73
74
  <text
74
75
  x={Number(checkBandScale(xScale(handleX(axis_dataKey)))) - 7}
75
76
  y={Number(yScale(series_dataKey)) + 4}
76
- fill='#000000'
77
+ fill={APP_FONT_COLOR}
77
78
  fontSize={11.5}
78
79
  >
79
80
  {series_dataKey}
@@ -83,7 +84,7 @@ const LineChartBumpCircle = (props: LineChartBumpCircleProp) => {
83
84
  <text
84
85
  x={Number(checkBandScale(xScale(handleX(axis_dataKey)))) - 4}
85
86
  y={Number(yScale(series_dataKey)) + 4}
86
- fill='#000000'
87
+ fill={APP_FONT_COLOR}
87
88
  fontSize={11.5}
88
89
  >
89
90
  {series_dataKey}
@@ -17,19 +17,19 @@ export const createStyles = (props: StyleProps): Style[] => {
17
17
 
18
18
  const dynamicSeriesKey = dynamicCategory ? originalSeriesKey : seriesKey
19
19
  const validPreliminaryData: PreliminaryDataItem[] = preliminaryData.filter(
20
- pd => pd.seriesKey && pd.column && pd.value && pd.type && pd.style && pd.type === 'effect'
20
+ pd => pd.seriesKeys?.length && pd.column && pd.value && pd.type && pd.style && pd.type === 'effect'
21
21
  )
22
22
  const isEffectLine = (pd, dataPoint) => {
23
23
  if (dynamicCategory) {
24
24
  return (
25
25
  pd.type === 'effect' &&
26
26
  pd.style !== 'Open Circles' &&
27
- pd.seriesKey === seriesKey &&
27
+ pd.seriesKeys.includes(seriesKey) &&
28
28
  String(dataPoint[dynamicSeriesKey]) === String(pd.value)
29
29
  )
30
30
  } else {
31
31
  return (
32
- pd.seriesKey === seriesKey &&
32
+ pd.seriesKeys.includes(seriesKey) &&
33
33
  dataPoint[pd.column] === pd.value &&
34
34
  pd.type === 'effect' &&
35
35
  pd.style !== 'Open Circles'
@@ -71,11 +71,11 @@ export const filterCircles = (
71
71
  ): DataItem[] => {
72
72
  // Filter and map preliminaryData to get circlesFiltered
73
73
  const circlesFiltered = preliminaryData
74
- ?.filter(item => item.style.includes('Circles') && item.type === 'effect')
74
+ ?.filter(item => item.style.includes('Circles') && item.type === 'effect' && item.seriesKeys?.length)
75
75
  .map(item => ({
76
76
  column: item.column,
77
77
  value: item.value,
78
- seriesKey: item.seriesKey,
78
+ seriesKeys: item.seriesKeys,
79
79
  circleSize: item.circleSize,
80
80
  style: item.style
81
81
  }))
@@ -85,7 +85,7 @@ export const filterCircles = (
85
85
  circlesFiltered.forEach(fc => {
86
86
  if (
87
87
  item[fc.column] === fc.value &&
88
- fc.seriesKey === seriesKey &&
88
+ fc.seriesKeys.includes(seriesKey) &&
89
89
  item[seriesKey] &&
90
90
  fc.style === 'Open Circles'
91
91
  ) {
@@ -98,7 +98,7 @@ export const filterCircles = (
98
98
  }
99
99
  if (
100
100
  (!fc.value || item[fc.column] === fc.value) &&
101
- fc.seriesKey === seriesKey &&
101
+ fc.seriesKeys.includes(seriesKey) &&
102
102
  item[seriesKey] &&
103
103
  fc.style === 'Filled Circles'
104
104
  ) {