@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
@@ -4,12 +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
+ import _ from 'lodash'
11
+
12
+ // CDC Components
10
13
  import { isDateScale } from '@cdc/core/helpers/cove/date'
11
14
  import BrushChart from './BrushChart'
12
- // CDC Components
13
15
  import { AreaChart, AreaChartStacked } from './AreaChart'
14
16
  import BarChart from './BarChart'
15
17
  import ConfigContext from '../ConfigContext'
@@ -28,7 +30,7 @@ import CategoricalYAxis from './Axis/Categorical.Axis'
28
30
  // Helpers
29
31
  import { isLegendWrapViewport, isMobileHeightViewport } from '@cdc/core/helpers/viewports'
30
32
  import { getTextWidth } from '@cdc/core/helpers/getTextWidth'
31
- import { calcInitialHeight } from '../helpers/sizeHelpers'
33
+ import { calcInitialHeight, handleAutoPaddingRight } from '../helpers/sizeHelpers'
32
34
 
33
35
  // Hooks
34
36
  import useMinMax from '../hooks/useMinMax'
@@ -41,7 +43,6 @@ import { useEditorPermissions } from './EditorPanel/useEditorPermissions'
41
43
  import Annotation from './Annotations'
42
44
  import { BlurStrokeText } from '@cdc/core/components/BlurStrokeText'
43
45
  import { countNumOfTicks } from '../helpers/countNumOfTicks'
44
- import _ from 'lodash'
45
46
 
46
47
  type LinearChartProps = {
47
48
  parentWidth: number
@@ -119,6 +120,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
119
120
  const xAxisLabelRefs = useRef([])
120
121
  const xAxisTitleRef = useRef(null)
121
122
  const lastMaxValue = useRef(maxValue)
123
+ const gridLineRefs = useRef([])
122
124
 
123
125
  const dataRef = useIntersectionObserver(triggerRef, {
124
126
  freezeOnceVisible: false
@@ -224,7 +226,10 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
224
226
  leftMax,
225
227
  rightMax,
226
228
  dimensions,
227
- xMax: parentWidth - Number(config.orientation === 'horizontal' ? config.xAxis.size : config.yAxis.size)
229
+ xMax:
230
+ parentWidth -
231
+ Number(config.orientation === 'horizontal' ? config.xAxis.size : config.yAxis.size) -
232
+ (hasRightAxis ? config.yAxis.rightAxisSize : 0)
228
233
  })
229
234
 
230
235
  const [yTickCount, xTickCount] = ['yAxis', 'xAxis'].map(axis =>
@@ -241,10 +246,10 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
241
246
  handleTooltipClick,
242
247
  handleTooltipMouseOff,
243
248
  TooltipListItem,
244
- getXValueFromCoordinate
245
249
  } = useCoveTooltip({
246
250
  xScale,
247
251
  yScale,
252
+ seriesScale,
248
253
  showTooltip,
249
254
  hideTooltip
250
255
  })
@@ -254,7 +259,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
254
259
  data.length && isDateTime
255
260
  ? [0, data.length - 1].map(i => parseDate(data[i][dataKey])).reduce((a, b) => Math.abs(a - b)) / MONTH_AS_MS
256
261
  : 0
257
- const useDateSpanMonths = isDateTime && dateSpanMonths > xTickCount
262
+ const useDateSpanMonths = isDateTime && dateSpanMonths > xTickCount && !config.runtime.xAxis.manual
258
263
 
259
264
  // GETTERS & FUNCTIONS
260
265
  const handleLeftTickFormatting = (tick, index, ticks) => {
@@ -329,6 +334,21 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
329
334
  }
330
335
 
331
336
  // EFFECTS
337
+ // Adjust padding on the right side of the chart to accommodate for overflow
338
+ useEffect(() => {
339
+ if (!parentRef.current || !parentWidth || !gridLineRefs.current.length) return
340
+
341
+ const [updatePadding, paddingToAdd] = handleAutoPaddingRight(parentRef, xAxisLabelRefs, parentWidth)
342
+
343
+ if (!updatePadding) return
344
+
345
+ parentRef.current.style.paddingRight = `${paddingToAdd}px`
346
+ // subtract padding from grid line's x1 value
347
+ gridLineRefs.current.forEach(gridLine => {
348
+ if (!gridLine) return
349
+ gridLine.setAttribute('x1', xMax - paddingToAdd)
350
+ })
351
+ }, [parentWidth, parentHeight, data])
332
352
 
333
353
  // Make sure the chart is visible if in the editor
334
354
  /* eslint-disable react-hooks/exhaustive-deps */
@@ -421,19 +441,22 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
421
441
  }, [maxValue])
422
442
 
423
443
  useEffect(() => {
424
- if (orientation === 'horizontal') return
425
- if (!labelsOverflow) return
444
+ if (!yScale?.ticks) return
445
+ const ticks = yScale.ticks(handleNumTicks)
446
+ if (orientation === 'horizontal' || !labelsOverflow || config.yAxis?.max || ticks.length === 0) {
447
+ setYAxisAutoPadding(0)
448
+ return
449
+ }
426
450
 
427
451
  // minimum percentage of the max value that the distance should be from the top grid line
428
452
  const MINIMUM_DISTANCE_PERCENTAGE = 0.025
429
453
 
430
- const topGridLine = Math.max(...yScale.ticks(handleNumTicks))
454
+ const topGridLine = Math.max(...ticks)
431
455
  const needsPaddingThreshold = topGridLine - maxValue * MINIMUM_DISTANCE_PERCENTAGE
432
456
  const maxValueIsGreaterThanThreshold = maxValue > needsPaddingThreshold
433
457
 
434
458
  if (!maxValueIsGreaterThanThreshold) return
435
459
 
436
- const ticks = yScale.ticks(handleNumTicks)
437
460
  const tickGap = ticks.length === 1 ? ticks[0] : ticks[1] - ticks[0]
438
461
  const nextTick = Math.max(...yScale.ticks(handleNumTicks)) + tickGap
439
462
  const divideBy = minValue < 0 ? maxValue / 2 : maxValue
@@ -592,7 +615,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
592
615
  </>
593
616
  )
594
617
  }
595
-
596
618
  return isNaN(width) ? (
597
619
  <React.Fragment></React.Fragment>
598
620
  ) : (
@@ -605,7 +627,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
605
627
  <svg
606
628
  ref={svgRef}
607
629
  onMouseMove={onMouseMove}
608
- width={parentWidth}
630
+ width={parentWidth + config.yAxis.rightAxisSize}
609
631
  height={isNoDataAvailable ? 1 : parentHeight}
610
632
  className={`linear ${config.animate ? 'animated' : ''} ${animatedChart && config.animate ? 'animate' : ''} ${
611
633
  debugSvg && 'debug'
@@ -638,6 +660,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
638
660
  <Group key={`vx-tick-${tick.value}-${i}`} className={'vx-axis-tick'}>
639
661
  {runtime.yAxis.gridLines && !hideFirstGridLine ? (
640
662
  <Line
663
+ innerRef={el => (gridLineRefs.current[i] = el)}
641
664
  key={`${tick.value}--hide-hideGridLines`}
642
665
  display={(isLogarithmicAxis && showTicks).toString()}
643
666
  from={{ x: tick.from.x + xMax, y: tick.from.y }}
@@ -697,22 +720,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
697
720
  yScale={yScale}
698
721
  />
699
722
  )}
700
- {((visualizationType === 'Area Chart' && config.visualizationSubType === 'regular') ||
701
- visualizationType === 'Combo') && (
702
- <AreaChart
703
- xScale={xScale}
704
- yScale={yScale}
705
- yMax={yMax}
706
- xMax={xMax}
707
- chartRef={svgRef}
708
- width={xMax}
709
- height={yMax}
710
- handleTooltipMouseOver={handleTooltipMouseOver}
711
- handleTooltipMouseOff={handleTooltipMouseOff}
712
- tooltipData={tooltipData}
713
- showTooltip={showTooltip}
714
- />
715
- )}
716
723
  {((visualizationType === 'Area Chart' && config.visualizationSubType === 'stacked') ||
717
724
  visualizationType === 'Combo') && (
718
725
  <AreaChartStacked
@@ -748,9 +755,7 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
748
755
  chartRef={svgRef}
749
756
  />
750
757
  )}
751
- {((visualizationType === 'Line' && !convertLineToBarGraph) ||
752
- visualizationType === 'Combo' ||
753
- visualizationType === 'Bump Chart') && (
758
+ {(visualizationType === 'Combo' || visualizationType === 'Bump Chart') && (
754
759
  <LineChart
755
760
  xScale={xScale}
756
761
  yScale={yScale}
@@ -764,7 +769,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
764
769
  handleTooltipClick={handleTooltipClick}
765
770
  tooltipData={tooltipData}
766
771
  showTooltip={showTooltip}
767
- chartRef={svgRef}
768
772
  />
769
773
  )}
770
774
  {(visualizationType === 'Forecasting' || visualizationType === 'Combo') && (
@@ -778,7 +782,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
778
782
  height={yMax}
779
783
  xScaleNoPadding={xScaleNoPadding}
780
784
  chartRef={svgRef}
781
- getXValueFromCoordinate={getXValueFromCoordinate}
782
785
  handleTooltipMouseOver={handleTooltipMouseOver}
783
786
  handleTooltipMouseOff={handleTooltipMouseOff}
784
787
  isBrush={false}
@@ -822,6 +825,9 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
822
825
  xMax={xMax}
823
826
  yMax={yMax}
824
827
  seriesStyle={config.runtime.series}
828
+ tooltipData={tooltipData}
829
+ handleTooltipMouseOver={handleTooltipMouseOver}
830
+ handleTooltipMouseOff={handleTooltipMouseOff}
825
831
  />
826
832
  </>
827
833
  )}
@@ -906,79 +912,45 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
906
912
  width={width}
907
913
  />
908
914
  )}
909
- {chartHasTooltipGuides && showTooltip && tooltipData && config.visual.verticalHoverLine && (
910
- <Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
915
+ {isNoDataAvailable && (
916
+ <Text
917
+ x={Number(config.yAxis.size) + Number(xMax / 2)}
918
+ y={initialHeight / 2 - (config.xAxis.padding || 0) / 2}
919
+ textAnchor='middle'
920
+ >
921
+ {config.chartMessage.noData}
922
+ </Text>
923
+ )}
924
+ {config.visual.horizontalHoverLine && tooltipData && (
925
+ <Group
926
+ key={`tooltipLine-horizontal${point.y}${point.x}`}
927
+ className='horizontal-tooltip-line'
928
+ left={config.yAxis.size ? config.yAxis.size : 0}
929
+ >
911
930
  <Line
912
- from={{ x: tooltipData.dataXPosition - 10, y: 0 }}
913
- to={{ x: tooltipData.dataXPosition - 10, y: yMax }}
931
+ from={{ x: 0, y: point.y }}
932
+ to={{ x: xMax, y: point.y }}
914
933
  stroke={'black'}
915
934
  strokeWidth={1}
916
935
  pointerEvents='none'
917
936
  strokeDasharray='5,5'
918
- className='vertical-tooltip-line'
937
+ className='horizontal-tooltip-line'
919
938
  />
920
939
  </Group>
921
940
  )}
922
- {chartHasTooltipGuides && showTooltip && tooltipData && config.visual.horizontalHoverLine && (
923
- <Group
924
- key='tooltipLine-horizontal'
925
- className='horizontal-tooltip-line'
926
- left={config.yAxis.size ? config.yAxis.size : 0}
927
- >
941
+ {config.visual.verticalHoverLine && tooltipData && (
942
+ <Group key={`tooltipLine-vertical${point.y}${point.x}`} className='vertical-tooltip-line'>
928
943
  <Line
929
- from={{ x: 0, y: tooltipData.dataYPosition }}
930
- to={{ x: xMax, y: tooltipData.dataYPosition }}
944
+ from={{ x: point.x, y: 0 }}
945
+ to={{ x: point.x, y: yMax }}
931
946
  stroke={'black'}
932
947
  strokeWidth={1}
933
948
  pointerEvents='none'
934
949
  strokeDasharray='5,5'
935
- className='horizontal-tooltip-line'
950
+ className='vertical-tooltip-line'
936
951
  />
937
952
  </Group>
938
953
  )}
939
- {isNoDataAvailable && (
940
- <Text
941
- x={Number(config.yAxis.size) + Number(xMax / 2)}
942
- y={initialHeight / 2 - (config.xAxis.padding || 0) / 2}
943
- textAnchor='middle'
944
- >
945
- {config.chartMessage.noData}
946
- </Text>
947
- )}
948
- {(config.visualizationType === 'Bar' || convertLineToBarGraph) &&
949
- config.tooltips.singleSeries &&
950
- config.visual.horizontalHoverLine && (
951
- <Group
952
- key='tooltipLine-horizontal'
953
- className='horizontal-tooltip-line'
954
- left={config.yAxis.size ? config.yAxis.size : 0}
955
- >
956
- <Line
957
- from={{ x: 0, y: point.y }}
958
- to={{ x: xMax, y: point.y }}
959
- stroke={'black'}
960
- strokeWidth={1}
961
- pointerEvents='none'
962
- strokeDasharray='5,5'
963
- className='horizontal-tooltip-line'
964
- />
965
- </Group>
966
- )}
967
- {(config.visualizationType === 'Bar' || convertLineToBarGraph) &&
968
- config.tooltips.singleSeries &&
969
- config.visual.verticalHoverLine && (
970
- <Group key='tooltipLine-vertical' className='vertical-tooltip-line'>
971
- <Line
972
- from={{ x: point.x, y: 0 }}
973
- to={{ x: point.x, y: yMax }}
974
- stroke={'black'}
975
- strokeWidth={1}
976
- pointerEvents='none'
977
- strokeDasharray='5,5'
978
- className='vertical-tooltip-line'
979
- />
980
- </Group>
981
- )}
982
954
  <Group left={Number(config.runtime.yAxis.size)}>
983
955
  <Annotation.Draggable
984
956
  xScale={xScale}
@@ -1399,20 +1371,21 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1399
1371
  const axisMaxHeight = bottomLabelStart + BOTTOM_LABEL_PADDING
1400
1372
 
1401
1373
  const containsMultipleWords = inputString => /\s/.test(inputString)
1402
- const ismultiLabel = filteredTicks.some(tick => containsMultipleWords(tick.value))
1374
+ const isMultiLabel = filteredTicks.some(tick => containsMultipleWords(tick.value))
1403
1375
 
1404
1376
  // Calculate sumOfTickWidth here, before map function
1405
- const tickWidthMax = Math.max(
1377
+ const longestTickLength = Math.max(
1406
1378
  ...filteredTicks.map(tick => getTextWidth(tick.formattedValue, GET_TEXT_WIDTH_FONT))
1407
1379
  )
1408
1380
  // const marginTop = 20 // moved to top bc need for yMax calcs
1409
- const accumulator = ismultiLabel ? 180 : 100
1381
+ const accumulator = isMultiLabel ? 180 : 100
1410
1382
 
1411
1383
  const textWidths = filteredTicks.map(tick => getTextWidth(tick.formattedValue, GET_TEXT_WIDTH_FONT))
1412
1384
  const sumOfTickWidth = textWidths.reduce((a, b) => a + b, accumulator)
1413
1385
  const spaceBetweenEachTick = (xMax - sumOfTickWidth) / (filteredTicks.length - 1)
1386
+ const bufferBetweenTicks = 40
1387
+ const maxLengthOfTick = width / filteredTicks.length - X_TICK_LABEL_PADDING * 2 - bufferBetweenTicks
1414
1388
 
1415
- // Check if ticks are overlapping
1416
1389
  // Determine the position of each tick
1417
1390
  let positions = [0] // The first tick is at position 0
1418
1391
  for (let i = 1; i < textWidths.length; i++) {
@@ -1424,35 +1397,22 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1424
1397
  const axisBBox = axisBottomRef?.current?.getBBox().height
1425
1398
  config.xAxis.axisBBox = axisBBox
1426
1399
 
1427
- // Check if ticks are overlapping
1428
- let areTicksTouching = false
1429
- textWidths.forEach((_, i) => {
1430
- if (positions[i] + textWidths[i] > positions[i + 1]) {
1431
- areTicksTouching = true
1432
- return
1433
- }
1434
- })
1435
-
1436
- // Force wrap when showing years once so it's easier to read
1437
- if (config.xAxis.showYearsOnce) {
1438
- areTicksTouching = true
1439
- }
1440
-
1441
1400
  // force wrap it last tick is close to the end of the axis
1442
1401
  const lastTickWidth = textWidths[textWidths.length - 1]
1443
1402
  const lastTickPosition = positions[positions.length - 1] + lastTickWidth
1444
1403
  const lastTickEnd = lastTickPosition + lastTickWidth / 2
1445
1404
  const lastTickEndThreshold = xMax - lastTickWidth
1446
1405
 
1447
- if (lastTickEnd > lastTickEndThreshold) {
1448
- areTicksTouching = true
1449
- }
1406
+ const areTicksTouching =
1407
+ textWidths.some(textWidth => textWidth > maxLengthOfTick) || // Force wrap if any tick is too long
1408
+ config.xAxis.showYearsOnce || // Force wrap when showing years once so it's easier to read
1409
+ lastTickEnd > lastTickEndThreshold // Force wrap it last tick is close to the end of the axis
1450
1410
 
1451
1411
  const dynamicMarginTop =
1452
- areTicksTouching && config.isResponsiveTicks ? tickWidthMax + DEFAULT_TICK_LENGTH + 20 : 0
1412
+ areTicksTouching && config.isResponsiveTicks ? longestTickLength + DEFAULT_TICK_LENGTH + 20 : 0
1453
1413
 
1454
1414
  config.dynamicMarginTop = dynamicMarginTop
1455
- config.xAxis.tickWidthMax = tickWidthMax
1415
+ config.xAxis.tickWidthMax = longestTickLength
1456
1416
 
1457
1417
  return (
1458
1418
  <Group className='bottom-axis' width={dimensions[0]}>
@@ -1556,7 +1516,6 @@ const LinearChart = forwardRef<SVGAElement, LinearChartProps>(({ parentHeight, p
1556
1516
  </TooltipWithBounds>
1557
1517
  </>
1558
1518
  )}
1559
-
1560
1519
  {config.visualizationType === 'Bump Chart' && (
1561
1520
  <ReactTooltip
1562
1521
  id={`bump-chart`}
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  import React, { useContext, useState, useEffect, useRef, useMemo } from 'react'
2
- import { animated, useTransition, interpolate } from 'react-spring'
2
+ import { animated, useTransition, to } from '@react-spring/web'
3
3
 
4
4
  // visx
5
5
  import { Pie } from '@visx/shape'
@@ -14,16 +14,9 @@ import { useTooltip as useCoveTooltip } from '../../hooks/useTooltip'
14
14
  import useIntersectionObserver from '../../hooks/useIntersectionObserver'
15
15
  import { handleChartAriaLabels } from '../../helpers/handleChartAriaLabels'
16
16
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
17
- import LegendComponent from '../Legend/Legend.Component'
18
- import { createFormatLabels } from '../Legend/helpers/createFormatLabels'
19
17
  import { scaleOrdinal } from '@visx/scale'
20
18
  import { getContrastColor } from '@cdc/core/helpers/cove/accessibility'
21
19
 
22
- const enterUpdateTransition = ({ startAngle, endAngle }) => ({
23
- startAngle,
24
- endAngle
25
- })
26
-
27
20
  type TooltipData = {
28
21
  data: {
29
22
  [key: string]: string | number
@@ -117,92 +110,71 @@ const PieChart = props => {
117
110
  }
118
111
  }, [dataRef?.isIntersecting, config.animate]) // eslint-disable-line
119
112
 
120
- const AnimatedPie = ({ arcs, path, getKey }) => {
121
- const transitions = useTransition(arcs, getKey, {
122
- from: enterUpdateTransition,
123
- enter: enterUpdateTransition,
124
- update: enterUpdateTransition,
125
- leave: enterUpdateTransition
113
+ function AnimatedPie({ arcs, path, getKey, colorScale, onHover, onLeave }) {
114
+ const enterExit = ({ startAngle, endAngle }) => ({ startAngle, endAngle })
115
+ const transitions = useTransition(arcs, {
116
+ keys: getKey,
117
+ from: enterExit,
118
+ enter: enterExit,
119
+ update: enterExit,
120
+ leave: enterExit
126
121
  })
127
122
 
128
- // DEV-5053
129
- // onMouseLeave function doesn't work on animated.path for some reason.
130
- // As a workaround, we continue to fire the tooltipData while hovered,
131
- // and use this useEffect to hide the tooltip so it doesn't persist when users scroll.
132
- useEffect(() => {
133
- const timeout = setTimeout(() => {
134
- hideTooltip()
135
- }, 500)
136
- return () => {
137
- clearTimeout(timeout)
138
- }
139
- }, [tooltipData])
140
-
141
- return (
142
- <>
143
- {transitions.map(({ item: arc, props, key }, animatedPieIndex) => {
144
- return (
145
- <Group
146
- className={arc.data[config.xAxis.dataKey]}
147
- key={`${key}-${animatedPieIndex}`}
148
- style={{
149
- opacity:
150
- config.legend.behavior === 'highlight' &&
151
- seriesHighlight.length > 0 &&
152
- seriesHighlight.indexOf(arc.data[config.runtime.xAxis.dataKey]) === -1
153
- ? 0.5
154
- : 1
155
- }}
156
- >
157
- <animated.path
158
- d={interpolate([props.startAngle, props.endAngle], (startAngle, endAngle) =>
159
- path({
160
- ...arc,
161
- startAngle,
162
- endAngle
163
- })
164
- )}
165
- fill={_colorScale(arc.data[config.runtime.xAxis.dataKey])}
166
- onMouseEnter={e => handleTooltipMouseOver(e, { data: arc.data[config.runtime.xAxis.dataKey], arc })}
167
- onMouseLeave={e => handleTooltipMouseOff()}
168
- />
169
- </Group>
170
- )
171
- })}
172
- {transitions.map(({ item: arc, key }, i) => {
173
- const roundTo = Number(config.dataFormat.roundTo) || 0
174
- const [centroidX, centroidY] = path.centroid(arc)
175
- const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.1
123
+ return transitions((styles, arc) => {
124
+ const key = getKey(arc)
125
+ let textColor = '#FFF'
176
126
 
177
- let textColor = '#FFF'
178
- if (_colorScale(arc.data[config.runtime.xAxis.dataKey])) {
179
- textColor = getContrastColor(textColor, _colorScale(arc.data[config.runtime.xAxis.dataKey]))
180
- }
181
- const degrees = ((arc.endAngle - arc.startAngle) * 180) / Math.PI
127
+ if (key && _colorScale(key)) {
128
+ textColor = getContrastColor(textColor, _colorScale(arc.data[config.runtime.xAxis.dataKey]))
129
+ }
130
+ const roundTo = Number(config.dataFormat.roundTo) || 0
131
+ // Calculate the percentage of the full circle (360 degrees)
132
+ const degrees = ((arc.endAngle - arc.startAngle) * 180) / Math.PI
182
133
 
183
- // Calculate the percentage of the full circle (360 degrees)
184
- const percentageOfCircle = (degrees / 360) * 100
185
- const roundedPercentage = percentageOfCircle.toFixed(roundTo)
134
+ const percentageOfCircle = (degrees / 360) * 100
135
+ const roundedPercentage = percentageOfCircle.toFixed(roundTo) + '%'
136
+ return (
137
+ <Group key={key} className={`slice-${key}`}>
138
+ {/* ── the slice */}
139
+ <animated.path
140
+ d={to([styles.startAngle, styles.endAngle], (start, end) =>
141
+ path({ ...arc, startAngle: start, endAngle: end })
142
+ )}
143
+ fill={colorScale(key)}
144
+ onMouseEnter={e =>
145
+ onHover(e, {
146
+ data: arc.data,
147
+ dataXPosition: e.clientX,
148
+ dataYPosition: e.clientY,
149
+ startAngle: arc.startAngle,
150
+ endAngle: arc.endAngle
151
+ })
152
+ }
153
+ onMouseLeave={onLeave}
154
+ />
186
155
 
187
- return (
188
- <animated.g key={`${key}${i}`}>
189
- {hasSpaceForLabel && (
190
- <Text
191
- style={{ fill: textColor }}
192
- x={centroidX}
193
- y={centroidY}
194
- dy='.33em'
195
- textAnchor='middle'
196
- pointerEvents='none'
197
- >
198
- {roundedPercentage + '%'}
199
- </Text>
200
- )}
201
- </animated.g>
202
- )
203
- })}
204
- </>
205
- )
156
+ {/* ── the percentage label */}
157
+ {arc.endAngle - arc.startAngle > 0.1 && (
158
+ <animated.text
159
+ transform={to([styles.startAngle, styles.endAngle], (start, end) => {
160
+ const [x, y] = path.centroid({
161
+ ...arc,
162
+ startAngle: start,
163
+ endAngle: end
164
+ })
165
+ return `translate(${x},${y})`
166
+ })}
167
+ textAnchor='middle'
168
+ pointerEvents='none'
169
+ fill={textColor}
170
+ >
171
+ {/** compute text inside the spring callback */}
172
+ {roundedPercentage}
173
+ </animated.text>
174
+ )}
175
+ </Group>
176
+ )
177
+ })
206
178
  }
207
179
 
208
180
  let chartWidth = props.parentWidth
@@ -257,12 +229,20 @@ const PieChart = props => {
257
229
  {/* prettier-ignore */}
258
230
  <Pie
259
231
  data={filteredData || _data}
260
- pieValue={d => d[pivotKey || config.runtime.yAxis.dataKey]}
232
+ pieValue={d => parseFloat(d[pivotKey || config.runtime.yAxis.dataKey])}
261
233
  pieSortValues={() => -1}
262
234
  innerRadius={radius - donutThickness}
263
235
  outerRadius={radius}
264
236
  >
265
- {pie => <AnimatedPie {...pie} getKey={d => d.data[config.runtime.xAxis.dataKey]}/>}
237
+ {pie => (
238
+ <AnimatedPie
239
+ {...pie}
240
+ getKey={d => d.data[config.runtime.xAxis.dataKey]}
241
+ colorScale={_colorScale}
242
+ onHover={handleTooltipMouseOver}
243
+ onLeave={handleTooltipMouseOff}
244
+ />
245
+ )}
266
246
  </Pie>
267
247
  </Group>
268
248
  </svg>
@@ -59,6 +59,11 @@ const ScatterPlot = ({ xScale, yScale }) => {
59
59
  opacity: 1,
60
60
  stroke: displayArea ? 'black' : ''
61
61
  }
62
+ if (item[s]==='') {
63
+ return <> </>
64
+ }
65
+
66
+
62
67
 
63
68
  return (
64
69
  <circle