@cdc/chart 4.26.1 → 4.26.3

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 (173) hide show
  1. package/CLAUDE.local.md +79 -0
  2. package/LICENSE +201 -0
  3. package/dist/{cdcchart-dgT_1dIT.es.js → cdcchart-DQ00cQCm.es.js} +1 -20
  4. package/dist/cdcchart.js +54742 -49796
  5. package/examples/data/data-with-metadata.json +10 -0
  6. package/examples/default.json +378 -0
  7. package/examples/feature/__data__/horizon-chart-data.json +373 -0
  8. package/examples/feature/annotations/index.json +3 -6
  9. package/examples/feature/horizon/horizon-chart.json +395 -0
  10. package/examples/feature/pie/planet-pie-example-config.json +2 -1
  11. package/examples/line-chart-states.json +1085 -0
  12. package/examples/metadata-variables.json +58 -0
  13. package/examples/private/123.json +694 -0
  14. package/examples/private/anchor-issue.json +4094 -0
  15. package/examples/private/backwards-slider.json +10430 -0
  16. package/examples/private/georgia.csv +160 -0
  17. package/examples/private/timeline-data.json +1 -0
  18. package/examples/private/timeline.json +389 -0
  19. package/examples/radar-chart-simple.json +133 -0
  20. package/examples/radar-chart.json +148 -0
  21. package/index.html +1 -31
  22. package/package.json +57 -59
  23. package/src/CdcChart.tsx +8 -4
  24. package/src/CdcChartComponent.tsx +398 -284
  25. package/src/_stories/Chart.Anchors.stories.tsx +10 -0
  26. package/src/_stories/Chart.BoxPlot.stories.tsx +7 -0
  27. package/src/_stories/Chart.CI.stories.tsx +13 -0
  28. package/src/_stories/Chart.Combo.stories.tsx +17 -0
  29. package/src/_stories/Chart.CustomColors.stories.tsx +78 -0
  30. package/src/_stories/Chart.Defaults.stories.tsx +95 -0
  31. package/src/_stories/Chart.DynamicSeries.stories.tsx +19 -0
  32. package/src/_stories/Chart.Filters.stories.tsx +4 -0
  33. package/src/_stories/Chart.Forecast.stories.tsx +4 -0
  34. package/src/_stories/Chart.HTMLInDataTable.stories.tsx +22 -0
  35. package/src/_stories/Chart.Legend.Gradient.stories.tsx +28 -0
  36. package/src/_stories/Chart.Patterns.stories.tsx +4 -0
  37. package/src/_stories/Chart.PreserveDecimals.stories.tsx +25 -0
  38. package/src/_stories/Chart.Regions.Categorical.stories.tsx +13 -0
  39. package/src/_stories/Chart.Regions.DateScale.stories.tsx +19 -0
  40. package/src/_stories/Chart.Regions.DateTimeScale.stories.tsx +25 -10
  41. package/src/_stories/Chart.ScatterPlot.stories.tsx +4 -0
  42. package/src/_stories/Chart.SmallMultiples.stories.tsx +16 -0
  43. package/src/_stories/Chart.SmallestLeftAxisMax.stories.tsx +64 -0
  44. package/src/_stories/Chart.stories.tsx +72 -1
  45. package/src/_stories/Chart.tooltip.stories.tsx +7 -0
  46. package/src/_stories/ChartAnnotation.stories.tsx +10 -0
  47. package/src/_stories/ChartAxisLabels.stories.tsx +4 -0
  48. package/src/_stories/ChartAxisTitles.stories.tsx +10 -0
  49. package/src/_stories/ChartBar.Editor.stories.tsx +97 -38
  50. package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
  51. package/src/_stories/ChartBrush.Matrix.Continuous.stories.tsx +41 -0
  52. package/src/_stories/ChartBrush.Matrix.Date.stories.tsx +114 -0
  53. package/src/_stories/ChartBrush.Matrix.DateTime.stories.tsx +78 -0
  54. package/src/_stories/ChartBrush.stories.tsx +7 -0
  55. package/src/_stories/ChartEditor.Editor.stories.tsx +1 -1
  56. package/src/_stories/ChartEditor.stories.tsx +7 -0
  57. package/src/_stories/ChartLine.QuadrantAngles.stories.tsx +89 -0
  58. package/src/_stories/ChartLine.Suppression.stories.tsx +7 -0
  59. package/src/_stories/ChartLine.Symbols.stories.tsx +4 -0
  60. package/src/_stories/ChartPrefixSuffix.stories.tsx +46 -1
  61. package/src/_stories/TechAdoptionWithLinks.stories.tsx +7 -0
  62. package/src/_stories/_mock/brush_continuous.json +86 -0
  63. package/src/_stories/_mock/brush_date_large.json +176 -0
  64. package/src/_stories/_mock/line_chart_angle_near_zero_fall.json +195 -0
  65. package/src/_stories/_mock/line_chart_angle_near_zero_rise.json +195 -0
  66. package/src/_stories/_mock/line_chart_angle_q1_steep_upward.json +195 -0
  67. package/src/_stories/_mock/line_chart_angle_q2_gentle_downward.json +195 -0
  68. package/src/_stories/_mock/line_chart_angle_q3_steep_downward.json +195 -0
  69. package/src/_stories/_mock/line_chart_angle_q4_gentle_upward.json +195 -0
  70. package/src/_stories/_mock/line_chart_quadrant_angles.json +264 -0
  71. package/src/_stories/_mock/paired-bar-abbr.json +421 -0
  72. package/src/_stories/_mock/pie_custom_colors.json +268 -0
  73. package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
  74. package/src/components/Annotations/components/AnnotationDraggable.styles.css +14 -20
  75. package/src/components/Annotations/components/AnnotationDraggable.tsx +240 -116
  76. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -2
  77. package/src/components/Annotations/components/AnnotationDropdown.tsx +8 -12
  78. package/src/components/Annotations/components/AnnotationList.styles.css +12 -18
  79. package/src/components/Annotations/components/AnnotationList.tsx +5 -4
  80. package/src/components/Annotations/components/findNearestDatum.ts +75 -85
  81. package/src/components/Annotations/helpers/getVisibleAnnotations.ts +38 -0
  82. package/src/components/Axis/BottomAxis.tsx +277 -0
  83. package/src/components/Axis/LeftAxis.tsx +404 -0
  84. package/src/components/Axis/LeftAxisGridlines.tsx +77 -0
  85. package/src/components/Axis/PairedBarAxis.tsx +192 -0
  86. package/src/components/Axis/README.md +94 -0
  87. package/src/components/Axis/RightAxis.tsx +108 -0
  88. package/src/components/Axis/axis.constants.ts +21 -0
  89. package/src/components/Axis/index.ts +7 -0
  90. package/src/components/BarChart/components/BarChart.Horizontal.tsx +12 -28
  91. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +12 -30
  92. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +12 -31
  93. package/src/components/BarChart/components/BarChart.Vertical.tsx +12 -28
  94. package/src/components/BarChart/components/BarChart.tsx +7 -1
  95. package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
  96. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
  97. package/src/components/BarChart/helpers/useBarChart.ts +3 -0
  98. package/src/components/Brush/BrushSelector.tsx +155 -22
  99. package/src/components/Brush/MiniChartPreview.tsx +133 -21
  100. package/src/components/EditorPanel/EditorPanel.tsx +81 -54
  101. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +67 -29
  102. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
  103. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +120 -2
  104. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +25 -43
  105. package/src/components/EditorPanel/components/Panels/Panel.Radar.tsx +353 -0
  106. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +83 -3
  107. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +66 -43
  108. package/src/components/EditorPanel/components/Panels/index.tsx +2 -0
  109. package/src/components/EditorPanel/editor-panel.scss +1 -1
  110. package/src/components/EditorPanel/useEditorPermissions.ts +55 -26
  111. package/src/components/ForestPlot/ForestPlot.tsx +26 -22
  112. package/src/components/HorizonChart/HorizonChart.tsx +131 -0
  113. package/src/components/HorizonChart/components/HorizonBand.tsx +160 -0
  114. package/src/components/HorizonChart/helpers/calculateHorizonBands.ts +27 -0
  115. package/src/components/HorizonChart/helpers/getHorizonLayerColors.ts +40 -0
  116. package/src/components/HorizonChart/index.tsx +3 -0
  117. package/src/components/Legend/Legend.Component.tsx +52 -4
  118. package/src/components/Legend/Legend.tsx +1 -1
  119. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +4 -4
  120. package/src/components/Legend/LegendValueRange.tsx +77 -0
  121. package/src/components/Legend/helpers/createFormatLabels.tsx +16 -2
  122. package/src/components/Legend/helpers/generateValueRanges.ts +92 -0
  123. package/src/components/LineChart/helpers/README.md +292 -0
  124. package/src/components/LineChart/helpers/labelPositioning.test.ts +245 -0
  125. package/src/components/LineChart/helpers/labelPositioning.ts +304 -0
  126. package/src/components/LineChart/index.tsx +44 -8
  127. package/src/components/LinearChart/README.md +109 -0
  128. package/src/components/LinearChart/VisualizationRenderer.tsx +267 -0
  129. package/src/components/LinearChart/linearChart.constants.ts +84 -0
  130. package/src/components/LinearChart/tests/LinearChart.test.tsx +278 -0
  131. package/src/components/LinearChart/tests/mockConfigContext.ts +131 -0
  132. package/src/components/LinearChart/utils/tickFormatting.ts +146 -0
  133. package/src/components/LinearChart.tsx +268 -1057
  134. package/src/components/PieChart/PieChart.tsx +20 -5
  135. package/src/components/RadarChart/RadarAxis.tsx +78 -0
  136. package/src/components/RadarChart/RadarChart.tsx +298 -0
  137. package/src/components/RadarChart/RadarGrid.tsx +64 -0
  138. package/src/components/RadarChart/RadarPolygon.tsx +91 -0
  139. package/src/components/RadarChart/helpers.ts +83 -0
  140. package/src/components/RadarChart/index.tsx +3 -0
  141. package/src/components/Regions/components/Regions.tsx +6 -6
  142. package/src/components/Sankey/components/Sankey.tsx +3 -3
  143. package/src/components/Sankey/sankey.scss +1 -1
  144. package/src/components/SmallMultiples/SmallMultiples.css +5 -5
  145. package/src/components/Sparkline/index.scss +4 -2
  146. package/src/components/WarmingStripes/WarmingStripes.tsx +95 -25
  147. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
  148. package/src/data/initial-state.js +37 -15
  149. package/src/data/legacy-defaults.ts +18 -0
  150. package/src/helpers/abbreviateNumber.ts +24 -17
  151. package/src/helpers/getChartPatternId.ts +17 -0
  152. package/src/helpers/getExcludedData.ts +4 -0
  153. package/src/helpers/getMinMax.ts +16 -2
  154. package/src/helpers/handleChartAriaLabels.ts +19 -19
  155. package/src/helpers/handleLineType.ts +22 -18
  156. package/src/helpers/seriesColumnSettings.ts +114 -0
  157. package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
  158. package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
  159. package/src/hooks/useProgrammaticTooltip.ts +23 -2
  160. package/src/hooks/useRightAxis.ts +14 -0
  161. package/src/hooks/useScales.ts +99 -56
  162. package/src/hooks/useTooltip.tsx +23 -3
  163. package/src/scss/main.scss +157 -79
  164. package/src/selectors/README.md +68 -0
  165. package/src/store/chart.reducer.ts +2 -0
  166. package/src/test/CdcChart.test.jsx +2 -2
  167. package/src/types/ChartConfig.ts +22 -0
  168. package/src/types/ChartContext.ts +1 -0
  169. package/src/types/Horizon.ts +64 -0
  170. package/tests/fixtures/chart-config-with-metadata.json +29 -0
  171. package/tests/fixtures/data-with-metadata.json +10 -0
  172. package/preview.html +0 -1616
  173. package/src/components/Annotations/components/helpers/index.tsx +0 -46
@@ -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,9 @@
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 pieCustomColorConfig from './_mock/pie_custom_colors.json'
5
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
6
+ import { expect, userEvent, waitFor } from 'storybook/test'
4
7
 
5
8
  const meta: Meta<typeof Chart> = {
6
9
  title: 'Components/Templates/Chart/Custom Colors',
@@ -13,6 +16,81 @@ export const ScatterPlot: Story = {
13
16
  args: {
14
17
  config: scatterPlotCustomColorConfig,
15
18
  isEditor: false
19
+ },
20
+ play: async ({ canvasElement }) => {
21
+ await assertVisualizationRendered(canvasElement)
22
+ }
23
+ }
24
+
25
+ const pieCustomColors = ['#0B6E4F', '#C75000', '#7C4DFF', '#0077B6', '#D7263D']
26
+ const pieFallbackCustomColors = ['#004E89', '#1A659E', '#47A8BD', '#D1495B', '#EDAE49']
27
+
28
+ const normalizeColor = (color: string) => {
29
+ const swatch = document.createElement('div')
30
+ swatch.style.color = color
31
+ document.body.appendChild(swatch)
32
+ const normalized = getComputedStyle(swatch).color
33
+ swatch.remove()
34
+ return normalized
35
+ }
36
+
37
+ const assertPieCustomColorStory = async (canvasElement: HTMLElement, expectedColors: string[]) => {
38
+ await assertVisualizationRendered(canvasElement)
39
+
40
+ const chart = canvasElement.querySelector('svg.animated-pie')
41
+ const legend = canvasElement.querySelector('.legend-container')
42
+ expect(chart).toBeTruthy()
43
+ expect(legend).toBeTruthy()
44
+
45
+ await waitFor(() => {
46
+ expect(chart?.querySelectorAll('path')).toHaveLength(expectedColors.length)
47
+ })
48
+
49
+ const legendItems = Array.from(legend!.querySelectorAll('[role="button"]')) as HTMLElement[]
50
+ expect(legendItems).toHaveLength(expectedColors.length)
51
+
52
+ const sliceColors = Array.from(chart!.querySelectorAll('path')).map(path =>
53
+ normalizeColor(path.getAttribute('fill') || '')
54
+ )
55
+ expect(sliceColors).toEqual(expectedColors.map(normalizeColor))
56
+
57
+ const legendColors = legendItems.map(item => {
58
+ const swatch = item.querySelector('span.legend-item') as HTMLElement | null
59
+ return normalizeColor(swatch ? getComputedStyle(swatch).backgroundColor : '')
60
+ })
61
+ expect(legendColors).toEqual(expectedColors.map(normalizeColor))
62
+
63
+ await userEvent.click(legendItems[0])
64
+
65
+ await waitFor(() => {
66
+ expect(chart?.querySelectorAll('path')).toHaveLength(1)
67
+ expect(legendItems[0].className).toContain('highlighted')
68
+ expect(legendItems[1].className).toContain('inactive')
69
+ })
70
+ }
71
+
72
+ export const PieChart: Story = {
73
+ args: {
74
+ config: pieCustomColorConfig,
75
+ isEditor: false
76
+ },
77
+ play: async ({ canvasElement }) => {
78
+ await assertPieCustomColorStory(canvasElement, pieCustomColors)
79
+ }
80
+ }
81
+
82
+ const pieChartCustomColorsFallbackConfig = JSON.parse(JSON.stringify(pieCustomColorConfig))
83
+ pieChartCustomColorsFallbackConfig.title = 'Pie Chart Custom Colors Fallback'
84
+ pieChartCustomColorsFallbackConfig.general.palette.customColors = pieFallbackCustomColors
85
+ delete pieChartCustomColorsFallbackConfig.general.palette.customColorsOrdered
86
+
87
+ export const PieChartCustomColorsFallback: Story = {
88
+ args: {
89
+ config: pieChartCustomColorsFallbackConfig,
90
+ isEditor: false
91
+ },
92
+ play: async ({ canvasElement }) => {
93
+ await assertPieCustomColorStory(canvasElement, pieFallbackCustomColors)
16
94
  }
17
95
  }
18
96
 
@@ -0,0 +1,95 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { expect } from 'storybook/test'
3
+ import Chart from '../CdcChart'
4
+ import simplifiedLine from './_mock/simplified_line.json'
5
+ import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
6
+ import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
7
+
8
+ const meta: Meta<typeof Chart> = {
9
+ title: 'Components/Templates/Chart/Defaults',
10
+ component: Chart
11
+ }
12
+
13
+ type Story = StoryObj<typeof Chart>
14
+
15
+ const oldConfig = editConfigKeys(simplifiedLine, [
16
+ { path: ['yAxis', 'hideAxis'], value: false },
17
+ { path: ['yAxis', 'hideTicks'], value: false },
18
+ { path: ['yAxis', 'gridLines'], value: false },
19
+ { path: ['yAxis', 'numTicks'], value: '' },
20
+ { path: ['xAxis', 'numTicks'], value: '' },
21
+ { path: ['table', 'expanded'], value: true },
22
+ { path: ['legend', 'position'], value: 'right' },
23
+ { path: ['dataFormat', 'commas'], value: false },
24
+ { path: ['tooltips', 'dateDisplayFormat'], value: '' }
25
+ ])
26
+
27
+ const newConfig = editConfigKeys(simplifiedLine, [
28
+ { path: ['yAxis', 'hideAxis'], value: true },
29
+ { path: ['yAxis', 'hideTicks'], value: true },
30
+ { path: ['yAxis', 'gridLines'], value: true },
31
+ { path: ['yAxis', 'numTicks'], value: 4 },
32
+ { path: ['xAxis', 'numTicks'], value: 6 },
33
+ { path: ['table', 'expanded'], value: false },
34
+ { path: ['legend', 'position'], value: 'top' },
35
+ { path: ['dataFormat', 'commas'], value: true },
36
+ { path: ['tooltips', 'dateDisplayFormat'], value: '%B %-d, %Y' }
37
+ ])
38
+
39
+ export const OldConfig_Preserves_Legacy_Defaults: Story = {
40
+ args: {
41
+ config: oldConfig,
42
+ isEditor: false
43
+ },
44
+ play: async ({ canvasElement }) => {
45
+ await assertVisualizationRendered(canvasElement)
46
+
47
+ await waitForPresence('.legend-container', canvasElement)
48
+ const legend = canvasElement.querySelector('.legend-container')
49
+ expect(legend).toBeInTheDocument()
50
+ expect(legend?.classList.contains('right')).toBe(true)
51
+
52
+ const axisLine = canvasElement.querySelector('.left-axis line[stroke="#000"]')
53
+ expect(axisLine).toBeInTheDocument()
54
+
55
+ const gridlines = canvasElement.querySelectorAll('.left-axis line[stroke="#d6d6d6"]')
56
+ expect(gridlines.length).toBe(0)
57
+
58
+ const dataTable = canvasElement.querySelector('.data-table')
59
+ expect(dataTable).toBeInTheDocument()
60
+ expect(dataTable?.getAttribute('hidden')).toBeNull()
61
+
62
+ const tickTexts = canvasElement.querySelectorAll('.left-axis .vx-axis-tick text')
63
+ for (const tick of tickTexts) {
64
+ expect(tick.textContent).not.toMatch(/,/)
65
+ }
66
+ }
67
+ }
68
+
69
+ export const NewConfig_Gets_New_Defaults: Story = {
70
+ args: {
71
+ config: newConfig,
72
+ isEditor: false
73
+ },
74
+ play: async ({ canvasElement }) => {
75
+ await assertVisualizationRendered(canvasElement)
76
+
77
+ await waitForPresence('.legend-container', canvasElement)
78
+ const legend = canvasElement.querySelector('.legend-container')
79
+ expect(legend).toBeInTheDocument()
80
+ expect(legend?.classList.contains('top')).toBe(true)
81
+
82
+ const axisLine = canvasElement.querySelector('.left-axis line[stroke="#000"]')
83
+ expect(axisLine).toBeNull()
84
+
85
+ await waitForPresence('.left-axis line[stroke="#d6d6d6"]', canvasElement)
86
+ const gridlines = canvasElement.querySelectorAll('.left-axis line[stroke="#d6d6d6"]')
87
+ expect(gridlines.length).toBeGreaterThan(0)
88
+
89
+ const dataTable = canvasElement.querySelector('.data-table')
90
+ expect(dataTable).toBeInTheDocument()
91
+ expect(dataTable?.getAttribute('hidden')).not.toBeNull()
92
+ }
93
+ }
94
+
95
+ export default meta
@@ -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
 
@@ -1,5 +1,6 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-vite'
2
2
  import CdcChart from '../CdcChartComponent'
3
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
3
4
 
4
5
  const meta: Meta<typeof CdcChart> = {
5
6
  title: 'Components/Templates/Chart/HTML in Data Table',
@@ -148,6 +149,9 @@ export const BasicHTMLInFootnoteColumn: Story = {
148
149
  }
149
150
  },
150
151
  isEditor: true
152
+ },
153
+ play: async ({ canvasElement }) => {
154
+ await assertVisualizationRendered(canvasElement)
151
155
  }
152
156
  }
153
157
 
@@ -208,6 +212,9 @@ export const HTMLWithLinksAndFormatting: Story = {
208
212
  }
209
213
  },
210
214
  isEditor: true
215
+ },
216
+ play: async ({ canvasElement }) => {
217
+ await assertVisualizationRendered(canvasElement)
211
218
  }
212
219
  }
213
220
 
@@ -265,6 +272,9 @@ export const HTMLWithLineBreaks: Story = {
265
272
  }
266
273
  },
267
274
  isEditor: true
275
+ },
276
+ play: async ({ canvasElement }) => {
277
+ await assertVisualizationRendered(canvasElement)
268
278
  }
269
279
  }
270
280
 
@@ -315,6 +325,9 @@ export const ComplexHTMLWithLists: Story = {
315
325
  }
316
326
  },
317
327
  isEditor: true
328
+ },
329
+ play: async ({ canvasElement }) => {
330
+ await assertVisualizationRendered(canvasElement)
318
331
  }
319
332
  }
320
333
 
@@ -368,6 +381,9 @@ export const HTMLWithInlineStyles: Story = {
368
381
  }
369
382
  },
370
383
  isEditor: true
384
+ },
385
+ play: async ({ canvasElement }) => {
386
+ await assertVisualizationRendered(canvasElement)
371
387
  }
372
388
  }
373
389
 
@@ -444,6 +460,9 @@ export const LineChartWithHTMLFootnotes: Story = {
444
460
  }
445
461
  },
446
462
  isEditor: true
463
+ },
464
+ play: async ({ canvasElement }) => {
465
+ await assertVisualizationRendered(canvasElement)
447
466
  }
448
467
  }
449
468
 
@@ -516,5 +535,8 @@ export const PieChartWithHTMLFootnotes: Story = {
516
535
  }
517
536
  },
518
537
  isEditor: true
538
+ },
539
+ play: async ({ canvasElement }) => {
540
+ await assertVisualizationRendered(canvasElement)
519
541
  }
520
542
  }
@@ -5,6 +5,7 @@ import SimplifiedLineConfig from './_mock/simplified_line.json'
5
5
 
6
6
  import Chart from '../CdcChartComponent'
7
7
  import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
8
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
8
9
 
9
10
  const meta: Meta<typeof Chart> = {
10
11
  title: 'Components/Templates/Chart/Legend',
@@ -16,41 +17,62 @@ type Story = StoryObj<typeof Chart>
16
17
  export const Legend_Gradient_Smooth: Story = {
17
18
  args: {
18
19
  config: chartGradientConfig
20
+ },
21
+ play: async ({ canvasElement }) => {
22
+ await assertVisualizationRendered(canvasElement)
19
23
  }
20
24
  }
21
25
  export const Legend_Group_By: Story = {
22
26
  args: {
23
27
  config: chartGroupedLagend
28
+ },
29
+ play: async ({ canvasElement }) => {
30
+ await assertVisualizationRendered(canvasElement)
24
31
  }
25
32
  }
26
33
 
27
34
  export const Legend_Gradient_Linear_Blocks: Story = {
28
35
  args: {
29
36
  config: editConfigKeys(chartGradientConfig, [{ path: ['legend', 'subStyle'], value: 'linear blocks' }])
37
+ },
38
+ play: async ({ canvasElement }) => {
39
+ await assertVisualizationRendered(canvasElement)
30
40
  }
31
41
  }
32
42
 
33
43
  export const Labels_On_Line_Legend_On_Top: Story = {
34
44
  args: {
35
45
  config: editConfigKeys(chartGradientConfig, [{ path: ['yAxis', 'labelsAboveGridlines'], value: true }])
46
+ },
47
+ play: async ({ canvasElement }) => {
48
+ await assertVisualizationRendered(canvasElement)
36
49
  }
37
50
  }
38
51
 
39
52
  export const Legend_On_Right: Story = {
40
53
  args: {
41
54
  config: SimplifiedLineConfig
55
+ },
56
+ play: async ({ canvasElement }) => {
57
+ await assertVisualizationRendered(canvasElement)
42
58
  }
43
59
  }
44
60
 
45
61
  export const Legend_On_Right_With_Box: Story = {
46
62
  args: {
47
63
  config: editConfigKeys(SimplifiedLineConfig, [{ path: ['legend', 'hideBorder'], value: false }])
64
+ },
65
+ play: async ({ canvasElement }) => {
66
+ await assertVisualizationRendered(canvasElement)
48
67
  }
49
68
  }
50
69
 
51
70
  export const Legend_Gradient_With_box: Story = {
52
71
  args: {
53
72
  config: editConfigKeys(chartGradientConfig, [{ path: ['legend', 'hideBorder'], value: false }])
73
+ },
74
+ play: async ({ canvasElement }) => {
75
+ await assertVisualizationRendered(canvasElement)
54
76
  }
55
77
  }
56
78
 
@@ -61,6 +83,9 @@ export const Legend_Gradient_With_Text: Story = {
61
83
  { path: ['legend', 'description'], value: 'Description' },
62
84
  { path: ['legend', 'hideBorder'], value: false }
63
85
  ])
86
+ },
87
+ play: async ({ canvasElement }) => {
88
+ await assertVisualizationRendered(canvasElement)
64
89
  }
65
90
  }
66
91
  export const Legend_Gradient_Wrapping_Label: Story = {
@@ -74,6 +99,9 @@ export const Legend_Gradient_Wrapping_Label: Story = {
74
99
  ])
75
100
  ).replace(/Data 1/g, 'This is a long string that should wrap')
76
101
  )
102
+ },
103
+ play: async ({ canvasElement }) => {
104
+ await assertVisualizationRendered(canvasElement)
77
105
  }
78
106
  }
79
107
 
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'
2
2
  import StackedPattern from './_mock/stacked-pattern-test.json'
3
3
 
4
4
  import Chart from '../CdcChartComponent'
5
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
5
6
 
6
7
  const meta: Meta<typeof Chart> = {
7
8
  title: 'Components/Templates/Chart/Patterns',
@@ -14,6 +15,9 @@ export const Stacked_Bar_Pattern: Story = {
14
15
  args: {
15
16
  config: StackedPattern,
16
17
  isEditor: true
18
+ },
19
+ play: async ({ canvasElement }) => {
20
+ await assertVisualizationRendered(canvasElement)
17
21
  }
18
22
  }
19
23