@cdc/chart 4.23.6 → 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.
@@ -412,7 +412,18 @@ const EditorPanel = () => {
412
412
 
413
413
  const addNewSeries = seriesKey => {
414
414
  let newSeries = config.series ? [...config.series] : []
415
- newSeries.push({ dataKey: seriesKey, type: config.visualizationType })
415
+ let forecastingStages = Array.from(new Set(data.map(item => item[seriesKey])))
416
+ let forecastingStageArr = []
417
+
418
+ forecastingStages.forEach(stage => {
419
+ forecastingStageArr.push({ key: stage })
420
+ })
421
+
422
+ if (config.visualizationType === 'Forecasting') {
423
+ newSeries.push({ dataKey: seriesKey, type: config.visualizationType, stages: forecastingStageArr, stageColumn: seriesKey, axis: 'Left', tooltip: true })
424
+ } else {
425
+ newSeries.push({ dataKey: seriesKey, type: config.visualizationType, axis: 'Left', tooltip: true })
426
+ }
416
427
  updateConfig({ ...config, series: newSeries }) // left axis series keys
417
428
  }
418
429
 
@@ -624,16 +635,6 @@ const EditorPanel = () => {
624
635
  })
625
636
  }, [config.orientation])
626
637
 
627
- // Set paired bars to be horizontal, even though that option doesn't display
628
- useEffect(() => {
629
- if (config.visualizationType === 'Paired Bar') {
630
- updateConfig({
631
- ...config,
632
- orientation: 'horizontal'
633
- })
634
- }
635
- }, []) // eslint-disable-line
636
-
637
638
  useEffect(() => {
638
639
  if (config.orientation === 'horizontal') {
639
640
  updateConfig({
@@ -643,13 +644,6 @@ const EditorPanel = () => {
643
644
  }
644
645
  }, [config.isLollipopChart, config.lollipopShape]) // eslint-disable-line
645
646
 
646
- /// temporary force orientation untill we support Vartical deviaton bar
647
- useEffect(() => {
648
- if (config.visualizationType === 'Deviation Bar') {
649
- updateConfig({ ...config, orientation: 'horizontal' })
650
- }
651
- }, [config.visualizationType])
652
-
653
647
  const ExclusionsList = useCallback(() => {
654
648
  const exclusions = [...config.exclusions.keys]
655
649
  return (
@@ -671,8 +665,8 @@ const EditorPanel = () => {
671
665
  }, [config]) // eslint-disable-line
672
666
 
673
667
  const visSupportsTooltipLines = () => {
674
- if (config.visualizationType === 'Combo' && config.runtime.forecastingSeriesKeys?.length > 0) return true
675
- if (config.visualizationType === 'Forecasting') return true
668
+ const chartsWithTooltipGuides = ['Combo', 'Forecasting', 'Area Chart', 'Line', 'Bar']
669
+ if (chartsWithTooltipGuides.includes(config.visualizationType)) return true
676
670
  return false
677
671
  }
678
672
 
@@ -831,7 +825,7 @@ const EditorPanel = () => {
831
825
  'Box Plot',
832
826
  'Combo',
833
827
  'Deviation Bar',
834
- // 'Forecasting',
828
+ 'Forecasting',
835
829
  'Line',
836
830
  'Paired Bar',
837
831
  'Pie',
@@ -1345,47 +1339,6 @@ const EditorPanel = () => {
1345
1339
  <TextField type='text' value={config.boxplot.labels.outliers} fieldName='outliers' section='boxplot' subsection='labels' label='Outliers' updateField={updateField} />
1346
1340
  {/* values */}
1347
1341
  <TextField type='text' value={config.boxplot.labels.values} fieldName='values' section='boxplot' subsection='labels' label='Values' updateField={updateField} />
1348
- <br />
1349
- <h4 style={{ fontSize: '18px' }}>Percentages for Quartiles</h4>
1350
- <TextField
1351
- type='number'
1352
- value={config.boxplot.firstQuartilePercentage ? config.boxplot.firstQuartilePercentage : 25}
1353
- fieldName='firstQuartilePercentage'
1354
- section='boxplot'
1355
- label='Lower Quartile'
1356
- max={100}
1357
- updateField={updateField}
1358
- tooltip={
1359
- <Tooltip style={{ textTransform: 'none' }}>
1360
- <Tooltip.Target>
1361
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1362
- </Tooltip.Target>
1363
- <Tooltip.Content>
1364
- <p>Represented by bottom line of box. 25% of data are lower.</p>
1365
- </Tooltip.Content>
1366
- </Tooltip>
1367
- }
1368
- />
1369
-
1370
- <TextField
1371
- type='number'
1372
- value={config.boxplot.thirdQuartilePercentage ? config.boxplot.thirdQuartilePercentage : 75}
1373
- fieldName='thirdQuartilePercentage'
1374
- label='Upper Quartile'
1375
- section='boxplot'
1376
- max={100}
1377
- updateField={updateField}
1378
- tooltip={
1379
- <Tooltip style={{ textTransform: 'none' }}>
1380
- <Tooltip.Target>
1381
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
1382
- </Tooltip.Target>
1383
- <Tooltip.Content>
1384
- <p>Represented by top line of box. 25% of data are higher.</p>
1385
- </Tooltip.Content>
1386
- </Tooltip>
1387
- }
1388
- />
1389
1342
  </AccordionItemPanel>
1390
1343
  </AccordionItem>
1391
1344
  )}
@@ -1444,10 +1397,35 @@ const EditorPanel = () => {
1444
1397
  </Tooltip>
1445
1398
  }
1446
1399
  />
1400
+ {config.orientation === 'horizontal' && config.visualizationType !== 'Paired Bar' && <CheckBox value={config.isResponsiveTicks} fieldName='isResponsiveTicks' label='Use Responsive Ticks' updateField={updateField} />}
1401
+ {(config.orientation === 'vertical' || !config.isResponsiveTicks) && <TextField value={config.yAxis.tickRotation} type='number' min='0' section='yAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />}
1402
+ {config.isResponsiveTicks && config.orientation === 'horizontal' && config.visualizationType !== 'Paired Bar' && (
1403
+ <TextField
1404
+ value={config.xAxis.maxTickRotation}
1405
+ type='number'
1406
+ min='0'
1407
+ section='xAxis'
1408
+ fieldName='maxTickRotation'
1409
+ label='Max Tick Rotation'
1410
+ className='number-narrow'
1411
+ updateField={updateField}
1412
+ tooltip={
1413
+ <Tooltip style={{ textTransform: 'none' }}>
1414
+ <Tooltip.Target>
1415
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
1416
+ </Tooltip.Target>
1417
+ <Tooltip.Content>
1418
+ <p>Degrees ticks will be rotated if values overlap, especially in smaller viewports.</p>
1419
+ </Tooltip.Content>
1420
+ </Tooltip>
1421
+ }
1422
+ />
1423
+ )}
1424
+
1447
1425
  {/* Hiding this for now, not interested in moving the axis lines away from chart comp. right now. */}
1448
1426
  {/* <TextField value={config.yAxis.axisPadding} type='number' max={10} min={0} section='yAxis' fieldName='axisPadding' label={'Axis Padding'} className='number-narrow' updateField={updateField} /> */}
1449
1427
  {config.orientation === 'horizontal' && <TextField value={config.xAxis.labelOffset} section='xAxis' fieldName='labelOffset' label='Label offset' type='number' className='number-narrow' updateField={updateField} />}
1450
- {config.orientation !== 'horizontal' && <CheckBox value={config.yAxis.gridLines} section='yAxis' fieldName='gridLines' label='Display Gridlines' updateField={updateField} />}
1428
+ {config.orientation !== 'horizontal' && <CheckBox value={config.yAxis.gridLines} section='yAxis' fieldName='gridLines' label='Show Gridlines' updateField={updateField} />}
1451
1429
  <CheckBox value={config.yAxis.enablePadding} section='yAxis' fieldName='enablePadding' label='Add Padding to Value Axis Scale' updateField={updateField} />
1452
1430
  {config.visualizationSubType === 'regular' && <CheckBox value={config.useLogScale} fieldName='useLogScale' label='use logarithmic scale' updateField={updateField} />}
1453
1431
  </>
@@ -1524,7 +1502,7 @@ const EditorPanel = () => {
1524
1502
  <>
1525
1503
  <TextField value={config.xAxis.target} section='xAxis' fieldName='target' type='number' label='Deviation point' placeholder='Auto' updateField={updateField} />
1526
1504
  <TextField value={config.xAxis.targetLabel || 'Target'} section='xAxis' fieldName='targetLabel' type='text' label='Deviation point Label' updateField={updateField} />
1527
- <CheckBox value={config.xAxis.showTargetLabel} section='xAxis' fieldName='showTargetLabel' label='Display Deviation point label' updateField={updateField} />
1505
+ <CheckBox value={config.xAxis.showTargetLabel} section='xAxis' fieldName='showTargetLabel' label='Show Deviation point label' updateField={updateField} />
1528
1506
  </>
1529
1507
  )}
1530
1508
  </>
@@ -1546,10 +1524,10 @@ const EditorPanel = () => {
1546
1524
  {/* start: anchors */}
1547
1525
  {visHasAnchors() && config.orientation !== 'horizontal' && (
1548
1526
  <div className='edit-block'>
1549
- <h3>Anchors</h3>
1527
+ <span className='edit-label column-heading'>Anchors</span>
1550
1528
  <Accordion allowZeroExpanded>
1551
1529
  {config.yAxis?.anchors?.map((anchor, index) => (
1552
- <AccordionItem className='series-item series-item--chart'>
1530
+ <AccordionItem className='series-item series-item--chart' key={`yaxis-anchors-2-${index}`}>
1553
1531
  <AccordionItemHeading className='series-item__title'>
1554
1532
  <>
1555
1533
  <AccordionItemButton className={'accordion__button accordion__button'}>
@@ -1672,10 +1650,10 @@ const EditorPanel = () => {
1672
1650
 
1673
1651
  {visHasAnchors() && config.orientation === 'horizontal' && (
1674
1652
  <div className='edit-block'>
1675
- <h3>Anchors</h3>
1653
+ <span className='edit-label column-heading'>Anchors</span>
1676
1654
  <Accordion allowZeroExpanded>
1677
1655
  {config.xAxis?.anchors?.map((anchor, index) => (
1678
- <AccordionItem className='series-item series-item--chart'>
1656
+ <AccordionItem className='series-item series-item--chart' key={`xaxis-anchors-${index}`}>
1679
1657
  <AccordionItemHeading className='series-item__title'>
1680
1658
  <>
1681
1659
  <AccordionItemButton className={'accordion__button accordion__button'}>
@@ -2060,8 +2038,31 @@ const EditorPanel = () => {
2060
2038
  <TextField value={config.dataFormat.bottomRoundTo} type='number' section='dataFormat' fieldName='bottomRoundTo' label='Round to decimal point' className='number-narrow' updateField={updateField} min={0} />
2061
2039
  </>
2062
2040
  )}
2041
+ {config.orientation === 'vertical' && config.visualizationType !== 'Paired Bar' && <CheckBox value={config.isResponsiveTicks} fieldName='isResponsiveTicks' label='Use Responsive Ticks' updateField={updateField} />}
2042
+ {(config.orientation === 'horizontal' || !config.isResponsiveTicks) && <TextField value={config.xAxis.tickRotation} type='number' min='0' section='xAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />}
2043
+ {config.orientation === 'vertical' && config.isResponsiveTicks && config.visualizationType !== 'Paired Bar' && (
2044
+ <TextField
2045
+ value={config.xAxis.maxTickRotation}
2046
+ type='number'
2047
+ min='0'
2048
+ section='xAxis'
2049
+ fieldName='maxTickRotation'
2050
+ label='Max Tick Rotation'
2051
+ className='number-narrow'
2052
+ updateField={updateField}
2053
+ tooltip={
2054
+ <Tooltip style={{ textTransform: 'none' }}>
2055
+ <Tooltip.Target>
2056
+ <Icon display='question' style={{ marginLeft: '0.5rem', display: 'inline-block', whiteSpace: 'nowrap' }} />
2057
+ </Tooltip.Target>
2058
+ <Tooltip.Content>
2059
+ <p>Degrees ticks will be rotated if values overlap, especially in smaller viewports.</p>
2060
+ </Tooltip.Content>
2061
+ </Tooltip>
2062
+ }
2063
+ />
2064
+ )}
2063
2065
 
2064
- {config.yAxis.labelPlacement !== 'Below Bar' && <TextField value={config.xAxis.tickRotation} type='number' min='0' section='xAxis' fieldName='tickRotation' label='Tick rotation (Degrees)' className='number-narrow' updateField={updateField} />}
2065
2066
  {config.orientation === 'horizontal' ? (
2066
2067
  <>
2067
2068
  <CheckBox value={config.yAxis.hideAxis} section='yAxis' fieldName='hideAxis' label='Hide Axis' updateField={updateField} />
@@ -2167,10 +2168,10 @@ const EditorPanel = () => {
2167
2168
  {/* anchors */}
2168
2169
  {visHasAnchors() && config.orientation !== 'horizontal' && (
2169
2170
  <div className='edit-block'>
2170
- <h3>Anchors</h3>
2171
+ <span className='edit-label column-heading'>Anchors</span>
2171
2172
  <Accordion allowZeroExpanded>
2172
2173
  {config.xAxis?.anchors?.map((anchor, index) => (
2173
- <AccordionItem className='series-item series-item--chart'>
2174
+ <AccordionItem className='series-item series-item--chart' key={`xaxis-anchors-2-${index}`}>
2174
2175
  <AccordionItemHeading className='series-item__title'>
2175
2176
  <>
2176
2177
  <AccordionItemButton className={'accordion__button accordion__button'}>
@@ -2293,10 +2294,10 @@ const EditorPanel = () => {
2293
2294
 
2294
2295
  {visHasAnchors() && config.orientation === 'horizontal' && (
2295
2296
  <div className='edit-block'>
2296
- <h3>Anchors</h3>
2297
+ <span className='edit-label column-heading'>Anchors</span>
2297
2298
  <Accordion allowZeroExpanded>
2298
2299
  {config.yAxis?.anchors?.map((anchor, index) => (
2299
- <AccordionItem className='series-item series-item--chart'>
2300
+ <AccordionItem className='series-item series-item--chart' key={`accordion-yaxis-anchors-${index}`}>
2300
2301
  <AccordionItemHeading className='series-item__title'>
2301
2302
  <>
2302
2303
  <AccordionItemButton className={'accordion__button accordion__button'}>
@@ -2483,9 +2484,9 @@ const EditorPanel = () => {
2483
2484
  <label className='checkbox'>
2484
2485
  <input
2485
2486
  type='checkbox'
2486
- checked={config.columns[val].useCommas}
2487
+ checked={config.columns[val].commas}
2487
2488
  onChange={event => {
2488
- editColumn(val, 'useCommas', event.target.checked)
2489
+ editColumn(val, 'commas', event.target.checked)
2489
2490
  }}
2490
2491
  />
2491
2492
  <span className='edit-label'>Add Commas to Numbers</span>
@@ -2500,7 +2501,7 @@ const EditorPanel = () => {
2500
2501
  editColumn(val, 'dataTable', event.target.checked)
2501
2502
  }}
2502
2503
  />
2503
- <span className='edit-label'>Display in Data Table</span>
2504
+ <span className='edit-label'>Show in Data Table</span>
2504
2505
  </label>
2505
2506
  </li>
2506
2507
  {/* disable for now */}
@@ -2996,7 +2997,7 @@ const EditorPanel = () => {
2996
2997
 
2997
2998
  {config.visualizationType === 'Spark Line' && (
2998
2999
  <div className='cove-accordion__panel-section checkbox-group'>
2999
- <CheckBox value={config.visual?.border} section='visual' fieldName='border' label='Display Border' updateField={updateField} />
3000
+ <CheckBox value={config.visual?.border} section='visual' fieldName='border' label='Show Border' updateField={updateField} />
3000
3001
  <CheckBox value={config.visual?.borderColorTheme} section='visual' fieldName='borderColorTheme' label='Use Border Color Theme' updateField={updateField} />
3001
3002
  <CheckBox value={config.visual?.accent} section='visual' fieldName='accent' label='Use Accent Style' updateField={updateField} />
3002
3003
  <CheckBox value={config.visual?.background} section='visual' fieldName='background' label='Use Theme Background Color' updateField={updateField} />
@@ -3015,6 +3016,25 @@ const EditorPanel = () => {
3015
3016
  <CheckBox value={config.visual.horizontalHoverLine} fieldName='horizontalHoverLine' section='visual' label='Horizontal Hover Line' updateField={updateField} />
3016
3017
  </>
3017
3018
  )}
3019
+
3020
+ {
3021
+ <label>
3022
+ <span className='edit-label column-heading'>Tooltip Opacity</span>
3023
+ <input
3024
+ type='number'
3025
+ value={config.tooltips.opacity ? config.tooltips.opacity : 100}
3026
+ onChange={e =>
3027
+ updateConfig({
3028
+ ...config,
3029
+ tooltips: {
3030
+ ...config.tooltips,
3031
+ opacity: e.target.value
3032
+ }
3033
+ })
3034
+ }
3035
+ />
3036
+ </label>
3037
+ }
3018
3038
  </AccordionItemPanel>
3019
3039
  </AccordionItem>
3020
3040
  {/* Spark Line has no data table */}
@@ -1,4 +1,4 @@
1
- import React, { useContext, useEffect, useState } from 'react'
1
+ import React, { useContext } from 'react'
2
2
 
3
3
  // cdc
4
4
  import ConfigContext from '../ConfigContext'
@@ -6,43 +6,21 @@ import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
6
6
  import { colorPalettesChart } from '@cdc/core/data/colorPalettes'
7
7
 
8
8
  // visx & d3
9
- import { useTooltipInPortal, defaultStyles } from '@visx/tooltip'
10
9
  import { curveMonotoneX } from '@visx/curve'
11
- import { Bar, Area, LinePath, Line } from '@visx/shape'
10
+ import { Bar, Area, LinePath } from '@visx/shape'
12
11
  import { Group } from '@visx/group'
13
12
 
14
- const Forecasting = ({ xScale, yScale, height, width, chartRef, handleTooltipMouseOver, tooltipData, showTooltip, handleTooltipMouseOff }) => {
15
- const { transformedData: data, rawData, config, seriesHighlight, formatNumber } = useContext(ConfigContext)
13
+ const Forecasting = ({ xScale, yScale, height, width, handleTooltipMouseOver, handleTooltipMouseOff }) => {
14
+ const { transformedData: data, rawData, config, seriesHighlight } = useContext(ConfigContext)
16
15
  const { xAxis, yAxis, legend, runtime } = config
17
16
  const DEBUG = false
18
17
 
19
- // sets the portal x/y for where tooltips should appear on the page.
20
- const [chartPosition, setChartPosition] = useState(null)
21
- useEffect(() => {
22
- setChartPosition(chartRef.current.getBoundingClientRect())
23
- }, [chartRef])
24
-
25
- // a unique id is needed for tooltips.
26
- const tooltip_id = `cdc-open-viz-tooltip-${config.runtime.uniqueId}`
27
-
28
- // it appears we need to use TooltipInPortal.
29
- const { TooltipInPortal } = useTooltipInPortal({
30
- detectBounds: true,
31
- // when tooltip containers are scrolled, this will correctly update the Tooltip position
32
- scroll: true
33
- })
34
-
35
- const TooltipListItem = ({ item }) => {
36
- const [label, value] = item
37
- return label === config.xAxis.dataKey ? `${label}: ${value}` : `${label}: ${formatNumber(value, 'left')}`
38
- }
39
-
40
18
  return (
41
19
  data && (
42
20
  <ErrorBoundary component='ForecastingChart'>
43
- <Group className='forecasting-items' key='forecasting-items-wrapper' left={yAxis.size}>
21
+ <Group className='forecasting-items' key='forecasting-items-wrapper' left={Number(yAxis.size)}>
44
22
  {runtime.forecastingSeriesKeys?.map((group, index) => {
45
- if (!group || !group.stages) return
23
+ if (!group || !group.stages) return false
46
24
  return group.stages.map((stage, stageIndex) => {
47
25
  const { behavior } = legend
48
26
  const groupData = rawData.filter(d => d[group.stageColumn] === stage.key)
@@ -54,13 +32,24 @@ const Forecasting = ({ xScale, yScale, height, width, chartRef, handleTooltipMou
54
32
  {group.confidenceIntervals?.map((ciGroup, ciGroupIndex) => {
55
33
  const palette = colorPalettesChart[stage.color]
56
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
57
46
  return (
58
47
  <Group key={`forecasting-areas--stage-${stage.key.replaceAll(' ', '-')}--group-${stageIndex}-${ciGroupIndex}`}>
59
48
  {/* prettier-ignore */}
60
49
  <Area
61
50
  curve={curveMonotoneX}
62
51
  data={groupData}
63
- fill={displayArea ? palette[ciGroupIndex] : 'transparent'}
52
+ fill={getFill()}
64
53
  opacity={transparentArea ? 0.1 : 0.5}
65
54
  x={d => xScale(Date.parse(d[xAxis.dataKey]))}
66
55
  y0={d => yScale(d[ciGroup.low])}
@@ -70,26 +59,10 @@ const Forecasting = ({ xScale, yScale, height, width, chartRef, handleTooltipMou
70
59
  {ciGroupIndex === 0 && (
71
60
  <>
72
61
  {/* prettier-ignore */}
73
- <LinePath
74
- data={groupData}
75
- x={ d => xScale(Date.parse(d[xAxis.dataKey])) }
76
- y={ d => yScale(d[ciGroup.high])}
77
- curve={curveMonotoneX}
78
- stroke={displayArea ? palette[2] : 'transparent'}
79
- strokeWidth={1}
80
- strokeOpacity={1}
81
- />
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} />
82
63
 
83
64
  {/* prettier-ignore */}
84
- <LinePath
85
- data={groupData}
86
- x={ d => xScale(Date.parse(d[xAxis.dataKey])) }
87
- y={ d => yScale(d[ciGroup.low])}
88
- curve={curveMonotoneX}
89
- stroke={displayArea ? palette[2] : 'transparent'}
90
- strokeWidth={1}
91
- strokeOpacity={1}
92
- />
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} />
93
66
  </>
94
67
  )}
95
68
  </Group>
@@ -99,46 +72,10 @@ const Forecasting = ({ xScale, yScale, height, width, chartRef, handleTooltipMou
99
72
  )
100
73
  })
101
74
  })}
102
-
103
- {tooltipData && Object.entries(tooltipData.data).length > 0 && config?.runtime?.forecastingSeriesKeys?.length > 0 && (config.visualizationType === 'Combo' || config.visualizationType === 'Forecasting') && (
104
- <TooltipInPortal key={Math.random()} top={tooltipData.dataYPosition + chartPosition?.top} left={tooltipData.dataXPosition + chartPosition?.left} style={defaultStyles}>
105
- <ul
106
- style={{
107
- listStyle: 'none',
108
- paddingLeft: 'unset',
109
- fontFamily: 'sans-serif',
110
- margin: 'auto',
111
- lineHeight: '1rem'
112
- }}
113
- data-tooltip-id={tooltip_id}
114
- >
115
- {typeof tooltipData === 'object' &&
116
- Object.entries(tooltipData.data).map((item, index) => (
117
- <li style={{ padding: '2.5px 0' }} key={`li-${index}`}>
118
- <TooltipListItem item={item} />
119
- </li>
120
- ))}
121
- </ul>
122
- </TooltipInPortal>
123
- )}
124
- {config?.runtime?.forecastingSeriesKeys?.length > 0 && (config.visualizationType === 'Combo' || config.visualizationType === 'Forecasting') && (
125
- <Group key='tooltip-hover-section'>
126
- <Bar key={'bars'} width={Number(width)} height={Number(height)} fill={DEBUG ? 'red' : 'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} />
127
- </Group>
128
- )}
129
- </Group>
130
-
131
- {showTooltip && tooltipData && config.visual.verticalHoverLine && (
132
- <Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
133
- <Line from={{ x: tooltipData.dataXPosition - 10, y: 0 }} to={{ x: tooltipData.dataXPosition - 10, y: height }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='vertical-tooltip-line' />
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} />
134
77
  </Group>
135
- )}
136
-
137
- {showTooltip && tooltipData && config.visual.horizontalHoverLine && (
138
- <Group key='tooltipLine-horizontal' className='horizontal-tooltip-line' left={config.yAxis.size ? config.yAxis.size : 0}>
139
- <Line from={{ x: 0, y: tooltipData.dataYPosition }} to={{ x: width, y: tooltipData.dataYPosition }} stroke={'black'} strokeWidth={1} pointerEvents='none' strokeDasharray='5,5' className='horizontal-tooltip-line' />
140
- </Group>
141
- )}
78
+ </Group>
142
79
  </ErrorBoundary>
143
80
  )
144
81
  )
@@ -33,6 +33,8 @@ const Legend = () => {
33
33
 
34
34
  const { innerClasses, containerClasses } = useLegendClasses(config)
35
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)
36
38
 
37
39
  const createLegendLabels = defaultLabels => {
38
40
  const colorCode = config.legend?.colorCode
@@ -51,7 +53,7 @@ const Legend = () => {
51
53
  value: aboveColor
52
54
  }
53
55
 
54
- return [labelBelow, labelAbove]
56
+ return reverseLabels([labelBelow, labelAbove])
55
57
  }
56
58
  if (visualizationType === 'Bar' && visualizationSubType === 'regular' && colorCode && series?.length === 1) {
57
59
  let palette = colorPalettes[config.palette]
@@ -76,7 +78,7 @@ const Legend = () => {
76
78
  return newLabel
77
79
  })
78
80
 
79
- return uniqueLabels
81
+ return reverseLabels(uniqueLabels)
80
82
  }
81
83
 
82
84
  // get forecasting items inside of combo
@@ -103,7 +105,7 @@ const Legend = () => {
103
105
 
104
106
  // loop through bars for now to meet requirements.
105
107
  config.runtime.barSeriesKeys &&
106
- config.runtime.barSeriesKeys.map((bar, index) => {
108
+ config.runtime.barSeriesKeys.forEach((bar, index) => {
107
109
  let colorValue = colorPalettes[config.palette][index] ? colorPalettes[config.palette][index] : '#ccc'
108
110
 
109
111
  const newLabel = {
@@ -116,7 +118,7 @@ const Legend = () => {
116
118
  seriesLabels.push(newLabel)
117
119
  })
118
120
 
119
- return seriesLabels
121
+ return reverseLabels(seriesLabels)
120
122
  }
121
123
 
122
124
  // DEV-4161: replaceable series name in the legend
@@ -147,17 +149,17 @@ const Legend = () => {
147
149
  return newLabel
148
150
  })
149
151
 
150
- return uniqueLabels
152
+ return reverseLabels(uniqueLabels)
151
153
  }
152
154
 
153
- return defaultLabels
155
+ return reverseLabels(defaultLabels)
154
156
  }
155
157
 
156
- const isBottomOrSmallViewport = legend.position === 'bottom' || currentViewport === 'sm' || currentViewport === 'xs' || currentViewport === 'xxs'
158
+ const isBottomOrSmallViewport = legend.position === 'bottom' || ['sm', 'xs', 'xxs'].includes(currentViewport)
157
159
 
158
160
  const legendClasses = {
159
161
  marginBottom: isBottomOrSmallViewport ? '15px' : '0px',
160
- marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.runtime.xAxis.size}px` : '0px'
162
+ marginTop: isBottomOrSmallViewport && orientation === 'horizontal' ? `${config.yAxis.label && config.isResponsiveTicks ? config.dynamicMarginTop : config.runtime.xAxis.size}px` : `0px`
161
163
  }
162
164
 
163
165
  const { HighLightedBarUtils } = useHighlightedBars(config)
@@ -2,22 +2,23 @@ import React, { useContext } from 'react'
2
2
 
3
3
  import * as allCurves from '@visx/curve'
4
4
  import { Group } from '@visx/group'
5
- import { LinePath } from '@visx/shape'
5
+ import { LinePath, Bar } from '@visx/shape'
6
6
  import { Text } from '@visx/text'
7
7
 
8
8
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
9
9
  import ConfigContext from '../ConfigContext'
10
10
  import useRightAxis from '../hooks/useRightAxis'
11
11
 
12
- export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, seriesStyle = 'Line' }) {
13
- const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, updateConfig, handleLineType } = useContext(ConfigContext)
12
+ const LineChart = ({ xScale, yScale, getXAxisData, getYAxisData, xMax, yMax, handleTooltipMouseOver, handleTooltipMouseOff, showTooltip, seriesStyle = 'Line', svgRef, handleTooltipClick, tooltipData }) => {
13
+ // Not sure why there's a redraw here.
14
14
 
15
+ const { colorPalettes, transformedData: data, colorScale, seriesHighlight, config, formatNumber, formatDate, parseDate, isNumber, updateConfig, handleLineType, dashboardConfig } = useContext(ConfigContext)
15
16
  const { yScaleRight } = useRightAxis({ config, yMax, data, updateConfig })
16
17
 
18
+ if (!handleTooltipMouseOver) return
17
19
  const handleAxisFormating = (axis = 'left', label, value) => {
18
20
  // if this is an x axis category/date value return without doing any formatting.
19
21
  // if (label === config.runtime.xAxis.label) return value
20
-
21
22
  axis = String(axis).toLocaleLowerCase()
22
23
  if (label) {
23
24
  return `${label}: ${formatNumber(value, axis)}`
@@ -25,6 +26,7 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
25
26
  return `${formatNumber(value, axis)}`
26
27
  }
27
28
 
29
+ const DEBUG = false
28
30
  return (
29
31
  <ErrorBoundary component='LineChart'>
30
32
  <Group left={config.runtime.yAxis.size ? parseInt(config.runtime.yAxis.size) : 66}>
@@ -35,6 +37,8 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
35
37
  const seriesData = config.series.filter(item => item.dataKey === seriesKey)
36
38
  const seriesAxis = seriesData[0].axis ? seriesData[0].axis : 'left'
37
39
 
40
+ let displayArea = config.legend.behavior === 'highlight' || seriesHighlight.length === 0 || seriesHighlight.indexOf(seriesKey) !== -1
41
+
38
42
  return (
39
43
  <Group
40
44
  key={`series-${seriesKey}`}
@@ -71,7 +75,10 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
71
75
  d[seriesKey] !== '' &&
72
76
  d[seriesKey] !== null &&
73
77
  isNumber(d[seriesKey]) && (
74
- <Group key={`series-${seriesKey}-point-${dataIndex}`}>
78
+ <Group key={`series-${seriesKey}-point-${dataIndex}`} className='checkwidth'>
79
+ {/* tooltips */}
80
+ <Bar key={'bars'} width={Number(xMax)} height={Number(yMax)} fill={DEBUG ? 'red' : 'transparent'} fillOpacity={0.05} onMouseMove={e => handleTooltipMouseOver(e, data)} onMouseOut={handleTooltipMouseOff} onClick={e => handleTooltipClick(e, data)} />
81
+
75
82
  {/* Render legend */}
76
83
  <Text display={config.labels ? 'block' : 'none'} x={xScale(getXAxisData(d))} y={seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey))} fill={'#000'} textAnchor='middle'>
77
84
  {formatNumber(d[seriesKey], 'left')}
@@ -83,10 +90,26 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
83
90
  cx={Number(xScale(getXAxisData(d)))}
84
91
  cy={seriesAxis === 'Right' ? yScaleRight(getYAxisData(d, seriesKey)) : yScale(getYAxisData(d, seriesKey))}
85
92
  fill={colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'}
86
- style={{ fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000' }}
93
+ style={{
94
+ fill: colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000'
95
+ }}
87
96
  data-tooltip-html={tooltip}
88
97
  data-tooltip-id={`cdc-open-viz-tooltip-${config.runtime.uniqueId}`}
89
98
  />
99
+
100
+ {/* circles that appear on hover */}
101
+ {/* todo: circle radii used here should be global with other circle radii */}
102
+ {/* {tooltipData && Object.entries(tooltipData.data).length > 0 && isNumber(tooltipData.data[seriesKey]) && config.lineDatapointStyle === 'hover' && config.series.filter(s => s.type === 'Line') && (
103
+ <circle
104
+ cx={config.xAxis.type === 'categorical' ? xScale(tooltipData.data[config.xAxis.dataKey]) : xScale(parseDate(tooltipData.data[config.xAxis.dataKey]))}
105
+ cy={yScale(tooltipData.data[seriesKey])}
106
+ r={4.5}
107
+ opacity={tooltipData[seriesKey] ? 1 : 0}
108
+ fillOpacity={1}
109
+ fill={displayArea ? (colorScale ? colorScale(config.runtime.seriesLabels ? config.runtime.seriesLabels[seriesKey] : seriesKey) : '#000') : 'transparent'}
110
+ style={{ filter: 'unset', opacity: 1 }}
111
+ />
112
+ )} */}
90
113
  </Group>
91
114
  )
92
115
  )
@@ -161,3 +184,5 @@ export default function LineChart({ xScale, yScale, getXAxisData, getYAxisData,
161
184
  </ErrorBoundary>
162
185
  )
163
186
  }
187
+
188
+ export default LineChart