@cdc/chart 4.26.2 → 4.26.4

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 (165) hide show
  1. package/CONFIG.md +169 -0
  2. package/README.md +42 -17
  3. package/dist/cdcchart.js +39419 -35180
  4. package/examples/__data__/data-with-metadata.json +10 -0
  5. package/examples/feature/bar/additional-column-tooltip.json +3 -3
  6. package/examples/feature/deviation/planet-deviation-config.json +2 -2
  7. package/examples/feature/paired-bar/paired-bar-example.json +2 -2
  8. package/examples/feature/pie/planet-pie-example-config.json +2 -1
  9. package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
  10. package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
  11. package/examples/feature/tests-covid/covid-example-config.json +2 -2
  12. package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
  13. package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
  14. package/examples/metadata-variables.json +58 -0
  15. package/examples/minimal-example.json +24 -0
  16. package/examples/private/test2.json +35012 -0
  17. package/package.json +4 -4
  18. package/src/CdcChart.tsx +8 -4
  19. package/src/CdcChartComponent.tsx +326 -290
  20. package/src/_stories/Chart.CustomColors.smoke.stories.tsx +97 -0
  21. package/src/_stories/Chart.Defaults.smoke.stories.tsx +95 -0
  22. package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
  23. package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
  24. package/src/_stories/{Chart.SmallMultiples.stories.tsx → Chart.SmallMultiples.smoke.stories.tsx} +17 -1
  25. package/src/_stories/Chart.SmallestLeftAxisMax.smoke.stories.tsx +64 -0
  26. package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +48 -2
  27. package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
  28. package/src/_stories/ChartBar.Editor.stories.tsx +177 -115
  29. package/src/_stories/ChartBrush.Editor.stories.tsx +11 -25
  30. package/src/_stories/ChartEditor.Editor.stories.tsx +24 -1
  31. package/src/_stories/{Chart.CustomColors.stories.tsx → ChartLine.DashedDates.smoke.stories.tsx} +4 -4
  32. package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
  33. package/src/_stories/_mock/paired-bar-abbr.json +421 -0
  34. package/src/_stories/_mock/paired-bar.json +1 -1
  35. package/src/_stories/_mock/pie_custom_colors.json +268 -0
  36. package/src/_stories/_mock/smallest_left_axis_max.json +104 -0
  37. package/src/components/Annotations/components/AnnotationDraggable.styles.css +10 -10
  38. package/src/components/Annotations/components/AnnotationDraggable.tsx +1 -1
  39. package/src/components/Annotations/components/AnnotationDropdown.styles.css +1 -1
  40. package/src/components/Annotations/components/AnnotationList.styles.css +11 -11
  41. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
  42. package/src/components/Axis/BottomAxis.tsx +10 -3
  43. package/src/components/Axis/LeftAxis.tsx +2 -4
  44. package/src/components/Axis/LeftAxisGridlines.tsx +1 -3
  45. package/src/components/Axis/PairedBarAxis.tsx +10 -4
  46. package/src/components/BarChart/components/BarChart.Horizontal.tsx +21 -33
  47. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +21 -35
  48. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +21 -36
  49. package/src/components/BarChart/components/BarChart.Vertical.tsx +21 -34
  50. package/src/components/BarChart/components/BarChart.tsx +2 -6
  51. package/src/components/BarChart/helpers/getPatternUrl.ts +94 -0
  52. package/src/components/BarChart/helpers/tests/getPatternUrl.test.ts +134 -0
  53. package/src/components/BarChart/helpers/useBarChart.ts +4 -1
  54. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
  55. package/src/components/Brush/BrushSelector.tsx +2 -1
  56. package/src/components/Brush/MiniChartPreview.tsx +21 -26
  57. package/src/components/EditorPanel/EditorPanel.tsx +692 -954
  58. package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
  59. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +218 -167
  60. package/src/components/EditorPanel/components/Panels/Panel.ForestPlotSettings.tsx +0 -78
  61. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +39 -1
  62. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -135
  63. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +154 -109
  64. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
  65. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +95 -28
  66. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +45 -42
  67. package/src/components/EditorPanel/editor-panel.scss +1 -1
  68. package/src/components/EditorPanel/useEditorPermissions.ts +1 -10
  69. package/src/components/Forecasting/Forecasting.tsx +2 -2
  70. package/src/components/ForestPlot/ForestPlot.tsx +26 -22
  71. package/src/components/HorizonChart/HorizonChart.tsx +10 -2
  72. package/src/components/HoverLine/HoverLine.tsx +6 -8
  73. package/src/components/Legend/Legend.Component.tsx +4 -4
  74. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +9 -4
  75. package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
  76. package/src/components/Legend/helpers/createFormatLabels.tsx +3 -2
  77. package/src/components/Legend/helpers/index.ts +0 -2
  78. package/src/components/LineChart/LineChartProps.ts +1 -0
  79. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +3 -2
  80. package/src/components/LineChart/index.tsx +11 -4
  81. package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
  82. package/src/components/LinearChart/tests/LinearChart.test.tsx +224 -0
  83. package/src/components/LinearChart/tests/mockConfigContext.ts +2 -0
  84. package/src/components/LinearChart.tsx +124 -47
  85. package/src/components/PairedBarChart.jsx +2 -2
  86. package/src/components/PieChart/PieChart.tsx +30 -13
  87. package/src/components/RadarChart/RadarChart.tsx +1 -1
  88. package/src/components/Regions/components/Regions.tsx +36 -26
  89. package/src/components/Sankey/components/Sankey.tsx +3 -3
  90. package/src/components/Sankey/sankey.scss +1 -1
  91. package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
  92. package/src/components/SmallMultiples/SmallMultipleTile.tsx +1 -0
  93. package/src/components/SmallMultiples/SmallMultiples.css +5 -5
  94. package/src/components/Sparkline/components/SparkLine.tsx +1 -1
  95. package/src/components/Sparkline/index.scss +4 -2
  96. package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
  97. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +8 -8
  98. package/src/data/initial-state.js +24 -14
  99. package/src/data/legacy-defaults.ts +18 -0
  100. package/src/helpers/abbreviateNumber.ts +24 -17
  101. package/src/helpers/calculateLeftYAxisWidth.ts +193 -0
  102. package/src/helpers/getChartPatternId.ts +17 -0
  103. package/src/helpers/getMinMax.ts +16 -2
  104. package/src/helpers/seriesColumnSettings.ts +114 -0
  105. package/src/helpers/tests/countNumOfTicks.test.ts +77 -0
  106. package/src/helpers/tests/seriesColumnSettings.test.ts +84 -0
  107. package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
  108. package/src/helpers/tooltipHelpers.ts +176 -0
  109. package/src/hooks/useHighlightedBars.ts +9 -16
  110. package/src/hooks/useProgrammaticTooltip.ts +16 -2
  111. package/src/hooks/useRightAxis.ts +14 -0
  112. package/src/hooks/useScales.ts +92 -56
  113. package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
  114. package/src/hooks/useTooltip.tsx +186 -40
  115. package/src/scss/main.scss +152 -79
  116. package/src/test/CdcChart.test.jsx +26 -2
  117. package/src/types/ChartConfig.ts +5 -0
  118. package/tests/fixtures/chart-config-with-metadata.json +29 -0
  119. package/tests/fixtures/data-with-metadata.json +10 -0
  120. package/examples/chart-regression-2.json +0 -2360
  121. package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
  122. package/examples/feature/__data__/city-temperature.json +0 -2198
  123. package/examples/feature/bar/Barchart_with_negative.json +0 -34
  124. package/examples/feature/boxplot/box-plot-data.json +0 -71
  125. package/examples/feature/boxplot/boxplot-data.json +0 -156
  126. package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
  127. package/examples/feature/forest-plot/log.json +0 -26
  128. package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
  129. package/examples/region-issue.json +0 -2065
  130. package/examples/xaxis.json +0 -493
  131. /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
  132. /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
  133. /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
  134. /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
  135. /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
  136. /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
  137. /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
  138. /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
  139. /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
  140. /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
  141. /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
  142. /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
  143. /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
  144. /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
  145. /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
  146. /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
  147. /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
  148. /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
  149. /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
  150. /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
  151. /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
  152. /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
  153. /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
  154. /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
  155. /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
  156. /package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +0 -0
  157. /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
  158. /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
  159. /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
  160. /package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +0 -0
  161. /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
  162. /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
  163. /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
  164. /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
  165. /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
@@ -0,0 +1,97 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import Chart from '../CdcChartComponent'
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'
7
+
8
+ const meta: Meta<typeof Chart> = {
9
+ title: 'Components/Templates/Chart/Custom Colors',
10
+ component: Chart
11
+ }
12
+
13
+ type Story = StoryObj<typeof Chart>
14
+
15
+ export const ScatterPlot: Story = {
16
+ args: {
17
+ config: scatterPlotCustomColorConfig,
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)
94
+ }
95
+ }
96
+
97
+ export default meta
@@ -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
@@ -493,6 +493,16 @@ export const PieChartWithHTMLFootnotes: Story = {
493
493
  Footnote: '<strong>7 states</strong> with 60-79% coverage<br>Target: Move to <em>high category</em>'
494
494
  }
495
495
  ],
496
+ xAxis: {
497
+ label: 'Coverage Category',
498
+ dataKey: 'Category',
499
+ type: 'categorical'
500
+ },
501
+ yAxis: {
502
+ label: 'Percentage',
503
+ dataKey: 'Percentage',
504
+ type: 'linear'
505
+ },
496
506
  series: [
497
507
  {
498
508
  dataKey: 'Percentage',
@@ -0,0 +1,25 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import Chart from '../CdcChart'
3
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
4
+ import MinimalExampleConfig from '../../examples/minimal-example.json'
5
+ import { expect } from 'storybook/test'
6
+
7
+ const meta: Meta<typeof Chart> = {
8
+ title: 'Components/Templates/Chart/Minimal Config',
9
+ component: Chart
10
+ }
11
+
12
+ export default meta
13
+ type Story = StoryObj<typeof Chart>
14
+
15
+ export const Minimal_Bar_Chart: Story = {
16
+ args: {
17
+ config: MinimalExampleConfig,
18
+ isEditor: false
19
+ },
20
+ play: async ({ canvasElement }) => {
21
+ await assertVisualizationRendered(canvasElement)
22
+ expect(canvasElement.textContent).toContain('Minimal Chart')
23
+ expect(canvasElement.textContent).toContain('A')
24
+ }
25
+ }
@@ -5,7 +5,8 @@ import smallMultiplesBigDataBars from './_mock/small_multiples/small_multiples_b
5
5
  import smallMultiplesLinesColors from './_mock/small_multiples/small_multiples_lines_colors.json'
6
6
  import smallMultiplesLines from './_mock/small_multiples/small_multiples_lines.json'
7
7
  import smallMultiplesStackedBars from './_mock/small_multiples/small_multiples_stacked_bars.json'
8
- import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
8
+ import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
9
+ import { expect } from 'storybook/test'
9
10
 
10
11
  const meta: Meta<typeof Chart> = {
11
12
  title: 'Components/Templates/Chart/Small Multiples',
@@ -14,6 +15,11 @@ const meta: Meta<typeof Chart> = {
14
15
 
15
16
  type Story = StoryObj<typeof Chart>
16
17
 
18
+ const getTooltipHtmlValues = (canvasElement: HTMLElement) =>
19
+ Array.from(canvasElement.querySelectorAll('[data-tooltip-html]')).map(
20
+ element => element.getAttribute('data-tooltip-html') || ''
21
+ )
22
+
17
23
  export const smallMultiples_Bars: Story = {
18
24
  args: {
19
25
  config: smallMultiplesBars,
@@ -21,6 +27,11 @@ export const smallMultiples_Bars: Story = {
21
27
  },
22
28
  play: async ({ canvasElement }) => {
23
29
  await assertVisualizationRendered(canvasElement)
30
+ await waitForPresence('[data-tooltip-html]', canvasElement)
31
+
32
+ const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
33
+ expect(tooltipHtmlValues.length).toBeGreaterThan(0)
34
+ expect(tooltipHtmlValues.some(html => html.includes('tooltip-marker-swatch'))).toBe(false)
24
35
  }
25
36
  }
26
37
 
@@ -58,6 +69,11 @@ export const smallMultiples_StackedBars: Story = {
58
69
  },
59
70
  play: async ({ canvasElement }) => {
60
71
  await assertVisualizationRendered(canvasElement)
72
+ await waitForPresence('[data-tooltip-html]', canvasElement)
73
+
74
+ const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
75
+ expect(tooltipHtmlValues.length).toBeGreaterThan(0)
76
+ expect(tooltipHtmlValues.some(html => html.includes('tooltip-marker-swatch'))).toBe(true)
61
77
  }
62
78
  }
63
79
  export default meta
@@ -0,0 +1,64 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import Chart from '../CdcChartComponent'
3
+ import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
4
+ import smallestLeftAxisMaxConfig from './_mock/smallest_left_axis_max.json'
5
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
6
+
7
+ const meta: Meta<typeof Chart> = {
8
+ title: 'Components/Templates/Chart/Smallest Left Axis Maximum',
9
+ component: Chart
10
+ }
11
+
12
+ type Story = StoryObj<typeof Chart>
13
+
14
+ /**
15
+ * Region B is the default filter — data only goes up to 1.
16
+ * Without smallestLeftAxisMax, the Y axis would show decimal ticks (0, 0.2, 0.4…).
17
+ * With smallestLeftAxisMax: 5, the axis extends to at least 5, producing clean integer ticks.
18
+ */
19
+ export const WithSmallestLeftAxisMax: Story = {
20
+ args: {
21
+ config: smallestLeftAxisMaxConfig
22
+ },
23
+ play: async ({ canvasElement }) => {
24
+ await assertVisualizationRendered(canvasElement)
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Same chart without the smallestLeftAxisMax setting.
30
+ * Region B data (max 1) causes decimal ticks on the Y axis — the problem this feature solves.
31
+ */
32
+ export const WithoutSmallestLeftAxisMax: Story = {
33
+ args: {
34
+ config: editConfigKeys(smallestLeftAxisMaxConfig, [{ path: ['yAxis', 'smallestLeftAxisMax'], value: undefined }])
35
+ },
36
+ play: async ({ canvasElement }) => {
37
+ await assertVisualizationRendered(canvasElement)
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Region A has data up to 28, well above the smallestLeftAxisMax of 5.
43
+ * The axis naturally scales to the data — the setting has no effect here.
44
+ */
45
+ export const LargerDataUnaffected: Story = {
46
+ args: {
47
+ config: editConfigKeys(smallestLeftAxisMaxConfig, [{ path: ['filters', '0', 'active'], value: 'Region A' }])
48
+ },
49
+ play: async ({ canvasElement }) => {
50
+ await assertVisualizationRendered(canvasElement)
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Editor view so you can see the Smallest Left Axis Maximum field in the Left Value Axis section.
56
+ */
57
+ export const Editor: Story = {
58
+ args: {
59
+ config: smallestLeftAxisMaxConfig,
60
+ isEditor: true
61
+ }
62
+ }
63
+
64
+ export default meta
@@ -6,6 +6,7 @@ import lineChartTwoPointsNewChart from './_mock/line_chart_two_points_new_chart.
6
6
  import lollipop from './_mock/lollipop.json'
7
7
  import forestPlot from '../../examples/feature/forest-plot/forest-plot.json'
8
8
  import pairedBar from './_mock/paired-bar.json'
9
+ import pairedBarAbbreviated from './_mock/paired-bar-abbr.json'
9
10
  import horizontalBarConfig from './_mock/horizontal_bar.json'
10
11
  import horizontalBarsDynamicYAxis from './_mock/horizontal-bars-dynamic-y-axis.json'
11
12
  import barChartLabels from './_mock/barchart_labels.mock.json'
@@ -13,8 +14,10 @@ import pieConfig from './_mock/pie_with_data.json'
13
14
  import pieCalculatedArea from './_mock/pie_calculated_area.json'
14
15
  import areaChartStacked from './_mock/area_chart_stacked.json'
15
16
  import multipleLines from './_mock/short_dates.json'
17
+ import MinimalExampleConfig from '../../examples/minimal-example.json'
16
18
  import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
17
- import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
19
+ import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
20
+ import { expect } from 'storybook/test'
18
21
 
19
22
  const meta: Meta<typeof Chart> = {
20
23
  title: 'Components/Templates/Chart',
@@ -63,7 +66,8 @@ export const Lollipop: Story = {
63
66
 
64
67
  export const Forest_Plot: Story = {
65
68
  args: {
66
- config: forestPlot
69
+ config: forestPlot,
70
+ isEditor: true
67
71
  },
68
72
  play: async ({ canvasElement }) => {
69
73
  await assertVisualizationRendered(canvasElement)
@@ -127,6 +131,16 @@ export const Paired_Bar: Story = {
127
131
  }
128
132
  }
129
133
 
134
+ export const Paired_Bar_Year_Tick_Format_Regression: Story = {
135
+ args: {
136
+ config: pairedBarAbbreviated,
137
+ isEditor: true
138
+ },
139
+ play: async ({ canvasElement }) => {
140
+ await assertVisualizationRendered(canvasElement)
141
+ }
142
+ }
143
+
130
144
  export const Area_Chart_stacked: Story = {
131
145
  args: {
132
146
  config: areaChartStacked,
@@ -137,4 +151,36 @@ export const Area_Chart_stacked: Story = {
137
151
  }
138
152
  }
139
153
 
154
+ export const Metadata_In_Description: Story = {
155
+ args: {
156
+ configUrl: '/packages/chart/tests/fixtures/chart-config-with-metadata.json'
157
+ },
158
+ play: async ({ canvasElement }) => {
159
+ await assertVisualizationRendered(canvasElement)
160
+ const subtext = await waitForPresence('.subtext', canvasElement)
161
+ expect(subtext?.textContent).toContain('January 15, 2026')
162
+ }
163
+ }
164
+
165
+ export const Metadata_Backward_Compat_Plain_Array: Story = {
166
+ args: {
167
+ config: lineChartTwoPointsRegressionTest,
168
+ isEditor: false
169
+ },
170
+ play: async ({ canvasElement }) => {
171
+ await assertVisualizationRendered(canvasElement)
172
+ }
173
+ }
174
+
175
+ export const Minimal_Config: Story = {
176
+ args: {
177
+ config: MinimalExampleConfig,
178
+ isEditor: false
179
+ },
180
+ play: async ({ canvasElement }) => {
181
+ await assertVisualizationRendered(canvasElement)
182
+ expect(canvasElement.textContent).toContain('Minimal Chart')
183
+ }
184
+ }
185
+
140
186
  export default meta
@@ -3,6 +3,10 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
3
3
  import Chart from '../CdcChartComponent'
4
4
  import barChartStacked from './_mock/barchart_labels.mock.json'
5
5
  import barChartSuppressed from './_mock/bar-chart-suppressed.json'
6
+ import simplifiedLine from './_mock/simplified_line.json'
7
+ import scatterPlot from './_mock/scatterplot_mock.json'
8
+ import gradientLegend from './_mock/legend.gradient_mock.json'
9
+ import patternLegend from './_mock/stacked-pattern-test.json'
6
10
  import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
7
11
  const meta: Meta<typeof Chart> = {
8
12
  title: 'Components/Templates/Chart/Tooltip',
@@ -68,4 +72,68 @@ export const Additional_Tooltip: Story = {
68
72
  }
69
73
  }
70
74
 
75
+ export const Line_Tooltip_Markers: Story = {
76
+ args: {
77
+ config: {
78
+ ...simplifiedLine,
79
+ title: 'Line Tooltip Markers'
80
+ }
81
+ },
82
+ play: async ({ canvasElement }) => {
83
+ await assertVisualizationRendered(canvasElement)
84
+ }
85
+ }
86
+
87
+ export const Scatter_Tooltip_Markers: Story = {
88
+ args: {
89
+ config: {
90
+ ...scatterPlot,
91
+ title: 'Scatter Tooltip Markers'
92
+ }
93
+ },
94
+ play: async ({ canvasElement }) => {
95
+ await assertVisualizationRendered(canvasElement)
96
+ }
97
+ }
98
+
99
+ export const No_Markers_When_Legend_Hidden: Story = {
100
+ args: {
101
+ config: {
102
+ ...simplifiedLine,
103
+ title: 'Tooltip Without Markers When Legend Hidden',
104
+ legend: {
105
+ ...simplifiedLine.legend,
106
+ hide: true
107
+ }
108
+ }
109
+ },
110
+ play: async ({ canvasElement }) => {
111
+ await assertVisualizationRendered(canvasElement)
112
+ }
113
+ }
114
+
115
+ export const No_Markers_For_Gradient_Legend: Story = {
116
+ args: {
117
+ config: {
118
+ ...gradientLegend,
119
+ title: 'Tooltip Without Markers For Gradient Legend'
120
+ }
121
+ },
122
+ play: async ({ canvasElement }) => {
123
+ await assertVisualizationRendered(canvasElement)
124
+ }
125
+ }
126
+
127
+ export const Pattern_Legend_Uses_Color_Markers: Story = {
128
+ args: {
129
+ config: {
130
+ ...patternLegend,
131
+ title: 'Tooltip Color Markers For Pattern Legend'
132
+ }
133
+ },
134
+ play: async ({ canvasElement }) => {
135
+ await assertVisualizationRendered(canvasElement)
136
+ }
137
+ }
138
+
71
139
  export default meta