@cdc/chart 4.26.3 → 4.26.5

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 (192) hide show
  1. package/CONFIG.md +489 -0
  2. package/README.md +42 -17
  3. package/dist/cdcchart.js +39024 -36587
  4. package/examples/feature/bar/additional-column-tooltip.json +3 -3
  5. package/examples/feature/deviation/planet-deviation-config.json +2 -2
  6. package/examples/feature/heatmap/average-age-categorical.json +313 -0
  7. package/examples/feature/heatmap/calendar-heatmap.json +134 -0
  8. package/examples/feature/heatmap/cell-values-bottom-axis.json +181 -0
  9. package/examples/feature/heatmap/sparse-aggregation.json +158 -0
  10. package/examples/feature/paired-bar/paired-bar-example.json +2 -2
  11. package/examples/feature/tests-case-rate/case-rate-example-config.json +2 -2
  12. package/examples/feature/tests-covid/covid-confidence-example-config.json +2 -2
  13. package/examples/feature/tests-covid/covid-example-config.json +2 -2
  14. package/examples/feature/tests-cutoff/cutoff-example-config.json +2 -2
  15. package/examples/feature/tests-non-numerics/planet-pie-example-config-nonnumeric.json +2 -2
  16. package/examples/metadata-variables.json +1 -1
  17. package/examples/minimal-example.json +24 -0
  18. package/examples/private/bump-chart.json +1116 -0
  19. package/examples/private/test-123.json +624 -0
  20. package/examples/private/test2.json +35012 -0
  21. package/package.json +5 -4
  22. package/src/CdcChartComponent.tsx +144 -60
  23. package/src/_stories/Chart.AltText.stories.tsx +136 -0
  24. package/src/_stories/{Chart.HTMLInDataTable.stories.tsx → Chart.HTMLInDataTable.smoke.stories.tsx} +10 -0
  25. package/src/_stories/Chart.HeatMap.stories.tsx +187 -0
  26. package/src/_stories/Chart.Minimal.smoke.stories.tsx +25 -0
  27. package/src/_stories/Chart.Sankey.smoke.stories.tsx +62 -0
  28. package/src/_stories/Chart.SmallMultiples.smoke.stories.tsx +108 -0
  29. package/src/_stories/{Chart.stories.tsx → Chart.smoke.stories.tsx} +12 -0
  30. package/src/_stories/{Chart.tooltip.stories.tsx → Chart.tooltip.smoke.stories.tsx} +68 -0
  31. package/src/_stories/{ChartAxisTitles.stories.tsx → ChartAxisTitles.smoke.stories.tsx} +13 -0
  32. package/src/_stories/ChartBar.Editor.stories.tsx +151 -85
  33. package/src/_stories/ChartBrush.Interaction.stories.tsx +112 -0
  34. package/src/_stories/{ChartBrush.stories.tsx → ChartBrush.smoke.stories.tsx} +13 -4
  35. package/src/_stories/ChartEditor.Editor.stories.tsx +23 -0
  36. package/src/_stories/ChartLine.DashedDates.smoke.stories.tsx +23 -0
  37. package/src/_stories/_mock/alt_text_metadata.json +38 -0
  38. package/src/_stories/_mock/heatmap-average-age-categorical.json +315 -0
  39. package/src/_stories/_mock/heatmap.json +156 -0
  40. package/src/_stories/_mock/line_chart_dashed_dates.json +276 -0
  41. package/src/_stories/_mock/paired-bar-abbr.json +1 -1
  42. package/src/_stories/_mock/paired-bar.json +1 -1
  43. package/src/_stories/_mock/reported_pertussis_cases.json +1643 -0
  44. package/src/_stories/_mock/sankey_mock.json +78 -0
  45. package/src/_stories/_mock/small_multiples/small_multiples_lines.json +1 -1
  46. package/src/components/Annotations/components/AnnotationDraggable.tsx +2 -2
  47. package/src/components/Annotations/components/AnnotationDropdown.tsx +5 -2
  48. package/src/components/AreaChart/components/AreaChart.Stacked.jsx +10 -2
  49. package/src/components/Axis/BottomAxis.tsx +1 -1
  50. package/src/components/Axis/LeftAxis.tsx +15 -14
  51. package/src/components/Axis/LeftAxisGridlines.tsx +14 -13
  52. package/src/components/BarChart/components/BarChart.Horizontal.tsx +9 -5
  53. package/src/components/BarChart/components/BarChart.StackedHorizontal.tsx +9 -5
  54. package/src/components/BarChart/components/BarChart.StackedVertical.tsx +9 -5
  55. package/src/components/BarChart/components/BarChart.Vertical.tsx +9 -6
  56. package/src/components/BarChart/components/BarChart.tsx +2 -6
  57. package/src/components/BarChart/helpers/useBarChart.ts +1 -1
  58. package/src/components/BoxPlot/BoxPlot.Vertical.tsx +3 -3
  59. package/src/components/Brush/BrushSelector.tsx +88 -187
  60. package/src/components/Brush/brushInteraction.ts +22 -0
  61. package/src/components/Brush/tests/brushInteraction.test.ts +38 -0
  62. package/src/components/EditorPanel/EditorPanel.tsx +946 -960
  63. package/src/components/EditorPanel/EditorPanelContext.ts +1 -1
  64. package/src/components/EditorPanel/components/Panels/Panel.Annotate.tsx +229 -165
  65. package/src/components/EditorPanel/components/Panels/Panel.General.tsx +105 -13
  66. package/src/components/EditorPanel/components/Panels/Panel.PatternSettings.tsx +169 -116
  67. package/src/components/EditorPanel/components/Panels/Panel.Regions.tsx +159 -109
  68. package/src/components/EditorPanel/components/Panels/Panel.Sankey.tsx +3 -3
  69. package/src/components/EditorPanel/components/Panels/Panel.Series.tsx +14 -28
  70. package/src/components/EditorPanel/components/Panels/Panel.Visual.tsx +1 -1
  71. package/src/components/EditorPanel/helpers/getVisualizationTypeConfigUpdate.ts +57 -0
  72. package/src/components/EditorPanel/helpers/tests/getVisualizationTypeConfigUpdate.test.ts +56 -0
  73. package/src/components/EditorPanel/useEditorPermissions.test.tsx +70 -0
  74. package/src/components/EditorPanel/useEditorPermissions.ts +32 -21
  75. package/src/components/Forecasting/Forecasting.tsx +2 -2
  76. package/src/components/HeatMap/components/HeatMap.tsx +762 -0
  77. package/src/components/HeatMap/components/HeatMapGradientLegend.tsx +114 -0
  78. package/src/components/HeatMap/heatmap.constants.ts +13 -0
  79. package/src/components/HeatMap/heatmap.css +71 -0
  80. package/src/components/HeatMap/helpers/index.ts +261 -0
  81. package/src/components/HeatMap/index.tsx +5 -0
  82. package/src/components/HeatMap/tests/HeatMap.test.tsx +1040 -0
  83. package/src/components/HeatMap/tests/helpers.test.ts +167 -0
  84. package/src/components/HorizonChart/HorizonChart.tsx +10 -2
  85. package/src/components/HoverLine/HoverLine.tsx +6 -8
  86. package/src/components/Legend/Legend.Component.tsx +4 -4
  87. package/src/components/Legend/LegendGroup/LegendGroup.styles.css +5 -0
  88. package/src/components/Legend/LegendGroup/LegendGroup.tsx +3 -1
  89. package/src/components/Legend/helpers/index.ts +0 -2
  90. package/src/components/LineChart/LineChartProps.ts +1 -0
  91. package/src/components/LineChart/components/LineChart.BumpCircle.tsx +22 -24
  92. package/src/components/LineChart/helpers.test.ts +345 -0
  93. package/src/components/LineChart/helpers.ts +15 -5
  94. package/src/components/LineChart/index.tsx +18 -5
  95. package/src/components/LinearChart/VisualizationRenderer.tsx +11 -1
  96. package/src/components/LinearChart/tests/LinearChart.test.tsx +228 -7
  97. package/src/components/LinearChart/tests/mockConfigContext.ts +1 -0
  98. package/src/components/LinearChart.tsx +155 -77
  99. package/src/components/PairedBarChart.jsx +2 -2
  100. package/src/components/PieChart/PieChart.tsx +15 -10
  101. package/src/components/RadarChart/RadarChart.tsx +3 -1
  102. package/src/components/Regions/components/Regions.tsx +199 -35
  103. package/src/components/Sankey/components/Sankey.tsx +7 -3
  104. package/src/components/Sankey/helpers/getSankeyTooltip.tsx +7 -6
  105. package/src/components/ScatterPlot/ScatterPlot.jsx +19 -17
  106. package/src/components/SmallMultiples/SmallMultipleTile.tsx +31 -3
  107. package/src/components/SmallMultiples/SmallMultiples.css +12 -0
  108. package/src/components/SmallMultiples/SmallMultiples.tsx +15 -9
  109. package/src/components/SmallMultiples/tests/SmallMultipleTile.test.tsx +152 -0
  110. package/src/components/Sparkline/components/SparkLine.tsx +5 -10
  111. package/src/components/WarmingStripes/WarmingStripes.tsx +5 -3
  112. package/src/components/WarmingStripes/WarmingStripesGradientLegend.css +1 -1
  113. package/src/components/WarmingStripes/WarmingStripesGradientLegend.tsx +2 -2
  114. package/src/data/initial-state.js +12 -0
  115. package/src/helpers/axisLabelFontSize.ts +8 -0
  116. package/src/helpers/bumpChartTooltip.ts +54 -0
  117. package/src/helpers/calculateLeftYAxisWidth.ts +197 -0
  118. package/src/helpers/ensureSpecialChartAxisTypes.ts +11 -0
  119. package/src/helpers/getMinMax.ts +2 -3
  120. package/src/helpers/getYAxisAutoPadding.ts +8 -6
  121. package/src/helpers/getYAxisAutoPaddingMode.ts +17 -0
  122. package/src/helpers/handleChartAriaLabels.ts +23 -10
  123. package/src/helpers/seriesHighlight.ts +26 -0
  124. package/src/helpers/tests/axisLabelFontSize.test.ts +15 -0
  125. package/src/helpers/tests/bumpChartTooltip.test.ts +75 -0
  126. package/src/helpers/tests/ensureSpecialChartAxisTypes.test.ts +30 -0
  127. package/src/helpers/tests/getYAxisAutoPadding.test.ts +37 -0
  128. package/src/helpers/tests/getYAxisAutoPaddingMode.test.ts +90 -0
  129. package/src/helpers/tests/handleChartAriaLabels.test.ts +71 -0
  130. package/src/helpers/tests/seriesHighlight.test.ts +61 -0
  131. package/src/helpers/tests/tooltipHelpers.test.ts +151 -0
  132. package/src/helpers/tooltipHelpers.ts +176 -0
  133. package/src/hooks/tests/useScales.test.tsx +105 -0
  134. package/src/hooks/useHighlightedBars.ts +9 -16
  135. package/src/hooks/useProgrammaticTooltip.ts +16 -2
  136. package/src/hooks/useScales.ts +19 -9
  137. package/src/hooks/useSmallMultipleSynchronization.ts +3 -4
  138. package/src/hooks/useTooltip.tsx +167 -38
  139. package/src/index.jsx +5 -2
  140. package/src/scss/main.scss +32 -49
  141. package/src/test/CdcChart.test.jsx +24 -0
  142. package/src/types/ChartConfig.ts +19 -0
  143. package/src/types/ChartContext.ts +2 -0
  144. package/examples/chart-regression-2.json +0 -2360
  145. package/examples/feature/__data__/age-adjusted-rates.json +0 -1486
  146. package/examples/feature/__data__/city-temperature.json +0 -2198
  147. package/examples/feature/bar/Barchart_with_negative.json +0 -34
  148. package/examples/feature/boxplot/box-plot-data.json +0 -71
  149. package/examples/feature/boxplot/boxplot-data.json +0 -156
  150. package/examples/feature/combo/combochart-categories_are_numbers .json +0 -18
  151. package/examples/feature/forest-plot/log.json +0 -26
  152. package/examples/feature/tests-date-exclusions/date-exclusions-data.json +0 -162
  153. package/examples/region-issue.json +0 -2065
  154. package/examples/xaxis.json +0 -493
  155. package/src/_stories/Chart.SmallMultiples.stories.tsx +0 -63
  156. /package/examples/{data → __data__}/data-with-metadata.json +0 -0
  157. /package/examples/feature/bar/{tall-data.json → __data__/tall-data.json} +0 -0
  158. /package/examples/feature/deviation/{planet-deviation-data.json → __data__/planet-deviation-data.json} +0 -0
  159. /package/examples/feature/paired-bar/{paired-bar-data.json → __data__/paired-bar-data.json} +0 -0
  160. /package/examples/feature/paired-bar/{paired-bar-formatted.json → __data__/paired-bar-formatted.json} +0 -0
  161. /package/examples/feature/tests-case-rate/{case-rate-example-data.json → __data__/case-rate-example-data.json} +0 -0
  162. /package/examples/feature/tests-covid/{covid-example-data-confidence.json → __data__/covid-example-data-confidence.json} +0 -0
  163. /package/examples/feature/tests-covid/{covid-example-data.json → __data__/covid-example-data.json} +0 -0
  164. /package/examples/feature/tests-cutoff/{cutoff-example-data.json → __data__/cutoff-example-data.json} +0 -0
  165. /package/examples/feature/tests-non-numerics/{planet-example-data-nonnumeric.json → __data__/planet-example-data-nonnumeric.json} +0 -0
  166. /package/src/_stories/{Chart.Anchors.stories.tsx → Chart.Anchors.smoke.stories.tsx} +0 -0
  167. /package/src/_stories/{Chart.BoxPlot.stories.tsx → Chart.BoxPlot.smoke.stories.tsx} +0 -0
  168. /package/src/_stories/{Chart.CI.stories.tsx → Chart.CI.smoke.stories.tsx} +0 -0
  169. /package/src/_stories/{Chart.Combo.stories.tsx → Chart.Combo.smoke.stories.tsx} +0 -0
  170. /package/src/_stories/{Chart.CustomColors.stories.tsx → Chart.CustomColors.smoke.stories.tsx} +0 -0
  171. /package/src/_stories/{Chart.Defaults.stories.tsx → Chart.Defaults.smoke.stories.tsx} +0 -0
  172. /package/src/_stories/{Chart.DynamicSeries.stories.tsx → Chart.DynamicSeries.smoke.stories.tsx} +0 -0
  173. /package/src/_stories/{Chart.Filters.stories.tsx → Chart.Filters.smoke.stories.tsx} +0 -0
  174. /package/src/_stories/{Chart.Forecast.stories.tsx → Chart.Forecast.smoke.stories.tsx} +0 -0
  175. /package/src/_stories/{Chart.Legend.Gradient.stories.tsx → Chart.Legend.Gradient.smoke.stories.tsx} +0 -0
  176. /package/src/_stories/{Chart.Patterns.stories.tsx → Chart.Patterns.smoke.stories.tsx} +0 -0
  177. /package/src/_stories/{Chart.PreserveDecimals.stories.tsx → Chart.PreserveDecimals.smoke.stories.tsx} +0 -0
  178. /package/src/_stories/{Chart.Regions.Categorical.stories.tsx → Chart.Regions.Categorical.smoke.stories.tsx} +0 -0
  179. /package/src/_stories/{Chart.Regions.DateScale.stories.tsx → Chart.Regions.DateScale.smoke.stories.tsx} +0 -0
  180. /package/src/_stories/{Chart.Regions.DateTimeScale.stories.tsx → Chart.Regions.DateTimeScale.smoke.stories.tsx} +0 -0
  181. /package/src/_stories/{Chart.ScatterPlot.stories.tsx → Chart.ScatterPlot.smoke.stories.tsx} +0 -0
  182. /package/src/_stories/{Chart.SmallestLeftAxisMax.stories.tsx → Chart.SmallestLeftAxisMax.smoke.stories.tsx} +0 -0
  183. /package/src/_stories/{ChartAnnotation.stories.tsx → ChartAnnotation.smoke.stories.tsx} +0 -0
  184. /package/src/_stories/{ChartAxisLabels.stories.tsx → ChartAxisLabels.smoke.stories.tsx} +0 -0
  185. /package/src/_stories/{ChartBrush.Matrix.Continuous.stories.tsx → ChartBrush.Matrix.Continuous.smoke.stories.tsx} +0 -0
  186. /package/src/_stories/{ChartBrush.Matrix.Date.stories.tsx → ChartBrush.Matrix.Date.smoke.stories.tsx} +0 -0
  187. /package/src/_stories/{ChartBrush.Matrix.DateTime.stories.tsx → ChartBrush.Matrix.DateTime.smoke.stories.tsx} +0 -0
  188. /package/src/_stories/{ChartLine.QuadrantAngles.stories.tsx → ChartLine.QuadrantAngles.smoke.stories.tsx} +0 -0
  189. /package/src/_stories/{ChartLine.Suppression.stories.tsx → ChartLine.Suppression.smoke.stories.tsx} +0 -0
  190. /package/src/_stories/{ChartLine.Symbols.stories.tsx → ChartLine.Symbols.smoke.stories.tsx} +0 -0
  191. /package/src/_stories/{ChartPrefixSuffix.stories.tsx → ChartPrefixSuffix.smoke.stories.tsx} +0 -0
  192. /package/src/_stories/{TechAdoptionWithLinks.stories.tsx → TechAdoptionWithLinks.smoke.stories.tsx} +0 -0
@@ -0,0 +1,187 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { expect } from 'storybook/test'
3
+ import Chart from '../CdcChart'
4
+ import heatMapAverageAgeCategoricalConfig from './_mock/heatmap-average-age-categorical.json'
5
+ import heatMapSparseAggregationConfig from '../../examples/feature/heatmap/sparse-aggregation.json'
6
+ import heatMapCellValuesBottomAxisConfig from '../../examples/feature/heatmap/cell-values-bottom-axis.json'
7
+ import heatMapCalendarConfig from '../../examples/feature/heatmap/calendar-heatmap.json'
8
+ import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
9
+
10
+ const meta: Meta<typeof Chart> = {
11
+ title: 'Components/Templates/Chart/HeatMap',
12
+ component: Chart
13
+ }
14
+
15
+ type Story = StoryObj<typeof Chart>
16
+
17
+ const heatMapTopYAxisLabelConfig = {
18
+ ...heatMapAverageAgeCategoricalConfig,
19
+ yAxis: {
20
+ ...heatMapAverageAgeCategoricalConfig.yAxis,
21
+ titlePlacement: 'top'
22
+ }
23
+ }
24
+
25
+ const getTooltipHtmlValues = (canvasElement: HTMLElement) =>
26
+ Array.from(canvasElement.querySelectorAll('[data-tooltip-html]')).map(
27
+ element => element.getAttribute('data-tooltip-html') || ''
28
+ )
29
+
30
+ const getSvgTextBox = (svg: SVGSVGElement, value: string) =>
31
+ Array.from(svg.querySelectorAll('text'))
32
+ .find(text => text.textContent === value)
33
+ ?.getBoundingClientRect()
34
+
35
+ const getSvgText = (svg: SVGSVGElement, value: string) =>
36
+ Array.from(svg.querySelectorAll('text')).find(text => text.textContent === value)
37
+
38
+ const getTranslateY = (element: Element | null | undefined) => {
39
+ const transform = element?.getAttribute('transform') || ''
40
+ const match = transform.match(/translate\([^,]+,\s*([^)]+)\)/)
41
+ return match ? Number(match[1]) : 0
42
+ }
43
+
44
+ const getElementBox = (canvasElement: HTMLElement, selector: string) =>
45
+ canvasElement.querySelector(selector)?.getBoundingClientRect()
46
+
47
+ export const HeatMap_Demo: Story = {
48
+ args: {
49
+ config: heatMapSparseAggregationConfig,
50
+ isEditor: false
51
+ },
52
+ play: async ({ canvasElement }) => {
53
+ await assertVisualizationRendered(canvasElement)
54
+ await waitForPresence('.visx-heatmap-rect', canvasElement)
55
+
56
+ const cells = canvasElement.querySelectorAll('.visx-heatmap-rect')
57
+ const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
58
+ const firstCell = cells[0]
59
+ const legendBox = getElementBox(canvasElement, '.cdc-heatmap__legend')
60
+ const heatMapBox = getElementBox(canvasElement, '.cdc-heatmap')
61
+ const dataTableBox = getElementBox(canvasElement, '.data-table-container')
62
+ const tableGap = (dataTableBox?.top || 0) - (heatMapBox?.bottom || 0)
63
+
64
+ expect(cells.length).toBe(9)
65
+ expect(firstCell).toHaveAttribute('tabindex', '0')
66
+ expect(firstCell).toHaveAttribute('aria-label')
67
+ expect(legendBox?.bottom).toBeLessThanOrEqual((heatMapBox?.top || 0) + 2)
68
+ expect(tableGap).toBeGreaterThanOrEqual(0)
69
+ expect(tableGap).toBeLessThan(80)
70
+ expect(tooltipHtmlValues.some(html => html.includes('tooltip-heading'))).toBe(true)
71
+ expect(tooltipHtmlValues.some(html => html.includes('Aggregated Rows: 2'))).toBe(true)
72
+ expect(tooltipHtmlValues.some(html => html.includes('Notes: Multiple values'))).toBe(true)
73
+ expect(tooltipHtmlValues.some(html => html.includes('No data'))).toBe(true)
74
+ }
75
+ }
76
+
77
+ export const HeatMap_Average_Age_Categorical_Demo: Story = {
78
+ args: {
79
+ config: heatMapAverageAgeCategoricalConfig,
80
+ isEditor: false
81
+ },
82
+ play: async ({ canvasElement }) => {
83
+ await assertVisualizationRendered(canvasElement)
84
+ await waitForPresence('.visx-heatmap-rect', canvasElement)
85
+
86
+ const cells = canvasElement.querySelectorAll('.visx-heatmap-rect')
87
+ const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
88
+ const firstCell = cells[0]
89
+ const firstCellAriaLabel = firstCell.getAttribute('aria-label') || ''
90
+ const chartSvg = firstCell.ownerSVGElement as SVGSVGElement
91
+ const firstCellBox = firstCell.getBoundingClientRect()
92
+ const lastCellBox = cells[cells.length - 1].getBoundingClientRect()
93
+ const cityLabelBox = getSvgTextBox(chartSvg, 'Atlanta')
94
+ const rowAxisTitle = getSvgText(chartSvg, 'City')
95
+ const columnLabelBox = getSvgTextBox(chartSvg, 'Urban Core')
96
+ const svgBox = chartSvg.getBoundingClientRect()
97
+ const blockLeft = Math.min(cityLabelBox?.left || firstCellBox.left, firstCellBox.left)
98
+ const blockRight = lastCellBox.right
99
+ const blockCenterDelta = Math.abs((blockLeft + blockRight) / 2 - (svgBox.left + svgBox.width / 2))
100
+ const rowLabelGap = cityLabelBox ? firstCellBox.left - cityLabelBox.right : 0
101
+ const columnLabelGap = columnLabelBox ? firstCellBox.top - columnLabelBox.bottom : 0
102
+
103
+ expect(cells.length).toBe(60)
104
+ expect(firstCellAriaLabel).toContain('Community Type: Urban Core')
105
+ expect(firstCellAriaLabel).toContain('City: Atlanta')
106
+ expect(rowLabelGap).toBeGreaterThan(16)
107
+ expect(rowLabelGap).toBeLessThan(80)
108
+ expect(columnLabelGap).toBeGreaterThan(0)
109
+ expect(rowAxisTitle?.getAttribute('transform')).toContain('rotate(-90)')
110
+ expect(columnLabelBox?.top).toBeGreaterThanOrEqual(svgBox.top - 1)
111
+ expect(blockCenterDelta).toBeLessThan(140)
112
+ expect(tooltipHtmlValues.some(html => html.includes('Community Type: Urban Core'))).toBe(true)
113
+ expect(tooltipHtmlValues.some(html => html.includes('Average age: 34'))).toBe(true)
114
+ expect(tooltipHtmlValues.some(html => html.includes('Notes: Dense city center'))).toBe(true)
115
+ }
116
+ }
117
+
118
+ export const HeatMap_Top_Y_Axis_Label_Demo: Story = {
119
+ args: {
120
+ config: heatMapTopYAxisLabelConfig,
121
+ isEditor: false
122
+ },
123
+ play: async ({ canvasElement }) => {
124
+ await assertVisualizationRendered(canvasElement)
125
+ await waitForPresence('.visx-heatmap-rect', canvasElement)
126
+
127
+ const chartSvg = canvasElement.querySelector('.cdc-heatmap__svg') as SVGSVGElement
128
+ const rowAxisTitle = getSvgText(chartSvg, 'City')
129
+ const columnAxisTitle = getSvgText(chartSvg, 'Community Type')
130
+
131
+ expect(rowAxisTitle?.getAttribute('transform')).toBeNull()
132
+ expect(rowAxisTitle?.getAttribute('y')).toBe(columnAxisTitle?.getAttribute('y'))
133
+ }
134
+ }
135
+
136
+ export const HeatMap_Cell_Values_And_Binned_Legend: Story = {
137
+ args: {
138
+ config: heatMapCellValuesBottomAxisConfig,
139
+ isEditor: false
140
+ },
141
+ play: async ({ canvasElement }) => {
142
+ await assertVisualizationRendered(canvasElement)
143
+ await waitForPresence('.visx-heatmap-rect', canvasElement)
144
+ await waitForPresence('.cdc-heatmap__cell-value', canvasElement)
145
+
146
+ const cellValues = canvasElement.querySelectorAll('.cdc-heatmap__cell-value')
147
+ const legendItems = canvasElement.querySelectorAll('.cdc-heatmap__legend .legend-item')
148
+ const legendBox = getElementBox(canvasElement, '.cdc-heatmap__legend')
149
+ const heatMapBox = getElementBox(canvasElement, '.cdc-heatmap')
150
+ const dataTableBox = getElementBox(canvasElement, '.data-table-container')
151
+ const tableGap = (dataTableBox?.top || 0) - (legendBox?.bottom || 0)
152
+
153
+ expect(canvasElement.querySelectorAll('.visx-heatmap-rect').length).toBe(20)
154
+ expect(canvasElement.querySelector('.visx-axis-bottom')).toBeTruthy()
155
+ expect(heatMapBox?.bottom).toBeLessThanOrEqual((legendBox?.top || 0) + 2)
156
+ expect(tableGap).toBeGreaterThanOrEqual(0)
157
+ expect(tableGap).toBeLessThan(80)
158
+ expect(cellValues.length).toBeGreaterThan(0)
159
+ expect(legendItems.length).toBeGreaterThan(0)
160
+ }
161
+ }
162
+
163
+ export const HeatMap_Calendar_Demo: Story = {
164
+ args: {
165
+ config: heatMapCalendarConfig,
166
+ isEditor: false
167
+ },
168
+ play: async ({ canvasElement }) => {
169
+ await assertVisualizationRendered(canvasElement)
170
+ await waitForPresence('.visx-heatmap-rect', canvasElement)
171
+
172
+ const cells = Array.from(canvasElement.querySelectorAll('.visx-heatmap-rect'))
173
+ const chartSvg = cells[0].ownerSVGElement as SVGSVGElement
174
+ const rowAxisTitle = getSvgText(chartSvg, 'Month')
175
+ const gridTop = Math.min(...cells.map(cell => Number(cell.getAttribute('y'))))
176
+ const gridBottom = Math.max(
177
+ ...cells.map(cell => Number(cell.getAttribute('y')) + Number(cell.getAttribute('height')))
178
+ )
179
+ const gridCenter = (gridTop + gridBottom) / 2
180
+
181
+ expect(cells.length).toBe(372)
182
+ expect(rowAxisTitle?.getAttribute('transform')).toContain('rotate(-90)')
183
+ expect(Math.abs(getTranslateY(rowAxisTitle) - gridCenter)).toBeLessThanOrEqual(1)
184
+ }
185
+ }
186
+
187
+ export default meta
@@ -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
+ }
@@ -0,0 +1,62 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import Chart from '../CdcChartComponent'
3
+ import sankeyConfig from './_mock/sankey_mock.json'
4
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
5
+
6
+ const meta: Meta<typeof Chart> = {
7
+ title: 'Components/Templates/Chart/Sankey',
8
+ component: Chart
9
+ }
10
+
11
+ export default meta
12
+ type Story = StoryObj<typeof Chart>
13
+
14
+ export const Sankey_Basic: Story = {
15
+ args: {
16
+ config: {
17
+ ...sankeyConfig,
18
+ title: 'Patient Care Pathway'
19
+ },
20
+ isEditor: false
21
+ },
22
+ play: async ({ canvasElement }) => {
23
+ await assertVisualizationRendered(canvasElement)
24
+ }
25
+ }
26
+
27
+ export const Sankey_WithStoryNodes: Story = {
28
+ args: {
29
+ config: {
30
+ ...sankeyConfig,
31
+ title: 'Care Pathway with Story Nodes',
32
+ data: [
33
+ {
34
+ links: sankeyConfig.data[0].links,
35
+ tooltips: true,
36
+ storyNodeText: [
37
+ { StoryNode: 'Screened', segmentTextBefore: 'Total Screened', segmentTextAfter: 'participants' },
38
+ { StoryNode: 'Recovered', segmentTextBefore: 'Fully', segmentTextAfter: 'patients' }
39
+ ]
40
+ }
41
+ ]
42
+ },
43
+ isEditor: false
44
+ },
45
+ play: async ({ canvasElement }) => {
46
+ await assertVisualizationRendered(canvasElement)
47
+ }
48
+ }
49
+
50
+ export const Sankey_TooltipsDisabled: Story = {
51
+ args: {
52
+ config: {
53
+ ...sankeyConfig,
54
+ title: 'Sankey — Tooltips Disabled',
55
+ enableTooltips: false
56
+ },
57
+ isEditor: false
58
+ },
59
+ play: async ({ canvasElement }) => {
60
+ await assertVisualizationRendered(canvasElement)
61
+ }
62
+ }
@@ -0,0 +1,108 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import Chart from '../CdcChartComponent'
3
+ import smallMultiplesBars from './_mock/small_multiples/small_multiples_bars.json'
4
+ import smallMultiplesBigDataBars from './_mock/small_multiples/small_multiples_big_data_bars.json'
5
+ import smallMultiplesLinesColors from './_mock/small_multiples/small_multiples_lines_colors.json'
6
+ import smallMultiplesLines from './_mock/small_multiples/small_multiples_lines.json'
7
+ import smallMultiplesStackedBars from './_mock/small_multiples/small_multiples_stacked_bars.json'
8
+ import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
9
+ import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
10
+ import { expect } from 'storybook/test'
11
+
12
+ const meta: Meta<typeof Chart> = {
13
+ title: 'Components/Templates/Chart/Small Multiples',
14
+ component: Chart
15
+ }
16
+
17
+ type Story = StoryObj<typeof Chart>
18
+
19
+ const getTooltipHtmlValues = (canvasElement: HTMLElement) =>
20
+ Array.from(canvasElement.querySelectorAll('[data-tooltip-html]')).map(
21
+ element => element.getAttribute('data-tooltip-html') || ''
22
+ )
23
+
24
+ export const smallMultiples_Bars: Story = {
25
+ args: {
26
+ config: smallMultiplesBars,
27
+ isEditor: false
28
+ },
29
+ play: async ({ canvasElement }) => {
30
+ await assertVisualizationRendered(canvasElement)
31
+ await waitForPresence('[data-tooltip-html]', canvasElement)
32
+
33
+ const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
34
+ expect(tooltipHtmlValues.length).toBeGreaterThan(0)
35
+ expect(tooltipHtmlValues.some(html => html.includes('tooltip-marker-swatch'))).toBe(false)
36
+ }
37
+ }
38
+
39
+ export const smallMultiples_BigDataBars: Story = {
40
+ args: {
41
+ config: smallMultiplesBigDataBars,
42
+ isEditor: false
43
+ },
44
+ play: async ({ canvasElement }) => {
45
+ await assertVisualizationRendered(canvasElement)
46
+ }
47
+ }
48
+ export const smallMultiples_LinesColors: Story = {
49
+ args: {
50
+ config: smallMultiplesLinesColors,
51
+ isEditor: false
52
+ },
53
+ play: async ({ canvasElement }) => {
54
+ await assertVisualizationRendered(canvasElement)
55
+ }
56
+ }
57
+ export const smallMultiples_Lines: Story = {
58
+ args: {
59
+ config: smallMultiplesLines,
60
+ isEditor: false
61
+ },
62
+ play: async ({ canvasElement }) => {
63
+ await assertVisualizationRendered(canvasElement)
64
+ }
65
+ }
66
+ export const smallMultiples_TopYAxisTitle: Story = {
67
+ args: {
68
+ config: editConfigKeys(smallMultiplesLines, [
69
+ { path: ['yAxis', 'label'], value: 'Percentage of emergency department visits' },
70
+ { path: ['runtime', 'yAxis', 'label'], value: 'Percentage of emergency department visits' },
71
+ { path: ['yAxis', 'inlineLabel'], value: ' percent' },
72
+ { path: ['yAxis', 'titlePlacement'], value: 'top' }
73
+ ]),
74
+ isEditor: false
75
+ },
76
+ play: async ({ canvasElement }) => {
77
+ await assertVisualizationRendered(canvasElement)
78
+ }
79
+ }
80
+ export const smallMultiples_TopYAxisTitle2: Story = {
81
+ args: {
82
+ config: editConfigKeys(smallMultiplesLines, [
83
+ { path: ['yAxis', 'label'], value: 'Percentage of emergency department visits' },
84
+ { path: ['runtime', 'yAxis', 'label'], value: 'Percentage of emergency department visits' },
85
+ { path: ['yAxis', 'inlineLabel'], value: '%' },
86
+ { path: ['yAxis', 'titlePlacement'], value: 'top' }
87
+ ]),
88
+ isEditor: false
89
+ },
90
+ play: async ({ canvasElement }) => {
91
+ await assertVisualizationRendered(canvasElement)
92
+ }
93
+ }
94
+ export const smallMultiples_StackedBars: Story = {
95
+ args: {
96
+ config: smallMultiplesStackedBars,
97
+ isEditor: false
98
+ },
99
+ play: async ({ canvasElement }) => {
100
+ await assertVisualizationRendered(canvasElement)
101
+ await waitForPresence('[data-tooltip-html]', canvasElement)
102
+
103
+ const tooltipHtmlValues = getTooltipHtmlValues(canvasElement)
104
+ expect(tooltipHtmlValues.length).toBeGreaterThan(0)
105
+ expect(tooltipHtmlValues.some(html => html.includes('tooltip-marker-swatch'))).toBe(true)
106
+ }
107
+ }
108
+ export default meta
@@ -14,6 +14,7 @@ import pieConfig from './_mock/pie_with_data.json'
14
14
  import pieCalculatedArea from './_mock/pie_calculated_area.json'
15
15
  import areaChartStacked from './_mock/area_chart_stacked.json'
16
16
  import multipleLines from './_mock/short_dates.json'
17
+ import MinimalExampleConfig from '../../examples/minimal-example.json'
17
18
  import { editConfigKeys } from '@cdc/core/helpers/configHelpers'
18
19
  import { assertVisualizationRendered, waitForPresence } from '@cdc/core/helpers/testing'
19
20
  import { expect } from 'storybook/test'
@@ -171,4 +172,15 @@ export const Metadata_Backward_Compat_Plain_Array: Story = {
171
172
  }
172
173
  }
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
+
174
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
@@ -61,3 +61,16 @@ export const Paired_Bar: Story = {
61
61
  await assertVisualizationRendered(canvasElement)
62
62
  }
63
63
  }
64
+
65
+ export const Top_Y_Axis_Title: Story = {
66
+ args: {
67
+ config: editConfigKeys(longXLabelsConfig, [
68
+ { path: ['yAxis', 'label'], value: 'Number of emergency department visits' },
69
+ { path: ['runtime', 'yAxis', 'label'], value: 'Number of emergency department visits' },
70
+ { path: ['yAxis', 'titlePlacement'], value: 'top' }
71
+ ])
72
+ },
73
+ play: async ({ canvasElement }) => {
74
+ await assertVisualizationRendered(canvasElement)
75
+ }
76
+ }