@cdc/chart 4.25.3 → 4.25.6-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 (86) hide show
  1. package/dist/cdcchart.js +50009 -46835
  2. package/index.html +130 -129
  3. package/package.json +22 -27
  4. package/src/CdcChartComponent.tsx +75 -35
  5. package/src/_stories/Chart.CI.stories.tsx +10 -0
  6. package/src/_stories/Chart.DynamicSeries.stories.tsx +68 -49
  7. package/src/_stories/Chart.stories.tsx +99 -86
  8. package/src/_stories/ChartPrefixSuffix.stories.tsx +29 -32
  9. package/{examples/private/line-issue.json → src/_stories/_mock/barchart_labels.mock.json} +150 -35
  10. package/src/_stories/_mock/dynamic_series_bar_config.json +1 -1
  11. package/src/_stories/_mock/dynamic_series_suppression_mock.json +610 -0
  12. package/{examples/private/not-loading.json → src/_stories/_mock/pie_calculated_area.json} +152 -95
  13. package/src/components/Annotations/components/AnnotationDropdown.tsx +2 -2
  14. package/src/components/AreaChart/components/AreaChart.jsx +33 -5
  15. package/src/components/Axis/Categorical.Axis.tsx +2 -2
  16. package/src/components/BarChart/components/BarChart.Horizontal.tsx +38 -37
  17. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +18 -8
  18. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +8 -8
  19. package/src/components/BarChart/components/BarChart.Vertical.tsx +47 -36
  20. package/src/components/BarChart/components/{BarChart.jsx → BarChart.tsx} +23 -5
  21. package/src/components/BarChart/components/context.tsx +20 -2
  22. package/src/components/BarChart/helpers/getBarHeights.ts +47 -0
  23. package/src/components/BarChart/helpers/index.ts +5 -2
  24. package/src/components/BarChart/helpers/tests/getBarHeights.test.ts +83 -0
  25. package/src/{hooks → components/BarChart/helpers}/useBarChart.ts +9 -46
  26. package/src/components/BoxPlot/BoxPlot.tsx +2 -1
  27. package/src/components/Brush/BrushChart.tsx +73 -0
  28. package/src/components/Brush/BrushController..tsx +39 -0
  29. package/src/components/DeviationBar.jsx +2 -2
  30. package/src/components/EditorPanel/EditorPanel.tsx +246 -156
  31. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +36 -36
  32. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +52 -25
  33. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +12 -4
  34. package/src/components/EditorPanel/components/Panels/panelVisual.styles.css +8 -0
  35. package/src/components/EditorPanel/useEditorPermissions.ts +5 -5
  36. package/src/components/ForestPlot/ForestPlot.tsx +2 -2
  37. package/src/components/HoverLine/HoverLine.tsx +74 -0
  38. package/src/components/Legend/Legend.Component.tsx +1 -1
  39. package/src/components/Legend/Legend.Suppression.tsx +59 -25
  40. package/src/components/Legend/helpers/createFormatLabels.tsx +28 -0
  41. package/src/components/Legend/helpers/index.ts +1 -1
  42. package/src/components/LineChart/LineChartProps.ts +3 -1
  43. package/src/components/LineChart/components/LineChart.Circle.tsx +72 -119
  44. package/src/components/LineChart/helpers.ts +139 -62
  45. package/src/components/LineChart/index.tsx +106 -55
  46. package/src/components/LinearChart.tsx +125 -138
  47. package/src/components/PairedBarChart.jsx +3 -2
  48. package/src/components/PieChart/PieChart.tsx +127 -102
  49. package/src/components/ScatterPlot/ScatterPlot.jsx +5 -0
  50. package/src/components/Sparkline/components/SparkLine.tsx +80 -18
  51. package/src/data/initial-state.js +11 -6
  52. package/src/helpers/countNumOfTicks.ts +4 -19
  53. package/src/helpers/dataHelpers.ts +23 -2
  54. package/src/helpers/getNewRuntime.ts +35 -0
  55. package/src/helpers/getPiePercent.ts +22 -0
  56. package/src/helpers/getTransformedData.ts +22 -0
  57. package/src/helpers/sizeHelpers.ts +1 -1
  58. package/src/helpers/tests/getNewRuntime.test.ts +82 -0
  59. package/src/helpers/tests/getPiePercent.test.ts +38 -0
  60. package/src/hooks/useMinMax.ts +21 -28
  61. package/src/hooks/useRightAxis.ts +5 -3
  62. package/src/hooks/useScales.ts +15 -6
  63. package/src/hooks/useTooltip.tsx +218 -203
  64. package/src/index.jsx +2 -2
  65. package/src/scss/main.scss +21 -10
  66. package/src/store/chart.actions.ts +2 -6
  67. package/src/store/chart.reducer.ts +23 -23
  68. package/src/types/ChartConfig.ts +12 -4
  69. package/src/types/ChartContext.ts +0 -2
  70. package/examples/private/DEV-8850-2.json +0 -493
  71. package/examples/private/DEV-9822.json +0 -574
  72. package/examples/private/DEV-9840.json +0 -553
  73. package/examples/private/DEV-9850-3.json +0 -461
  74. package/examples/private/chart.json +0 -1084
  75. package/examples/private/ci_formatted.json +0 -202
  76. package/examples/private/ci_issue.json +0 -3016
  77. package/examples/private/completed.json +0 -634
  78. package/examples/private/dem-data-long.csv +0 -20
  79. package/examples/private/dem-data-long.json +0 -36
  80. package/examples/private/demographic_data.csv +0 -157
  81. package/examples/private/demographic_data.json +0 -2654
  82. package/examples/private/demographic_dynamic.json +0 -443
  83. package/examples/private/demographic_standard.json +0 -560
  84. package/examples/private/ehdi.json +0 -29939
  85. package/examples/private/test.json +0 -493
  86. package/src/components/ZoomBrush.tsx +0 -251
@@ -4,15 +4,14 @@ import React, { forwardRef, useContext, useEffect, useMemo, useRef, useState } f
4
4
  import { AxisLeft, AxisBottom, AxisRight, AxisTop } from '@visx/axis'
5
5
  import { Group } from '@visx/group'
6
6
  import { Line, Bar } from '@visx/shape'
7
- import { Text } from '@visx/text'
8
7
  import { Tooltip as ReactTooltip } from 'react-tooltip'
8
+ import { Text } from '@visx/text'
9
9
  import { useTooltip, TooltipWithBounds } from '@visx/tooltip'
10
10
  import _ from 'lodash'
11
11
 
12
12
  // CDC Components
13
13
  import { isDateScale } from '@cdc/core/helpers/cove/date'
14
- import BrushChart from './BrushChart'
15
- import { AreaChart, AreaChartStacked } from './AreaChart'
14
+ import { AreaChartStacked } from './AreaChart'
16
15
  import BarChart from './BarChart'
17
16
  import ConfigContext from '../ConfigContext'
18
17
  import BoxPlot from './BoxPlot'
@@ -43,6 +42,8 @@ import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
43
42
  import Annotation from './Annotations'
44
43
  import { BlurStrokeText } from '@cdc/core/components/BlurStrokeText'
45
44
  import { countNumOfTicks } from '../helpers/countNumOfTicks'
45
+ import HoverLine from './HoverLine/HoverLine'
46
+ import BrushChart from './BrushChart'
46
47
 
47
48
  type LinearChartProps = {
48
49
  parentWidth: number
@@ -59,10 +60,23 @@ const AXIS_LABEL_FONT_SIZE = 18
59
60
  const AXIS_LABEL_FONT_SIZE_SMALL = 14
60
61
  const TICK_LABEL_MARGIN_RIGHT = 4.5
61
62
 
63
+ type TooltipData = {
64
+ dataXPosition?: number
65
+ dataYPosition?: number
66
+ }
67
+
68
+ type UseTooltipReturn<T = TooltipData> = {
69
+ tooltipData: T | null
70
+ showTooltip: (tooltipData: T) => void
71
+ hideTooltip: () => void
72
+ tooltipOpen: boolean
73
+ tooltipLeft: number | null
74
+ tooltipTop: number | null
75
+ }
76
+
62
77
  const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, parentWidth }, svgRef) => {
63
78
  // prettier-ignore
64
79
  const {
65
- brushConfig,
66
80
  colorScale,
67
81
  config,
68
82
  convertLineToBarGraph,
@@ -79,8 +93,8 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
79
93
  parentRef,
80
94
  tableData,
81
95
  transformedData: data,
82
- updateConfig,
83
96
  seriesHighlight,
97
+ brushConfig
84
98
  } = useContext(ConfigContext)
85
99
 
86
100
  // CONFIG
@@ -99,14 +113,14 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
99
113
  dataFormat,
100
114
  debugSvg
101
115
  } = config
102
- const { suffix, onlyShowTopPrefixSuffix } = dataFormat
103
- const { labelsAboveGridlines, hideAxis } = config.yAxis
116
+ const { labelsAboveGridlines, hideAxis, inlineLabel } = config.yAxis
104
117
 
105
118
  // HOOKS % STATES
106
119
  const { minValue, maxValue, existPositiveValue, isAllLine } = useReduceData(config, data)
107
120
  const { visSupportsReactTooltip } = useEditorPermissions()
108
121
  const { hasTopAxis } = getTopAxis(config)
109
122
  const [animatedChart, setAnimatedChart] = useState(false)
123
+ const [showHoverLine, setShowHoverLine] = useState(false)
110
124
  const [point, setPoint] = useState({ x: 0, y: 0 })
111
125
  const [suffixWidth, setSuffixWidth] = useState(0)
112
126
  const [yAxisAutoPadding, setYAxisAutoPadding] = useState(0)
@@ -121,6 +135,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
121
135
  const xAxisTitleRef = useRef(null)
122
136
  const lastMaxValue = useRef(maxValue)
123
137
  const gridLineRefs = useRef([])
138
+ const tooltipRef = useRef(null)
124
139
 
125
140
  const dataRef = useIntersectionObserver(triggerRef, {
126
141
  freezeOnceVisible: false
@@ -132,8 +147,8 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
132
147
  const isLogarithmicAxis = config.yAxis.type === 'logarithmic'
133
148
  const isForestPlot = visualizationType === 'Forest Plot'
134
149
  const isDateTime = config.xAxis.type === 'date-time'
135
- const suffixHasNoSpace = !suffix.includes(' ')
136
- const labelsOverflow = onlyShowTopPrefixSuffix && !suffixHasNoSpace
150
+ const inlineLabelHasNoSpace = !inlineLabel?.includes(' ')
151
+ const labelsOverflow = inlineLabel && !inlineLabelHasNoSpace
137
152
  const padding = orientation === 'horizontal' ? Number(config.xAxis.size) : Number(config.yAxis.size)
138
153
  const yLabelOffset = isNaN(parseInt(`${runtime.yAxis.labelOffset}`)) ? 0 : parseInt(`${runtime.yAxis.labelOffset}`)
139
154
  const tickLabelFontSize = isMobileHeightViewport(currentViewport) ? TICK_LABEL_FONT_SIZE_SMALL : TICK_LABEL_FONT_SIZE
@@ -218,7 +233,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
218
233
  yMax
219
234
  }
220
235
  const { min, max, leftMax, rightMax } = useMinMax(properties)
221
- const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data, updateConfig })
236
+ const { yScaleRight, hasRightAxis } = useRightAxis({ config, yMax, data })
222
237
  const { xScale, yScale, seriesScale, g1xScale, g2xScale, xScaleNoPadding, xScaleAnnotation } = useScales({
223
238
  ...properties,
224
239
  min,
@@ -226,7 +241,10 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
226
241
  leftMax,
227
242
  rightMax,
228
243
  dimensions,
229
- xMax: parentWidth - Number(config.orientation === 'horizontal' ? config.xAxis.size : config.yAxis.size)
244
+ xMax:
245
+ parentWidth -
246
+ Number(config.orientation === 'horizontal' ? config.xAxis.size : config.yAxis.size) -
247
+ (hasRightAxis ? config.yAxis.rightAxisSize : 0)
230
248
  })
231
249
 
232
250
  const [yTickCount, xTickCount] = ['yAxis', 'xAxis'].map(axis =>
@@ -235,7 +253,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
235
253
  const handleNumTicks = isForestPlot ? config.data.length : yTickCount
236
254
 
237
255
  // Tooltip Helpers
238
- const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } = useTooltip()
256
+
257
+ const { tooltipData, showTooltip, hideTooltip, tooltipOpen, tooltipLeft, tooltipTop } =
258
+ useTooltip<UseTooltipReturn<TooltipData>>()
239
259
 
240
260
  // prettier-ignore
241
261
  const {
@@ -243,12 +263,12 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
243
263
  handleTooltipClick,
244
264
  handleTooltipMouseOff,
245
265
  TooltipListItem,
246
- getXValueFromCoordinate
247
266
  } = useCoveTooltip({
248
- xScale,
249
- yScale,
250
- showTooltip,
251
- hideTooltip
267
+ xScale,
268
+ yScale,
269
+ seriesScale,
270
+ showTooltip,
271
+ hideTooltip
252
272
  })
253
273
  // get the number of months between the first and last date
254
274
  const { dataKey } = runtime.xAxis
@@ -256,7 +276,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
256
276
  data.length && isDateTime
257
277
  ? [0, data.length - 1].map(i => parseDate(data[i][dataKey])).reduce((a, b) => Math.abs(a - b)) / MONTH_AS_MS
258
278
  : 0
259
- const useDateSpanMonths = isDateTime && dateSpanMonths > xTickCount
279
+ const useDateSpanMonths = isDateTime && dateSpanMonths > xTickCount && !config.runtime.xAxis.manual
260
280
 
261
281
  // GETTERS & FUNCTIONS
262
282
  const handleLeftTickFormatting = (tick, index, ticks) => {
@@ -268,7 +288,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
268
288
  if (config.data && !config.data[index] && visualizationType === 'Forest Plot') return
269
289
  if (config.visualizationType === 'Forest Plot') return config.data[index][config.xAxis.dataKey]
270
290
  if (isDateScale(runtime.yAxis)) return formatDate(parseDate(tick))
271
- if (orientation === 'vertical' && max - min < 3)
291
+ if (orientation === 'vertical' && max - min < 3 && !config.dataFormat?.roundTo)
272
292
  return formatNumber(tick, 'left', shouldAbbreviate, false, false, '1', { index, length: ticks.length })
273
293
  if (orientation === 'vertical') {
274
294
  // TODO suggestion: pass all options as object key/values to allow for more flexibility
@@ -372,7 +392,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
372
392
  if (!suffixEl) return setSuffixWidth(0)
373
393
  const suffixElWidth = suffixEl.getBBox().width
374
394
  setSuffixWidth(suffixElWidth)
375
- }, [config.dataFormat.suffix, config.dataFormat.onlyShowTopPrefixSuffix])
395
+ }, [config.dataFormat.suffix, inlineLabel])
376
396
 
377
397
  // forest plot x-axis label positioning
378
398
  useEffect(() => {
@@ -398,11 +418,11 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
398
418
  const topLabelOnGridline = topYLabelRef.current && yAxis.labelsAboveGridlines
399
419
 
400
420
  // Heights to add
401
-
402
421
  const brushHeight = brush?.active ? brush?.height + brush?.height : 0
422
+ const brushHeightWithMargin = config.brush?.active ? brushHeight + brushHeight : 0
403
423
  const forestRowsHeight = isForestPlot ? config.data.length * forestPlot.rowHeight : 0
404
424
  const topLabelOnGridlineHeight = topLabelOnGridline ? topYLabelRef.current.getBBox().height : 0
405
- const additionalHeight = axisBottomHeight + brushHeight + forestRowsHeight + topLabelOnGridlineHeight
425
+ const additionalHeight = axisBottomHeight + brushHeightWithMargin + forestRowsHeight + topLabelOnGridlineHeight
406
426
  const newHeight = initialHeight + additionalHeight
407
427
  if (!parentRef.current) return
408
428
 
@@ -438,7 +458,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
438
458
  }, [maxValue])
439
459
 
440
460
  useEffect(() => {
441
- if (orientation === 'horizontal' || !labelsOverflow || config.yAxis?.max) {
461
+ if (!yScale?.ticks) return
462
+ const ticks = yScale.ticks(handleNumTicks)
463
+ if (orientation === 'horizontal' || !labelsOverflow || config.yAxis?.max || ticks.length === 0) {
442
464
  setYAxisAutoPadding(0)
443
465
  return
444
466
  }
@@ -446,13 +468,12 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
446
468
  // minimum percentage of the max value that the distance should be from the top grid line
447
469
  const MINIMUM_DISTANCE_PERCENTAGE = 0.025
448
470
 
449
- const topGridLine = Math.max(...yScale.ticks(handleNumTicks))
471
+ const topGridLine = Math.max(...ticks)
450
472
  const needsPaddingThreshold = topGridLine - maxValue * MINIMUM_DISTANCE_PERCENTAGE
451
473
  const maxValueIsGreaterThanThreshold = maxValue > needsPaddingThreshold
452
474
 
453
475
  if (!maxValueIsGreaterThanThreshold) return
454
476
 
455
- const ticks = yScale.ticks(handleNumTicks)
456
477
  const tickGap = ticks.length === 1 ? ticks[0] : ticks[1] - ticks[0]
457
478
  const nextTick = Math.max(...yScale.ticks(handleNumTicks)) + tickGap
458
479
  const divideBy = minValue < 0 ? maxValue / 2 : maxValue
@@ -467,6 +488,23 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
467
488
  setYAxisAutoPadding(newPadding * 100 + 0.1)
468
489
  }, [maxValue, labelsOverflow, yScale, handleNumTicks])
469
490
 
491
+ useEffect(() => {
492
+ if (!tooltipOpen) return
493
+ if (!tooltipRef.current) return
494
+
495
+ const { dataXPosition } = tooltipData as { [key: string]: number }
496
+
497
+ if (!dataXPosition) return
498
+
499
+ const { width: tooltipWidth } = tooltipRef.current.node.getBoundingClientRect()
500
+
501
+ const rightSideRemainingSpace = parentWidth - dataXPosition
502
+
503
+ const rightSide = rightSideRemainingSpace <= tooltipWidth && dataXPosition > parentWidth / 2 - 10
504
+ const maxWidth = rightSide ? dataXPosition - 10 : parentWidth - (dataXPosition + 6)
505
+ tooltipRef.current.node.style.maxWidth = `${maxWidth}px`
506
+ }, [tooltipOpen, tooltipData])
507
+
470
508
  // Render Functions
471
509
  const generatePairedBarAxis = () => {
472
510
  const axisMaxHeight = bottomLabelStart + BOTTOM_LABEL_PADDING
@@ -611,7 +649,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
611
649
  </>
612
650
  )
613
651
  }
614
-
615
652
  return isNaN(width) ? (
616
653
  <React.Fragment></React.Fragment>
617
654
  ) : (
@@ -624,7 +661,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
624
661
  <svg
625
662
  ref={svgRef}
626
663
  onMouseMove={onMouseMove}
627
- width={parentWidth}
664
+ width={parentWidth + config.yAxis.rightAxisSize}
628
665
  height={isNoDataAvailable ? 1 : parentHeight}
629
666
  className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${
630
667
  debugSvg && 'debug'
@@ -632,6 +669,8 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
632
669
  role='img'
633
670
  aria-label={handleChartAriaLabels(config)}
634
671
  style={{ overflow: 'visible' }}
672
+ onMouseLeave={() => setShowHoverLine(false)}
673
+ onMouseEnter={() => setShowHoverLine(true)}
635
674
  >
636
675
  {!isDraggingAnnotation && <Bar width={parentWidth} height={initialHeight} fill={'transparent'}></Bar>}{' '}
637
676
  {/* GRID LINES */}
@@ -717,24 +756,8 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
717
756
  yScale={yScale}
718
757
  />
719
758
  )}
720
- {((visualizationType === 'Area Chart' && config.visualizationSubType === 'regular') ||
721
- (visualizationType === 'Combo' && config.visualizationSubType === 'regular')) && (
722
- <AreaChart
723
- xScale={xScale}
724
- yScale={yScale}
725
- yMax={yMax}
726
- xMax={xMax}
727
- chartRef={svgRef}
728
- width={xMax}
729
- height={yMax}
730
- handleTooltipMouseOver={handleTooltipMouseOver}
731
- handleTooltipMouseOff={handleTooltipMouseOff}
732
- tooltipData={tooltipData}
733
- showTooltip={showTooltip}
734
- />
735
- )}
736
759
  {((visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked') ||
737
- (visualizationType === 'Combo' && config.visualizationSubType === 'stacked')) && (
760
+ visualizationType === 'Combo') && (
738
761
  <AreaChartStacked
739
762
  xScale={xScale}
740
763
  yScale={yScale}
@@ -768,9 +791,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
768
791
  chartRef={svgRef}
769
792
  />
770
793
  )}
771
- {((visualizationType === 'Line' && !convertLineToBarGraph) ||
772
- visualizationType === 'Combo' ||
773
- visualizationType === 'Bump Chart') && (
794
+ {(visualizationType === 'Combo' || visualizationType === 'Bump Chart') && (
774
795
  <LineChart
775
796
  xScale={xScale}
776
797
  yScale={yScale}
@@ -784,9 +805,29 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
784
805
  handleTooltipClick={handleTooltipClick}
785
806
  tooltipData={tooltipData}
786
807
  showTooltip={showTooltip}
787
- chartRef={svgRef}
788
808
  />
789
809
  )}
810
+ {/* Line chart */}
811
+ {/* TODO: Make this just line or combo? */}
812
+ {!['Paired Bar', 'Box Plot', 'Area Chart', 'Scatter Plot', 'Deviation Bar', 'Forecasting', 'Bar'].includes(
813
+ visualizationType
814
+ ) &&
815
+ !convertLineToBarGraph && (
816
+ <>
817
+ <LineChart
818
+ xScale={xScale}
819
+ yScale={yScale}
820
+ getXAxisData={getXAxisData}
821
+ getYAxisData={getYAxisData}
822
+ xMax={xMax}
823
+ yMax={yMax}
824
+ seriesStyle={config.runtime.series}
825
+ tooltipData={tooltipData}
826
+ handleTooltipMouseOver={handleTooltipMouseOver}
827
+ handleTooltipMouseOff={handleTooltipMouseOff}
828
+ />
829
+ </>
830
+ )}
790
831
  {(visualizationType === 'Forecasting' || visualizationType === 'Combo') && (
791
832
  <Forecasting
792
833
  showTooltip={showTooltip}
@@ -794,14 +835,11 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
794
835
  xScale={xScale}
795
836
  yScale={yScale}
796
837
  width={xMax}
797
- le
798
838
  height={yMax}
799
839
  xScaleNoPadding={xScaleNoPadding}
800
840
  chartRef={svgRef}
801
- getXValueFromCoordinate={getXValueFromCoordinate}
802
841
  handleTooltipMouseOver={handleTooltipMouseOver}
803
842
  handleTooltipMouseOff={handleTooltipMouseOff}
804
- isBrush={false}
805
843
  />
806
844
  )}
807
845
  {visualizationType === 'Forest Plot' && (
@@ -842,6 +880,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
842
880
  xMax={xMax}
843
881
  yMax={yMax}
844
882
  seriesStyle={config.runtime.series}
883
+ tooltipData={tooltipData}
884
+ handleTooltipMouseOver={handleTooltipMouseOver}
885
+ handleTooltipMouseOff={handleTooltipMouseOff}
845
886
  />
846
887
  </>
847
888
  )}
@@ -869,7 +910,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
869
910
  strokeDasharray={handleLineType(anchor.lineStyle)}
870
911
  stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
871
912
  className='anchor-y'
872
- from={{ x: 0 + padding, y: position - middleOffset}}
913
+ from={{ x: 0 + padding, y: position - middleOffset }}
873
914
  to={{ x: width - config.yAxis.rightAxisSize, y: position - middleOffset }}
874
915
  />
875
916
  )
@@ -901,14 +942,14 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
901
942
  return (
902
943
  // prettier-ignore
903
944
  <Line
904
- key={`xAxis-${anchor.value}--${index}`}
905
- strokeDasharray={handleLineType(anchor.lineStyle)}
906
- stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
907
- fill={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
908
- className='anchor-x'
909
- from={{ x: Number(anchorPosition) + Number(padding), y: 0 }}
910
- to={{ x: Number(anchorPosition) + Number(padding), y: yMax }}
911
- />
945
+ key={`xAxis-${anchor.value}--${index}`}
946
+ strokeDasharray={handleLineType(anchor.lineStyle)}
947
+ stroke={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
948
+ fill={anchor.color ? anchor.color : 'rgba(0,0,0,1)'}
949
+ className='anchor-x'
950
+ from={{ x: Number(anchorPosition) + Number(padding), y: 0 }}
951
+ to={{ x: Number(anchorPosition) + Number(padding), y: yMax }}
952
+ />
912
953
  )
913
954
  })}
914
955
  {/* we are handling regions in bar charts differently, so that we can calculate the bar group into the region space. */}
@@ -926,36 +967,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
926
967
  width={width}
927
968
  />
928
969
  )}
929
- {chartHasTooltipGuides && showTooltip && tooltipData && config.visual.verticalHoverLine && (
930
- <Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
931
- <Line
932
- from={{ x: tooltipData.dataXPosition - 10, y: 0 }}
933
- to={{ x: tooltipData.dataXPosition - 10, y: yMax }}
934
- stroke={'black'}
935
- strokeWidth={1}
936
- pointerEvents='none'
937
- strokeDasharray='5,5'
938
- className='vertical-tooltip-line'
939
- />
940
- </Group>
941
- )}
942
- {chartHasTooltipGuides && showTooltip && tooltipData && config.visual.horizontalHoverLine && (
943
- <Group
944
- key='tooltipLine-horizontal'
945
- className='horizontal-tooltip-line'
946
- left={config.yAxis.size ? config.yAxis.size : 0}
947
- >
948
- <Line
949
- from={{ x: 0, y: tooltipData.dataYPosition }}
950
- to={{ x: xMax, y: tooltipData.dataYPosition }}
951
- stroke={'black'}
952
- strokeWidth={1}
953
- pointerEvents='none'
954
- strokeDasharray='5,5'
955
- className='horizontal-tooltip-line'
956
- />
957
- </Group>
958
- )}
959
970
  {isNoDataAvailable && (
960
971
  <Text
961
972
  x={Number(config.yAxis.size) + Number(xMax / 2)}
@@ -965,40 +976,12 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
965
976
  {config.chartMessage.noData}
966
977
  </Text>
967
978
  )}
968
- {(config.visualizationType === 'Bar' || convertLineToBarGraph) &&
969
- config.tooltips.singleSeries &&
970
- config.visual.horizontalHoverLine && (
971
- <Group
972
- key='tooltipLine-horizontal'
973
- className='horizontal-tooltip-line'
974
- left={config.yAxis.size ? config.yAxis.size : 0}
975
- >
976
- <Line
977
- from={{ x: 0, y: point.y }}
978
- to={{ x: xMax, y: point.y }}
979
- stroke={'black'}
980
- strokeWidth={1}
981
- pointerEvents='none'
982
- strokeDasharray='5,5'
983
- className='horizontal-tooltip-line'
984
- />
985
- </Group>
986
- )}
987
- {(config.visualizationType === 'Bar' || convertLineToBarGraph) &&
988
- config.tooltips.singleSeries &&
989
- config.visual.verticalHoverLine && (
990
- <Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
991
- <Line
992
- from={{ x: point.x, y: 0 }}
993
- to={{ x: point.x, y: yMax }}
994
- stroke={'black'}
995
- strokeWidth={1}
996
- pointerEvents='none'
997
- strokeDasharray='5,5'
998
- className='vertical-tooltip-line'
999
- />
1000
- </Group>
1001
- )}
979
+ {showHoverLine && (
980
+ <>
981
+ <HoverLine xMax={xMax} yMax={yMax} point={point} tooltipData={tooltipData} orientation='horizontal' />
982
+ <HoverLine xMax={xMax} yMax={yMax} point={point} tooltipData={tooltipData} orientation='vertical' />
983
+ </>
984
+ )}
1002
985
  <Group left={Number(config.runtime.yAxis.size)}>
1003
986
  <Annotation.Draggable
1004
987
  xScale={xScale}
@@ -1069,15 +1052,18 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1069
1052
 
1070
1053
  // Vertical value/suffix vars
1071
1054
  const lastTick = props.ticks.length - 1 === i
1072
- const hideTopTick = lastTick && onlyShowTopPrefixSuffix && suffix && !suffixHasNoSpace
1055
+ const useInlineLabel = lastTick && inlineLabel
1056
+ const hideTopTick = lastTick && inlineLabel && !inlineLabelHasNoSpace
1073
1057
  const valueOnLinePadding = hideAxis ? -8 : -12
1074
1058
  const labelXPadding = labelsAboveGridlines ? valueOnLinePadding : TICK_LABEL_MARGIN_RIGHT
1075
1059
  const labelYPadding = labelsAboveGridlines ? 4 : 0
1076
1060
  const labelX = tick.to.x - labelXPadding
1077
1061
  const labelY = tick.to.y - labelYPadding
1078
1062
  const labelVerticalAnchor = labelsAboveGridlines ? 'end' : 'middle'
1079
- const combineDomSuffixWithValue =
1080
- onlyShowTopPrefixSuffix && labelsAboveGridlines && suffix && lastTick
1063
+ const combineDomInlineLabelWithValue = inlineLabel && labelsAboveGridlines && lastTick
1064
+ const formattedValue = useInlineLabel
1065
+ ? tick.formattedValue.replace(config.dataFormat.suffix, '')
1066
+ : tick.formattedValue
1081
1067
 
1082
1068
  return (
1083
1069
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
@@ -1196,11 +1182,11 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1196
1182
  visualizationType !== 'Bump Chart' &&
1197
1183
  !config.yAxis.hideLabel && (
1198
1184
  <>
1199
- {/* TOP ONLY SUFFIX: Dom suffix for 'show only top suffix' behavior */}
1200
- {/* top suffix is shown alone and is allowed to 'overflow' to the right */}
1201
- {/* SPECIAL ONE CHAR CASE: a one character top-only suffix does not overflow */}
1185
+ {/* INLINE LABEL BEHAVIOR: Dom suffix for 'inlineLabel' behavior */}
1186
+ {/* inline label is shown alone and is allowed to 'overflow' to the right */}
1187
+ {/* SPECIAL ONE CHAR CASE: a one character inlineLabel does not overflow */}
1202
1188
  {/* IF VALUES ON LINE: suffix is combined with value to avoid having to calculate varying (now left-aligned) value widths */}
1203
- {onlyShowTopPrefixSuffix && lastTick && !labelsAboveGridlines && (
1189
+ {inlineLabel && lastTick && !labelsAboveGridlines && (
1204
1190
  <BlurStrokeText
1205
1191
  innerRef={suffixRef}
1206
1192
  display={isLogarithmicAxis ? showTicks : 'block'}
@@ -1209,7 +1195,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1209
1195
  y={labelY}
1210
1196
  angle={-Number(config.yAxis.tickRotation) || 0}
1211
1197
  verticalAnchor={labelVerticalAnchor}
1212
- textAnchor={suffixHasNoSpace ? 'end' : 'start'}
1198
+ textAnchor={inlineLabelHasNoSpace ? 'end' : 'start'}
1213
1199
  fill={config.yAxis.tickLabelColor}
1214
1200
  stroke={'#fff'}
1215
1201
  paintOrder={'stroke'} // keeps stroke under fill
@@ -1217,7 +1203,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1217
1203
  style={{ whiteSpace: 'pre-wrap' }} // prevents leading spaces from being trimmed
1218
1204
  fontSize={tickLabelFontSize}
1219
1205
  >
1220
- {suffix}
1206
+ {inlineLabel}
1221
1207
  </BlurStrokeText>
1222
1208
  )}
1223
1209
 
@@ -1226,7 +1212,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1226
1212
  innerRef={el => lastTick && (topYLabelRef.current = el)}
1227
1213
  display={isLogarithmicAxis ? showTicks : 'block'}
1228
1214
  dx={isLogarithmicAxis ? -6 : 0}
1229
- x={suffixHasNoSpace ? labelX - suffixWidth : labelX}
1215
+ x={inlineLabelHasNoSpace ? labelX - suffixWidth : labelX}
1230
1216
  y={labelY + (config.runtime.horizontal ? horizontalTickOffset : 0)}
1231
1217
  angle={-Number(config.yAxis.tickRotation) || 0}
1232
1218
  verticalAnchor={config.runtime.horizontal ? 'start' : labelVerticalAnchor}
@@ -1239,7 +1225,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1239
1225
  style={{ whiteSpace: 'pre-wrap' }} // prevents leading spaces from being trimmed
1240
1226
  fontSize={tickLabelFontSize}
1241
1227
  >
1242
- {`${tick.formattedValue}${combineDomSuffixWithValue ? suffix : ''}`}
1228
+ {`${formattedValue}${combineDomInlineLabelWithValue ? inlineLabel : ''}`}
1243
1229
  </BlurStrokeText>
1244
1230
  </>
1245
1231
  )}
@@ -1543,13 +1529,15 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1543
1529
  showTooltip &&
1544
1530
  !tooltipData?.data?.some(subArray => subArray.some(item => item === undefined)) &&
1545
1531
  tooltipData.dataYPosition &&
1546
- tooltipData.dataXPosition && (
1532
+ tooltipData.dataXPosition &&
1533
+ !config.tooltips.singleSeries && (
1547
1534
  <>
1548
1535
  <style>{`.tooltip {background-color: rgba(255,255,255, ${
1549
1536
  config.tooltips.opacity / 100
1550
1537
  }) !important;`}</style>
1551
1538
  <style>{`.tooltip {max-width:300px} !important; word-wrap: break-word; `}</style>
1552
1539
  <TooltipWithBounds
1540
+ ref={tooltipRef}
1553
1541
  key={Math.random()}
1554
1542
  className={'tooltip cdc-open-viz-module'}
1555
1543
  left={tooltipLeft}
@@ -1564,7 +1552,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1564
1552
  </TooltipWithBounds>
1565
1553
  </>
1566
1554
  )}
1567
-
1568
1555
  {config.visualizationType === 'Bump Chart' && (
1569
1556
  <ReactTooltip
1570
1557
  id={`bump-chart`}
@@ -1574,7 +1561,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1574
1561
  style={{ background: `rgba(255,255,255, ${config.tooltips.opacity / 100})`, color: 'black' }}
1575
1562
  />
1576
1563
  )}
1577
- {visSupportsReactTooltip() && !isDraggingAnnotation && (
1564
+ {!isDraggingAnnotation && (
1578
1565
  <ReactTooltip
1579
1566
  id={`cdc-open-viz-tooltip-${runtime.uniqueId}`}
1580
1567
  variant='light'
@@ -6,6 +6,7 @@ import { Text } from '@visx/text'
6
6
 
7
7
  import ConfigContext from '../ConfigContext'
8
8
  import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
9
+ import { APP_FONT_COLOR } from '@cdc/core/helpers/constants'
9
10
  import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
10
11
 
11
12
  const PairedBarChart = ({ width, height, originalWidth }) => {
@@ -47,8 +48,8 @@ const PairedBarChart = ({ width, height, originalWidth }) => {
47
48
  })
48
49
 
49
50
  // Set label color
50
- groupOne.labelColor = groupOne.color ? getContrastColor('#000', groupOne.color) : '#000'
51
- groupTwo.labelColor = groupTwo.color ? getContrastColor('#000', groupTwo.color) : '#000'
51
+ groupOne.labelColor = groupOne.color ? getContrastColor(APP_FONT_COLOR, groupOne.color) : APP_FONT_COLOR
52
+ groupTwo.labelColor = groupTwo.color ? getContrastColor(APP_FONT_COLOR, groupTwo.color) : APP_FONT_COLOR
52
53
 
53
54
  const label = config.yAxis.label ? `${config.yAxis.label}: ` : ''
54
55