@cdc/chart 4.26.1 → 4.26.2

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 (132) hide show
  1. package/CLAUDE.local.md +79 -0
  2. package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
  3. package/dist/cdcchart.js +45357 -43655
  4. package/examples/default.json +378 -0
  5. package/examples/feature/__data__/horizon-chart-data.json +373 -0
  6. package/examples/feature/annotations/index.json +3 -6
  7. package/examples/feature/horizon/horizon-chart.json +395 -0
  8. package/examples/line-chart-states.json +1085 -0
  9. package/examples/private/123.json +694 -0
  10. package/examples/private/anchor-issue.json +4094 -0
  11. package/examples/private/backwards-slider.json +10430 -0
  12. package/examples/private/georgia.csv +160 -0
  13. package/examples/private/timeline-data.json +1 -0
  14. package/examples/private/timeline.json +389 -0
  15. package/examples/radar-chart-simple.json +133 -0
  16. package/examples/radar-chart.json +148 -0
  17. package/index.html +1 -31
  18. package/package.json +57 -59
  19. package/src/CdcChartComponent.tsx +99 -18
  20. package/src/_stories/Chart.Anchors.stories.tsx +10 -0
  21. package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
  22. package/src/_stories/Chart.CI.stories.tsx +13 -0
  23. package/src/_stories/Chart.Combo.stories.tsx +17 -0
  24. package/src/_stories/Chart.CustomColors.stories.tsx +4 -0
  25. package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
  26. package/src/_stories/Chart.Filters.stories.tsx +4 -0
  27. package/src/_stories/Chart.Forecast.stories.tsx +4 -0
  28. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
  29. package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
  30. package/src/_stories/Chart.Patterns.stories.tsx +4 -0
  31. package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
  32. package/src/_stories/Chart.Regions.Categorical.stories.tsx +13 -0
  33. package/src/_stories/Chart.Regions.DateScale.stories.tsx +19 -0
  34. package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +25 -10
  35. package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
  36. package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
  37. package/src/_stories/Chart.stories.tsx +37 -0
  38. package/src/_stories/Chart.tooltip.stories.tsx +7 -0
  39. package/src/_stories/ChartAnnotation.stories.tsx +10 -0
  40. package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
  41. package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
  42. package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
  43. package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
  44. package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
  45. package/src/_stories/ChartBrush.stories.tsx +7 -0
  46. package/src/_stories/ChartEditor.stories.tsx +7 -0
  47. package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
  48. package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
  49. package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
  50. package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
  51. package/src/_stories/TechAdoptionWithLinks.stories.tsx +7 -0
  52. package/src/_stories/_mock/brush_continuous.json +86 -0
  53. package/src/_stories/_mock/brush_date_large.json +176 -0
  54. package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
  55. package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
  56. package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
  57. package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
  58. package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
  59. package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
  60. package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
  61. package/src/components/Annotations/components/AnnotationDraggable.styles.css +11 -17
  62. package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
  63. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
  64. package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
  65. package/src/components/Annotations/components/AnnotationList.styles.css +4 -10
  66. package/src/components/Annotations/components/AnnotationList.tsx +5 -4
  67. package/src/components/Annotations/components/findNearestDatum.ts +75 -85
  68. package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
  69. package/src/components/Axis/BottomAxis.tsx +270 -0
  70. package/src/components/Axis/LeftAxis.tsx +404 -0
  71. package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
  72. package/src/components/Axis/PairedBarAxis.tsx +186 -0
  73. package/src/components/Axis/README.md +94 -0
  74. package/src/components/Axis/RightAxis.tsx +108 -0
  75. package/src/components/Axis/axis.constants.ts +21 -0
  76. package/src/components/Axis/index.ts +7 -0
  77. package/src/components/BarChart/components/BarChart.tsx +7 -1
  78. package/src/components/Brush/BrushSelector.tsx +154 -22
  79. package/src/components/Brush/MiniChartPreview.tsx +138 -21
  80. package/src/components/EditorPanel/EditorPanel.tsx +25 -11
  81. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +60 -22
  82. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +81 -1
  83. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +1 -1
  84. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
  85. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +0 -1
  86. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +21 -1
  87. package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
  88. package/src/components/EditorPanel/useEditorPermissions.ts +55 -26
  89. package/src/components/HorizonChart/HorizonChart.tsx +131 -0
  90. package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
  91. package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
  92. package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
  93. package/src/components/HorizonChart/index.tsx +3 -0
  94. package/src/components/Legend/Legend.Component.tsx +52 -4
  95. package/src/components/Legend/Legend.tsx +1 -1
  96. package/src/components/Legend/LegendValueRange.tsx +77 -0
  97. package/src/components/Legend/helpers/createFormatLabels.tsx +13 -0
  98. package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
  99. package/src/components/LineChart/helpers/README.md +292 -0
  100. package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
  101. package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
  102. package/src/components/LineChart/index.tsx +44 -8
  103. package/src/components/LinearChart/README.md +109 -0
  104. package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
  105. package/src/components/LinearChart/linearChart.constants.ts +84 -0
  106. package/src/components/LinearChart/tests/LinearChart.test.tsx +201 -0
  107. package/src/components/LinearChart/tests/mockConfigContext.ts +129 -0
  108. package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
  109. package/src/components/LinearChart.tsx +250 -1059
  110. package/src/components/PieChart/PieChart.tsx +1 -1
  111. package/src/components/RadarChart/RadarAxis.tsx +78 -0
  112. package/src/components/RadarChart/RadarChart.tsx +298 -0
  113. package/src/components/RadarChart/RadarGrid.tsx +64 -0
  114. package/src/components/RadarChart/RadarPolygon.tsx +91 -0
  115. package/src/components/RadarChart/helpers.ts +83 -0
  116. package/src/components/RadarChart/index.tsx +3 -0
  117. package/src/components/WarmingStripes/WarmingStripes.tsx +95 -25
  118. package/src/data/initial-state.js +14 -1
  119. package/src/helpers/getExcludedData.ts +4 -0
  120. package/src/helpers/handleChartAriaLabels.ts +19 -19
  121. package/src/helpers/handleLineType.ts +22 -18
  122. package/src/hooks/useProgrammaticTooltip.ts +23 -2
  123. package/src/hooks/useScales.ts +7 -0
  124. package/src/hooks/useTooltip.tsx +3 -0
  125. package/src/scss/main.scss +5 -0
  126. package/src/selectors/README.md +68 -0
  127. package/src/store/chart.reducer.ts +2 -0
  128. package/src/types/ChartConfig.ts +18 -0
  129. package/src/types/ChartContext.ts +1 -0
  130. package/src/types/Horizon.ts +64 -0
  131. package/preview.html +0 -1616
  132. package/src/components/Annotations/components/helpers/index.tsx +0 -46
package/package.json CHANGED
@@ -1,84 +1,82 @@
1
1
  {
2
2
  "name": "@cdc/chart",
3
- "version": "4.26.1",
3
+ "version": "4.26.2",
4
4
  "description": "React component for visualizing tabular data in various types of charts",
5
- "moduleName": "CdcChart",
6
- "main": "dist/cdcchart",
7
- "type": "module",
8
- "scripts": {
9
- "start": "vite --open",
10
- "build": "vite build",
11
- "preview": "vite preview",
12
- "graph": "nx graph",
13
- "prepublishOnly": "lerna run --scope @cdc/chart build",
14
- "test": "vitest run --reporter verbose",
15
- "test-watch": "vitest watch --reporter verbose",
16
- "test-watch:ui": "vitest --ui"
17
- },
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/CDCgov/cdc-open-viz",
21
- "directory": "packages/chart"
22
- },
23
- "author": "Matthew Pallansch <mpallansch@adittech.com>",
24
- "bugs": {
25
- "url": "https://github.com/CDCgov/cdc-open-viz/issues"
26
- },
27
5
  "license": "Apache-2.0",
6
+ "author": "Matthew Pallansch <mpallansch@adittech.com>",
7
+ "bugs": "https://github.com/CDCgov/cdc-open-viz/issues",
28
8
  "dependencies": {
29
- "@cdc/core": "^4.26.1",
9
+ "@cdc/core": "^4.26.2",
30
10
  "@hello-pangea/dnd": "^16.2.0",
31
11
  "@react-spring/web": "^9.7.5",
32
12
  "@rollup/plugin-dsv": "^3.0.2",
33
- "@visx/annotation": "^3.3.0",
34
- "@visx/axis": "3.12.0",
13
+ "@visx/annotation": "^3.12.0",
14
+ "@visx/axis": "^3.12.0",
35
15
  "@visx/brush": "^3.12.0",
36
- "@visx/curve": "3.12.0",
16
+ "@visx/curve": "^3.12.0",
37
17
  "@visx/drag": "^3.12.0",
38
- "@visx/event": "3.12.0",
39
- "@visx/glyph": "3.12.0",
40
- "@visx/gradient": "3.12.0",
41
- "@visx/legend": "3.12.0",
42
- "@visx/marker": "3.12.0",
43
- "@visx/mock-data": "3.12.0",
44
- "@visx/pattern": "^3.0.0",
45
- "@visx/responsive": "^2.10.0",
46
- "@visx/scale": "3.12.0",
47
- "@visx/shape": "3.12.0",
48
- "@visx/stats": "3.12.0",
49
- "@visx/text": "3.12.0",
50
- "@visx/tooltip": "3.12.0",
51
- "@vitejs/plugin-react": "^4.3.4",
52
- "chroma-js": "3.1.2",
53
- "d3-array": "3.2.4",
54
- "d3-format": "^3.1.0",
18
+ "@visx/event": "^3.12.0",
19
+ "@visx/glyph": "^3.12.0",
20
+ "@visx/gradient": "^3.12.0",
21
+ "@visx/legend": "^3.12.0",
22
+ "@visx/marker": "^3.12.0",
23
+ "@visx/mock-data": "^3.12.0",
24
+ "@visx/pattern": "^3.12.0",
25
+ "@visx/responsive": "^3.12.0",
26
+ "@visx/scale": "^3.12.0",
27
+ "@visx/shape": "^3.12.0",
28
+ "@visx/stats": "^3.12.0",
29
+ "@visx/text": "^3.12.0",
30
+ "@visx/tooltip": "^3.12.0",
31
+ "@vitejs/plugin-react": "^5.1.2",
32
+ "chroma-js": "^3.1.2",
33
+ "d3-array": "^3.2.4",
34
+ "d3-format": "^3.1.2",
55
35
  "d3-sankey": "^0.12.3",
56
- "d3-time-format": "4.1.0",
57
- "dompurify": "^3.1.5",
58
- "html-react-parser": "5.2.3",
36
+ "d3-time-format": "^4.1.0",
37
+ "dompurify": "^3.3.1",
38
+ "html-react-parser": "^5.2.3",
59
39
  "js-base64": "^2.5.2",
60
- "lodash": "^4.17.21",
61
- "papaparse": "5.5.2",
40
+ "lodash": "^4.17.23",
41
+ "papaparse": "^5.5.2",
62
42
  "react-accessible-accordion": "^5.0.1",
63
- "react-icons": "5.5.0",
43
+ "react-icons": "^5.5.0",
64
44
  "react-tooltip": "5.8.2-beta.3",
65
45
  "resize-observer-polyfill": "^1.5.1",
66
46
  "sass": "^1.89.2",
67
- "use-debounce": "^6.0.1",
68
- "vite": "^5.4.21",
47
+ "use-debounce": "^10.1.0",
48
+ "vite": "^7.3.1",
69
49
  "vite-plugin-css-injected-by-js": "^2.4.0",
70
50
  "vite-plugin-svgr": "^4.2.0",
71
- "whatwg-fetch": "3.6.20"
72
- },
73
- "peerDependencies": {
74
- "react": "^18.2.0",
75
- "react-dom": "^18.2.0"
51
+ "whatwg-fetch": "^3.6.20"
76
52
  },
77
- "gitHead": "7e3b27098c4eb7a24bc9c3654ad53f88d6419f16",
78
53
  "devDependencies": {
79
54
  "@types/d3-array": "^3.2.1",
80
55
  "@types/d3-format": "^3.0.4",
81
56
  "@types/d3-sankey": "^0.12.4",
82
57
  "@types/d3-time-format": "^4.0.3"
83
- }
58
+ },
59
+ "gitHead": "be3413e8e1149abf94225108f86a7910f56e0616",
60
+ "main": "dist/cdcchart",
61
+ "moduleName": "CdcChart",
62
+ "peerDependencies": {
63
+ "react": "^18.2.0",
64
+ "react-dom": "^18.2.0"
65
+ },
66
+ "repository": {
67
+ "type": "git",
68
+ "url": "git+https://github.com/CDCgov/cdc-open-viz",
69
+ "directory": "packages/chart"
70
+ },
71
+ "scripts": {
72
+ "build": "vite build",
73
+ "graph": "nx graph",
74
+ "prepublishOnly": "lerna run --scope @cdc/chart build",
75
+ "preview": "vite preview",
76
+ "start": "vite --open",
77
+ "test": "vitest run --reporter verbose",
78
+ "test-watch": "vitest watch --reporter verbose",
79
+ "test-watch:ui": "vitest --ui"
80
+ },
81
+ "type": "module"
84
82
  }
@@ -22,15 +22,16 @@ import { Runtime } from '@cdc/core/types/Runtime'
22
22
  import { Label } from './types/Label'
23
23
  // External Libraries
24
24
  import ParentSize from '@visx/responsive/lib/components/ParentSize'
25
- import { timeParse, timeFormat } from 'd3-time-format'
25
+ import { timeParse } from 'd3-time-format'
26
26
  import parse from 'html-react-parser'
27
27
  import _ from 'lodash'
28
28
  // Primary Components
29
29
  import ConfigContext, { ChartDispatchContext } from './ConfigContext'
30
30
  import PieChart from './components/PieChart'
31
+ import RadarChart from './components/RadarChart'
31
32
  import SankeyChart from './components/Sankey'
32
33
  import LinearChart from './components/LinearChart'
33
- import { isDateScale } from '@cdc/core/helpers/cove/date'
34
+ import { isDateScale, formatDate as coreFormatDate } from '@cdc/core/helpers/cove/date'
34
35
 
35
36
  import { twoColorPalette } from '@cdc/core/data/colorPalettes'
36
37
  import { filterChartColorPalettes } from '@cdc/core/helpers/filterColorPalettes'
@@ -53,6 +54,7 @@ import Loading from '@cdc/core/components/Loading'
53
54
  import Filters from '@cdc/core/components/Filters'
54
55
  import MediaControls from '@cdc/core/components/MediaControls'
55
56
  import Annotation from './components/Annotations'
57
+ import { getVisibleAnnotations } from './components/Annotations/helpers/getVisibleAnnotations'
56
58
  // Core Helpers
57
59
  import { DataTransform } from '@cdc/core/helpers/DataTransform'
58
60
  import { isLegendWrapViewport } from '@cdc/core/helpers/viewports'
@@ -133,7 +135,8 @@ const CdcChart: React.FC<CdcChartProps> = ({
133
135
  coveLoadedEventRan,
134
136
  imageId,
135
137
  seriesHighlight,
136
- colorScale
138
+ colorScale,
139
+ brushData
137
140
  } = state
138
141
  const { description, visualizationType } = config
139
142
  const svgRef = useRef(null)
@@ -263,8 +266,39 @@ const CdcChart: React.FC<CdcChartProps> = ({
263
266
  delete defaultsWithoutPalette.general?.palette
264
267
  }
265
268
 
269
+ // Override palette defaults for Line charts specifically
270
+ if (loadedConfig?.visualizationType === 'Line' && !loadedConfig?.general?.palette) {
271
+ if (!defaultsWithoutPalette.general) {
272
+ defaultsWithoutPalette.general = {}
273
+ }
274
+ defaultsWithoutPalette.general.palette = {
275
+ isReversed: false,
276
+ version: '2.0',
277
+ name: 'divergent_blue_cyan'
278
+ }
279
+ }
280
+
281
+ // Override palette defaults for Horizon Chart specifically
282
+ if (loadedConfig?.visualizationType === 'Horizon Chart' && !loadedConfig?.general?.palette) {
283
+ if (!defaultsWithoutPalette.general) {
284
+ defaultsWithoutPalette.general = {}
285
+ }
286
+ defaultsWithoutPalette.general.palette = {
287
+ isReversed: false,
288
+ version: '2.0',
289
+ name: 'sequential_blue'
290
+ }
291
+ }
292
+
266
293
  let newConfig = { ...defaultsWithoutPalette, ...loadedConfig }
267
294
 
295
+ // Ensure Horizon Chart has enough palette colors for all layers
296
+ if (newConfig.visualizationType === 'Horizon Chart') {
297
+ const numLayers = newConfig.horizon?.numLayers ?? 4
298
+ const currentCount = _.get(newConfig, 'general.paletteColorCount', 4)
299
+ _.set(newConfig, 'general.paletteColorCount', Math.max(currentCount, numLayers))
300
+ }
301
+
268
302
  _.defaultsDeep(newConfig, {
269
303
  table: { showVertical: false }
270
304
  })
@@ -385,6 +419,11 @@ const CdcChart: React.FC<CdcChartProps> = ({
385
419
  newConfig.runtime.seriesKeys = _.uniq(pieData.map(d => d[newConfig.xAxis.dataKey]))
386
420
  newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
387
421
  newConfig.runtime.isPieChart = true // Flag to know when to use derived keys
422
+ } else if (newConfig.visualizationType === 'Radar') {
423
+ // Radar chart: seriesKeys are the entity names from xAxis.dataKey
424
+ const radarData = currentData.length > 0 ? currentData : newExcludedData
425
+ newConfig.runtime.seriesKeys = _.uniq(radarData.map(d => d[newConfig.xAxis.dataKey]))
426
+ newConfig.runtime.seriesLabelsAll = newConfig.runtime.seriesKeys
388
427
  } else {
389
428
  const finalData = dataOverride || newConfig.formattedData || newConfig.data
390
429
  newConfig.runtime.seriesKeys = (newConfig.runtime.series || []).flatMap(series => {
@@ -458,6 +497,22 @@ const CdcChart: React.FC<CdcChartProps> = ({
458
497
  newConfig.visualizationSubType = 'stacked'
459
498
  }
460
499
 
500
+ if (newConfig.visualizationType === 'Horizon Chart' && newConfig.series) {
501
+ // Apply horizon defaults if not set
502
+ newConfig.horizon = {
503
+ numLayers: 4,
504
+ mode: 'offset', // Always offset for now, mirror hidden from UI
505
+ bandGap: 15,
506
+ bottomPadding: 15,
507
+ ...newConfig.horizon
508
+ }
509
+
510
+ // Set categorical as default xAxis type for horizon charts if not already set
511
+ if (!newConfig.xAxis.type) {
512
+ newConfig.xAxis.type = 'categorical'
513
+ }
514
+ }
515
+
461
516
  if (isHorizontalVariant) {
462
517
  // For horizontal charts, axes are swapped, so processedYAxis goes to runtime.xAxis and vice versa
463
518
  const horizontalXAxisSource = _.cloneDeep((newConfig.yAxis as any)?.yAxis || newConfig.yAxis)
@@ -618,7 +673,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
618
673
  if (newData) {
619
674
  newConfig.data = newData
620
675
  }
621
- } else if (newConfig.formattedData) {
676
+ } else if (newConfig.formattedData && Array.isArray(newConfig.formattedData)) {
622
677
  newConfig.data = newConfig.formattedData
623
678
  } else if (newConfig.dataDescription) {
624
679
  // For dashboard contexts, get data from datasets if config.data is undefined
@@ -654,7 +709,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
654
709
  updateConfig(preparedConfig, preppedData.data)
655
710
  }
656
711
  } catch (err) {
657
- console.error('Could not Load!')
712
+ console.error('Could not Load!', err)
658
713
  }
659
714
  }
660
715
 
@@ -746,6 +801,16 @@ const CdcChart: React.FC<CdcChartProps> = ({
746
801
  }
747
802
  }, [config, stateData]) // eslint-disable-line
748
803
 
804
+ // Clear brush selection when brush slider is disabled
805
+ useEffect(() => {
806
+ const isBrushDisabled = !config?.xAxis?.brushActive
807
+ const hasBrushData = Array.isArray(brushData) && brushData.length > 0
808
+
809
+ if (isBrushDisabled && hasBrushData) {
810
+ dispatch({ type: 'SET_BRUSH_DATA', payload: [] })
811
+ }
812
+ }, [config?.xAxis?.brushActive, brushData])
813
+
749
814
  // Updates runtime axis labels when config or data changes when using markup variables
750
815
  useEffect(() => {
751
816
  if (
@@ -844,15 +909,11 @@ const CdcChart: React.FC<CdcChartProps> = ({
844
909
  const formatDate = (date, i, ticks) => {
845
910
  const displayFormat =
846
911
  config.runtime[section].dateDisplayFormat || config.runtime[section].dateParseFormat || '%Y-%m-%d'
847
- let formattedDate = timeFormat(displayFormat)(date)
848
- // Handle the case where all months work with '%b.' except for May
849
- if (displayFormat?.includes('%b.') && formattedDate.includes('May.')) {
850
- formattedDate = formattedDate.replace(/May\./g, 'May')
851
- }
912
+ let formattedDate = coreFormatDate(displayFormat, date)
852
913
  // Show years only once
853
914
  if (config.xAxis.showYearsOnce && displayFormat?.includes('%Y') && ticks) {
854
915
  const prevDate = ticks[i - 1] ? ticks[i - 1].value : null
855
- const prevFormattedDate = timeFormat(displayFormat)(prevDate)
916
+ const prevFormattedDate = coreFormatDate(displayFormat, prevDate)
856
917
  const year = formattedDate.match(/\d{4}/)
857
918
  const prevYear = prevFormattedDate.match(/\d{4}/)
858
919
  if (year && prevYear && year[0] === prevYear[0]) {
@@ -863,7 +924,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
863
924
  }
864
925
 
865
926
  const formatTooltipsDate = date => {
866
- return timeFormat(config.tooltips.dateDisplayFormat)(date)
927
+ return coreFormatDate(config.tooltips.dateDisplayFormat, date)
867
928
  }
868
929
 
869
930
  // Format numeric data based on settings in config OR from passed in settings for Additional Columns
@@ -1118,6 +1179,12 @@ const CdcChart: React.FC<CdcChartProps> = ({
1118
1179
  return tableConfig
1119
1180
  }
1120
1181
 
1182
+ // Transform and clean data for chart rendering
1183
+ const transformedData = getTransformedData({ brushData: state.brushData, filteredData, excludedData, clean })
1184
+
1185
+ // Filter annotations to only those visible in current data view
1186
+ const visibleAnnotations = getVisibleAnnotations(config.annotations, transformedData, config.xAxis?.dataKey)
1187
+
1121
1188
  // Prevent render if loading
1122
1189
  let body = <Loading />
1123
1190
 
@@ -1209,7 +1276,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
1209
1276
  />
1210
1277
  )}
1211
1278
  <SkipTo skipId={handleChartTabbing(config, legendId)} skipMessage='Skip Over Chart Container' />
1212
- {config.annotations?.length > 0 && (
1279
+ {visibleAnnotations.length > 0 && (
1213
1280
  <SkipTo
1214
1281
  skipId={handleChartTabbing(config, legendId)}
1215
1282
  skipMessage={`Skip over annotations`}
@@ -1239,7 +1306,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
1239
1306
  {/* All charts with LinearChart */}
1240
1307
  {filteredData &&
1241
1308
  filteredData.length > 0 &&
1242
- !['Spark Line', 'Line', 'Sankey', 'Pie', 'Sankey'].includes(config.visualizationType) && (
1309
+ !['Spark Line', 'Line', 'Sankey', 'Pie', 'Radar'].includes(config.visualizationType) && (
1243
1310
  <div ref={parentRef} style={{ width: `100%` }}>
1244
1311
  <ParentSize>
1245
1312
  {parent => (
@@ -1261,6 +1328,19 @@ const CdcChart: React.FC<CdcChartProps> = ({
1261
1328
  )}
1262
1329
  </ParentSize>
1263
1330
  )}
1331
+ {/* Radar Chart */}
1332
+ {filteredData && filteredData.length > 0 && config.visualizationType === 'Radar' && (
1333
+ <ParentSize className='justify-content-center d-flex' style={{ width: `100%` }}>
1334
+ {parent => (
1335
+ <RadarChart
1336
+ ref={svgRef}
1337
+ parentWidth={parent.width}
1338
+ parentHeight={parent.height}
1339
+ interactionLabel={interactionLabel}
1340
+ />
1341
+ )}
1342
+ </ParentSize>
1343
+ )}
1264
1344
  {/* Line Chart */}
1265
1345
  {filteredData &&
1266
1346
  filteredData.length > 0 &&
@@ -1382,7 +1462,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
1382
1462
  return (
1383
1463
  <DataTable
1384
1464
  /* changing the "key" will force the table to re-render
1385
- when the default sort changes while editing */
1465
+ when the default sort changes while editing */
1386
1466
  key={dataTableDefaultSortBy}
1387
1467
  config={dataTableConfig}
1388
1468
  rawData={dataTableRawData}
@@ -1432,7 +1512,7 @@ const CdcChart: React.FC<CdcChartProps> = ({
1432
1512
  </MediaControls.Section>
1433
1513
  </div>
1434
1514
  )}
1435
- {config?.annotations?.length > 0 && <Annotation.Dropdown />}
1515
+ {visibleAnnotations.length > 0 && <Annotation.Dropdown />}
1436
1516
  {/* show pdf or image button */}
1437
1517
  {processedLegacyFootnotes && (
1438
1518
  <section className='footnotes pt-2 mt-4'>{parse(processedLegacyFootnotes)}</section>
@@ -1505,10 +1585,11 @@ const CdcChart: React.FC<CdcChartProps> = ({
1505
1585
  setSharedFilterValue,
1506
1586
  svgRef,
1507
1587
  tableData: filteredData || excludedData,
1508
- transformedData: getTransformedData({ brushData: state.brushData, filteredData, excludedData, clean }),
1588
+ transformedData,
1509
1589
  twoColorPalette,
1510
1590
  unfilteredData: stateData,
1511
- updateConfig
1591
+ updateConfig,
1592
+ visibleAnnotations
1512
1593
  }
1513
1594
 
1514
1595
  return (
@@ -3,6 +3,7 @@ import { Meta, Story } from '@storybook/react-vite'
3
3
  import Chart from '../CdcChartComponent'
4
4
  import exampleComboBarNonNumeric from './../../examples/feature/tests-date-exclusions/date-exclusions-config.json'
5
5
  import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
6
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
6
7
 
7
8
  export default {
8
9
  title: 'Components/Templates/Chart/Anchors',
@@ -15,17 +16,26 @@ export const Anchor_DateLinear: Story = {
15
16
  args: {
16
17
  config: exampleComboBarNonNumeric,
17
18
  isEditor: false
19
+ },
20
+ play: async ({ canvasElement }) => {
21
+ await assertVisualizationRendered(canvasElement)
18
22
  }
19
23
  }
20
24
 
21
25
  export const Anchor_Categorical: Story = {
22
26
  args: {
23
27
  config: editConfigKeys(exampleComboBarNonNumeric, [{ path: ['xAxis', 'type'], value: 'categorical' }])
28
+ },
29
+ play: async ({ canvasElement }) => {
30
+ await assertVisualizationRendered(canvasElement)
24
31
  }
25
32
  }
26
33
 
27
34
  export const Anchor_Date_Time: Story = {
28
35
  args: {
29
36
  config: editConfigKeys(exampleComboBarNonNumeric, [{ path: ['xAxis', 'type'], value: 'date-time' }])
37
+ },
38
+ play: async ({ canvasElement }) => {
39
+ await assertVisualizationRendered(canvasElement)
30
40
  }
31
41
  }
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import boxPlotConfig from './_mock/boxplot_multiseries.json'
3
3
  import Chart from '../CdcChartComponent'
4
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
4
5
 
5
6
  const meta: Meta<typeof Chart> = {
6
7
  title: 'Components/Templates/Chart/Box Plot',
@@ -17,6 +18,9 @@ export const BoxPlot_Vertical: Story = {
17
18
  title: 'Vertical Multiseries Box Plot',
18
19
  isEditor: false
19
20
  }
21
+ },
22
+ play: async ({ canvasElement }) => {
23
+ await assertVisualizationRendered(canvasElement)
20
24
  }
21
25
  }
22
26
 
@@ -29,6 +33,9 @@ export const BoxPlot_Horizontal: Story = {
29
33
  yAxis: { ...boxPlotConfig.yAxis, labelPlacement: 'Above Bar' }
30
34
  },
31
35
  isEditor: false
36
+ },
37
+ play: async ({ canvasElement }) => {
38
+ await assertVisualizationRendered(canvasElement)
32
39
  }
33
40
  }
34
41
 
@@ -4,6 +4,7 @@ import lineChartDynamicCI from './_mock/line_chart_dynamic_ci.json'
4
4
  import lineChartNonDynamicCI from './_mock/line_chart_non_dynamic_ci.json'
5
5
 
6
6
  import Chart from '../CdcChartComponent'
7
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
7
8
 
8
9
  const meta: Meta<typeof Chart> = {
9
10
  title: 'Components/Templates/Chart/Confidence Intervals',
@@ -14,6 +15,9 @@ export const bar_chart_with_labels: Story = {
14
15
  args: {
15
16
  config: barChartCiLabels,
16
17
  isEditor: false
18
+ },
19
+ play: async ({ canvasElement }) => {
20
+ await assertVisualizationRendered(canvasElement)
17
21
  }
18
22
  }
19
23
  export const bar_chart_horizontal_labels: Story = {
@@ -24,6 +28,9 @@ export const bar_chart_horizontal_labels: Story = {
24
28
  yAxis: { ...barChartCiLabels.yAxis, displayNumbersOnBar: true }
25
29
  },
26
30
  isEditor: false
31
+ },
32
+ play: async ({ canvasElement }) => {
33
+ await assertVisualizationRendered(canvasElement)
27
34
  }
28
35
  }
29
36
 
@@ -31,6 +38,9 @@ export const line_Chart_Dynamic_Confidence_Intervals: Story = {
31
38
  args: {
32
39
  config: lineChartDynamicCI,
33
40
  isEditor: false
41
+ },
42
+ play: async ({ canvasElement }) => {
43
+ await assertVisualizationRendered(canvasElement)
34
44
  }
35
45
  }
36
46
 
@@ -38,6 +48,9 @@ export const line_Chart_Non_Dynamic_Confidence_Intervals: Story = {
38
48
  args: {
39
49
  config: lineChartNonDynamicCI,
40
50
  isEditor: false
51
+ },
52
+ play: async ({ canvasElement }) => {
53
+ await assertVisualizationRendered(canvasElement)
41
54
  }
42
55
  }
43
56
  export default meta
@@ -2,6 +2,8 @@
2
2
  import React from 'react'
3
3
  import { Meta, Story } from '@storybook/react'
4
4
  import CdcChart from '@cdc/chart/src/CdcChart'
5
+ import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
6
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
5
7
  import comboChartConfig from './_mock/combo.json'
6
8
 
7
9
  export default {
@@ -16,3 +18,18 @@ ComboChart.args = {
16
18
  config: comboChartConfig,
17
19
  isEditor: true
18
20
  }
21
+ ComboChart.play = async ({ canvasElement }) => {
22
+ await assertVisualizationRendered(canvasElement)
23
+ }
24
+
25
+ export const ComboChartWithBrush = Template.bind({})
26
+ ComboChartWithBrush.args = {
27
+ config: editConfigKeys(comboChartConfig, [
28
+ { path: ['xAxis', 'brushActive'], value: true },
29
+ { path: ['xAxis', 'brushDefaultRecentDateCount'], value: 12 }
30
+ ]),
31
+ isEditor: true
32
+ }
33
+ ComboChartWithBrush.play = async ({ canvasElement }) => {
34
+ await assertVisualizationRendered(canvasElement)
35
+ }
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import Chart from '../CdcChartComponent'
3
3
  import scatterPlotCustomColorConfig from './_mock/scatterplot_mock.json'
4
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
4
5
 
5
6
  const meta: Meta<typeof Chart> = {
6
7
  title: 'Components/Templates/Chart/Custom Colors',
@@ -13,6 +14,9 @@ export const ScatterPlot: Story = {
13
14
  args: {
14
15
  config: scatterPlotCustomColorConfig,
15
16
  isEditor: false
17
+ },
18
+ play: async ({ canvasElement }) => {
19
+ await assertVisualizationRendered(canvasElement)
16
20
  }
17
21
  }
18
22
 
@@ -3,6 +3,7 @@ import DynamicSeriesBarConfig from './_mock/dynamic_series_bar_config.json'
3
3
  import DynamicSeriesSuppression from './_mock/dynamic_series_suppression_mock.json'
4
4
  import { Meta, StoryObj } from '@storybook/react-vite'
5
5
  import Chart from '../CdcChartComponent'
6
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
6
7
 
7
8
  const meta: Meta<typeof Chart> = {
8
9
  title: 'Components/Templates/Chart/Dynamic Series',
@@ -23,6 +24,9 @@ export const LineShowPoints: Story = {
23
24
  lineDatapointStyle: 'always show'
24
25
  },
25
26
  isEditor: false
27
+ },
28
+ play: async ({ canvasElement }) => {
29
+ await assertVisualizationRendered(canvasElement)
26
30
  }
27
31
  }
28
32
 
@@ -30,6 +34,9 @@ export const LineSuppression: Story = {
30
34
  args: {
31
35
  config: DynamicSeriesSuppression,
32
36
  isEditor: false
37
+ },
38
+ play: async ({ canvasElement }) => {
39
+ await assertVisualizationRendered(canvasElement)
33
40
  }
34
41
  }
35
42
 
@@ -37,6 +44,9 @@ export const LineHoverPoints: Story = {
37
44
  args: {
38
45
  config: DynamicSeriesConfig,
39
46
  isEditor: false
47
+ },
48
+ play: async ({ canvasElement }) => {
49
+ await assertVisualizationRendered(canvasElement)
40
50
  }
41
51
  }
42
52
 
@@ -44,6 +54,9 @@ export const Bar_Vertical: Story = {
44
54
  args: {
45
55
  config: DynamicSeriesBarConfig,
46
56
  isEditor: true
57
+ },
58
+ play: async ({ canvasElement }) => {
59
+ await assertVisualizationRendered(canvasElement)
47
60
  }
48
61
  }
49
62
 
@@ -51,6 +64,9 @@ export const Bar_Horizontal: Story = {
51
64
  args: {
52
65
  config: { ...DynamicSeriesBarConfig, orientation: 'horizontal' },
53
66
  isEditor: false
67
+ },
68
+ play: async ({ canvasElement }) => {
69
+ await assertVisualizationRendered(canvasElement)
54
70
  }
55
71
  }
56
72
 
@@ -62,6 +78,9 @@ export const Legend_Order: Story = {
62
78
  legend: { ...DynamicSeriesBarConfig, order: 'desc' }
63
79
  },
64
80
  isEditor: false
81
+ },
82
+ play: async ({ canvasElement }) => {
83
+ await assertVisualizationRendered(canvasElement)
65
84
  }
66
85
  }
67
86
 
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import Chart from '../CdcChartComponent'
3
3
  import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
4
4
  import scatterPlotDownloadImage from './_mock/scatterplot-image-download.json'
5
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
5
6
 
6
7
  const meta: Meta<typeof Chart> = {
7
8
  title: 'Components/Templates/Chart/Filters',
@@ -13,6 +14,9 @@ type Story = StoryObj<typeof Chart>
13
14
  export const Tab_Simple: Story = {
14
15
  args: {
15
16
  config: editConfigKeys(scatterPlotDownloadImage, [{ path: ['filters', '0', 'filterStyle'], value: 'tab-simple' }])
17
+ },
18
+ play: async ({ canvasElement }) => {
19
+ await assertVisualizationRendered(canvasElement)
16
20
  }
17
21
  }
18
22
 
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
2
2
 
3
3
  import Chart from '../CdcChart'
4
4
  import forecastComboWithGaps from './_mock/forecast_combo_with_gaps.json'
5
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
5
6
 
6
7
  const meta: Meta<typeof Chart> = {
7
8
  title: 'Components/Templates/Chart/Forecast Chart',
@@ -30,6 +31,9 @@ export const Forecast_Combo_With_Gaps: Story = {
30
31
  'A combo chart with forecast confidence intervals that demonstrates proper gap handling. The forecast areas and lines are split into separate segments when gaps are detected, preventing misleading connections across missing data periods. Invalid data points (NA values) are automatically filtered out.'
31
32
  }
32
33
  }
34
+ },
35
+ play: async ({ canvasElement }) => {
36
+ await assertVisualizationRendered(canvasElement)
33
37
  }
34
38
  }
35
39