@cdc/core 4.25.11 → 4.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.claude/agents/qa-test-developer.md +126 -0
  2. package/CLAUDE.local.md +67 -0
  3. package/_stories/Gallery.Charts.stories.tsx +300 -0
  4. package/_stories/Gallery.DataBite.stories.tsx +79 -0
  5. package/_stories/Gallery.Maps.stories.tsx +239 -0
  6. package/_stories/Gallery.WaffleChart.stories.tsx +187 -0
  7. package/_stories/PageART.stories.tsx +193 -0
  8. package/_stories/PageBRFSS.stories.tsx +294 -0
  9. package/_stories/PageCancerRegistries.stories.tsx +199 -0
  10. package/_stories/PageEasternEquineEncephalitis.stories.tsx +216 -0
  11. package/_stories/PageExcessiveAlcoholUse.stories.tsx +201 -0
  12. package/_stories/PageMaternalMortality.stories.tsx +193 -0
  13. package/_stories/PageOralHealth.stories.tsx +201 -0
  14. package/_stories/PageRespiratory.stories.tsx +332 -0
  15. package/_stories/PageSmokingTobacco.stories.tsx +200 -0
  16. package/_stories/PageStateDiabetesProfiles.stories.tsx +201 -0
  17. package/_stories/PageWastewater.stories.tsx +477 -0
  18. package/_stories/VegaImport.stories.tsx +401 -0
  19. package/_stories/vega-fixtures/bars-with-line.json +444 -0
  20. package/_stories/vega-fixtures/bars.json +58 -0
  21. package/_stories/vega-fixtures/combo-bar-rolling-mean.json +88 -0
  22. package/_stories/vega-fixtures/combo.json +68 -0
  23. package/_stories/vega-fixtures/grouped-horizontal-bars.json +83 -0
  24. package/_stories/vega-fixtures/grouped-horizontal-bars2.json +231 -0
  25. package/_stories/vega-fixtures/horizontal-bar.json +427 -0
  26. package/_stories/vega-fixtures/horizontal-bars-with-bad-colors.json +197 -0
  27. package/_stories/vega-fixtures/horizontal-bars2.json +58 -0
  28. package/_stories/vega-fixtures/lines.json +227 -0
  29. package/_stories/vega-fixtures/measles-bars.json +348 -0
  30. package/_stories/vega-fixtures/measles-map.json +11101 -0
  31. package/_stories/vega-fixtures/measles-stacked-bars.json +2147 -0
  32. package/_stories/vega-fixtures/multi-dataset.json +255 -0
  33. package/_stories/vega-fixtures/no-data.json +14 -0
  34. package/_stories/vega-fixtures/pie-chart.json +94 -0
  35. package/_stories/vega-fixtures/repeat-spec.json +47 -0
  36. package/_stories/vega-fixtures/stacked-area.json +222 -0
  37. package/_stories/vega-fixtures/stacked-bar-with-rect.json +3412 -0
  38. package/_stories/vega-fixtures/stacked-bars-with-line.json +364 -0
  39. package/_stories/vega-fixtures/stacked-bars.json +212 -0
  40. package/_stories/vega-fixtures/stacked-horizontal-bars.json +140 -0
  41. package/_stories/vega-fixtures/warning-combo.json +59 -0
  42. package/_stories/vega-fixtures/warning-scatter-and-line.json +1182 -0
  43. package/assets/icon-chart-area.svg +1 -0
  44. package/assets/icon-chart-radar.svg +23 -0
  45. package/assets/icon-magnifying-glass.svg +5 -0
  46. package/assets/icon-warming-stripes.svg +13 -0
  47. package/assets/logo2.svg +31 -0
  48. package/components/AdvancedEditor/AdvancedEditor.tsx +4 -0
  49. package/components/AdvancedEditor/EmbedEditor.tsx +513 -0
  50. package/components/ComboBox/ComboBox.tsx +345 -0
  51. package/components/ComboBox/combobox.styles.css +185 -0
  52. package/components/ComboBox/index.ts +1 -0
  53. package/components/CustomColorsEditor/CustomColorsEditor.tsx +3 -10
  54. package/components/DataTable/DataTable.tsx +132 -58
  55. package/components/DataTable/data-table.css +216 -215
  56. package/components/DataTable/helpers/getSeriesName.ts +6 -0
  57. package/components/DataTable/helpers/mapCellMatrix.tsx +14 -6
  58. package/components/EditorPanel/ColumnsEditor.tsx +37 -19
  59. package/components/EditorPanel/DataTableEditor.tsx +51 -25
  60. package/components/EditorPanel/EditorPanel.styles.css +16 -0
  61. package/components/EditorPanel/EditorPanel.tsx +144 -0
  62. package/components/EditorPanel/EditorPanelDispatch.tsx +75 -0
  63. package/components/EditorPanel/FieldSetWrapper.tsx +66 -23
  64. package/components/EditorPanel/Inputs.tsx +33 -7
  65. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +14 -6
  66. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +240 -175
  67. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +33 -29
  68. package/components/EditorPanel/sections/VisualSection.tsx +169 -0
  69. package/components/Filters/Filters.tsx +31 -5
  70. package/components/Filters/helpers/getNestedOptions.ts +2 -1
  71. package/components/Filters/helpers/handleSorting.ts +1 -1
  72. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +84 -2
  73. package/components/Layout/components/Visualization/index.tsx +27 -1
  74. package/components/Layout/components/Visualization/visualizations.scss +7 -0
  75. package/components/Legend/Legend.Gradient.tsx +1 -1
  76. package/components/MediaControls.tsx +53 -28
  77. package/components/_stories/CustomColorsEditor.stories.tsx +37 -0
  78. package/components/_stories/DataTable.stories.tsx +1 -0
  79. package/components/ui/Icon.tsx +3 -1
  80. package/components/ui/Title/index.tsx +30 -2
  81. package/components/ui/Title/title.styles.css +42 -0
  82. package/data/colorPalettes.ts +18 -5
  83. package/data/mapColorPalettes.ts +10 -0
  84. package/devTemplate/dev.js +235 -0
  85. package/devTemplate/index.html +30 -0
  86. package/devTemplate/preview.html +1503 -0
  87. package/devTemplate/sidebar.css +151 -0
  88. package/dist/cove-main.css +2803 -4448
  89. package/dist/cove-main.css.map +1 -1
  90. package/generateViteConfig.js +118 -2
  91. package/helpers/DataTransform.ts +1 -5
  92. package/helpers/addValuesToFilters.ts +6 -1
  93. package/helpers/cove/date.ts +33 -1
  94. package/helpers/cove/string.ts +29 -0
  95. package/helpers/coveUpdateWorker.ts +21 -12
  96. package/helpers/embed/embedCodeGenerator.ts +80 -0
  97. package/helpers/embed/embedHelper.js +158 -0
  98. package/helpers/embed/filterUtils.ts +121 -0
  99. package/helpers/embed/index.ts +21 -0
  100. package/helpers/embed/urlValidation.ts +119 -0
  101. package/helpers/filterVizData.ts +6 -1
  102. package/helpers/getFileExtension.ts +0 -6
  103. package/helpers/getUniqueValues.ts +19 -0
  104. package/helpers/hashObj.ts +25 -0
  105. package/helpers/isRightAlignedTableValue.js +5 -0
  106. package/helpers/metrics/helpers.ts +1 -0
  107. package/helpers/metrics/types.ts +3 -0
  108. package/helpers/palettes/colorDistributions.ts +1 -1
  109. package/helpers/palettes/utils.ts +12 -12
  110. package/helpers/parseCsvWithQuotes.ts +15 -14
  111. package/helpers/pivotData.ts +2 -2
  112. package/helpers/prepareScreenshot.ts +288 -0
  113. package/helpers/queryStringUtils.ts +29 -0
  114. package/helpers/testing.ts +44 -0
  115. package/helpers/tests/DataTransform.test.ts +125 -0
  116. package/helpers/tests/date.test.ts +64 -0
  117. package/helpers/tests/prepareScreenshot.test.ts +414 -0
  118. package/helpers/tests/queryStringUtils.test.ts +381 -0
  119. package/helpers/tests/testStandaloneBuild.ts +23 -5
  120. package/helpers/useDataVizClasses.ts +0 -1
  121. package/helpers/vegaConfig.ts +1 -1
  122. package/helpers/vegaConfigImport.ts +160 -0
  123. package/helpers/ver/4.26.1.ts +80 -0
  124. package/helpers/ver/4.26.2.ts +84 -0
  125. package/helpers/ver/tests/4.26.1.test.ts +105 -0
  126. package/helpers/ver/tests/4.26.2.test.ts +298 -0
  127. package/helpers/viewports.ts +2 -0
  128. package/hooks/useDataColumns.ts +63 -0
  129. package/hooks/useFilterManagement.ts +94 -0
  130. package/hooks/useLegendSeparators.ts +26 -0
  131. package/hooks/useListManagement.ts +192 -0
  132. package/package.json +29 -33
  133. package/styles/_button-section.scss +0 -3
  134. package/styles/v2/components/editor.scss +9 -9
  135. package/styles/v2/utils/_grid.scss +8 -3
  136. package/types/Annotation.ts +10 -11
  137. package/types/Axis.ts +1 -0
  138. package/types/ForecastingSeriesKey.ts +1 -0
  139. package/types/General.ts +2 -0
  140. package/types/MarkupInclude.ts +1 -0
  141. package/types/Palette.ts +21 -0
  142. package/types/Series.ts +3 -0
  143. package/types/Table.ts +1 -0
  144. package/types/Visualization.ts +7 -0
  145. package/types/VizFilter.ts +1 -0
  146. package/LICENSE +0 -201
  147. package/_stories/StoryRenderingTests.stories.tsx +0 -164
@@ -0,0 +1,477 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { within, expect } from 'storybook/test'
3
+ import Chart from '@cdc/chart'
4
+ import CdcMap from '@cdc/map'
5
+ import Dashboard from '@cdc/dashboard'
6
+ import { useEffect, useState } from 'react'
7
+
8
+ // Fallback step function for test descriptions
9
+ const step = async (description: string, fn: () => Promise<void> | void) => {
10
+ console.log(`▶ ${description}`)
11
+ await fn()
12
+ console.log(`✓ ${description}`)
13
+ }
14
+
15
+ const meta: Meta = {
16
+ title: 'Regression Tests/Pages/Wastewater',
17
+ parameters: {
18
+ layout: 'fullscreen',
19
+ docs: {
20
+ description: {
21
+ component: 'Stories for all visualizations from the CDC National Wastewater Surveillance System (NWSS) pages'
22
+ }
23
+ }
24
+ },
25
+ tags: ['autodocs']
26
+ }
27
+
28
+ export default meta
29
+
30
+ // Config URLs from the NWSS pages
31
+ const CONFIG_URLS = {
32
+ // Main NWSS page (https://www.cdc.gov/nwss/index.html)
33
+ homePageModules: 'https://www.cdc.gov/nwss/rv/modules/home-page-modules.json',
34
+
35
+ // Measles page (https://www.cdc.gov/nwss/rv/measles.html)
36
+ measlesTopModules: 'https://www.cdc.gov/nwss/rv/modules/measles/top-three-modules.json',
37
+ measlesMap: 'https://www.cdc.gov/nwss/rv/modules/measles/measles-us-map.json',
38
+ measlesTimePeriod: 'https://www.cdc.gov/nwss/rv/modules/measles/time-period.json',
39
+
40
+ // COVID-19 National Data page (https://www.cdc.gov/nwss/rv/COVID19-national-data.html)
41
+ covidTopModules: 'https://www.cdc.gov/nwss/rv/modules/sc2/covid-top-modules.json',
42
+ covidTimePeriodMap: 'https://www.cdc.gov/nwss/rv/modules/sc2/covid-time-period-state-map.json',
43
+ covidStateLevel: 'https://www.cdc.gov/nwss/rv/modules/sc2/covid-19-state-level.json',
44
+ covidNationalRegionalTrends: 'https://www.cdc.gov/nwss/rv/modules/sc2/covid-19-national-and-regional-trends.json',
45
+
46
+ // COVID-19 State Trend page (https://www.cdc.gov/nwss/rv/COVID19-statetrend.html)
47
+ covidStateLevelRest: 'https://www.cdc.gov/nwss/rv/modules/sc2/State-Level-covid-rest.json'
48
+ }
49
+
50
+ // Helper to fetch config and update data URLs to use absolute cdc.gov paths
51
+ const useConfigWithAbsoluteDataUrl = (configUrl: string) => {
52
+ const [config, setConfig] = useState(null)
53
+
54
+ useEffect(() => {
55
+ fetch(configUrl)
56
+ .then(res => res.json())
57
+ .then(data => {
58
+ // Convert relative data URLs to absolute cdc.gov URLs
59
+ if (data.dataUrl) {
60
+ // Handle different relative path formats (../../path or /path)
61
+ const dataUrl = data.dataUrl.replace(/^(\.\.\/)+/, '').replace(/^\//, '')
62
+ data.dataUrl = `https://www.cdc.gov/${dataUrl}`
63
+ }
64
+ if (data.dataFileName) {
65
+ const dataFileName = data.dataFileName.replace(/^(\.\.\/)+/, '').replace(/^\//, '')
66
+ data.dataFileName = `https://www.cdc.gov/${dataFileName}`
67
+ }
68
+
69
+ // For dashboard configs, convert dataKey references in visualizations
70
+ if (data.visualizations) {
71
+ Object.values(data.visualizations).forEach((viz: any) => {
72
+ if (viz.dataKey) {
73
+ const dataKey = viz.dataKey.replace(/^(\.\.\/)+/, '').replace(/^\//, '')
74
+ viz.dataKey = `https://www.cdc.gov/${dataKey}`
75
+ }
76
+ })
77
+ }
78
+
79
+ // For dashboard configs, convert datasets
80
+ if (data.datasets) {
81
+ const newDatasets = {}
82
+ Object.entries(data.datasets).forEach(([key, dataset]: [string, any]) => {
83
+ const newKey = key.replace(/^(\.\.\/)+/, '').replace(/^\//, '')
84
+ const absoluteKey = `https://www.cdc.gov/${newKey}`
85
+
86
+ newDatasets[absoluteKey] = {
87
+ ...dataset,
88
+ dataFileName: dataset.dataFileName
89
+ ? `https://www.cdc.gov/${dataset.dataFileName.replace(/^(\.\.\/)+/, '').replace(/^\//, '')}`
90
+ : dataset.dataFileName,
91
+ dataUrl: dataset.dataUrl
92
+ ? `https://www.cdc.gov/${dataset.dataUrl.replace(/^(\.\.\/)+/, '').replace(/^\//, '')}`
93
+ : dataset.dataUrl
94
+ }
95
+ })
96
+ data.datasets = newDatasets
97
+ }
98
+
99
+ // Set activeDashboard to 0 if it's null and multiDashboards exist
100
+ if (data.multiDashboards && data.multiDashboards.length > 0 && data.activeDashboard === null) {
101
+ data.activeDashboard = 0
102
+ }
103
+
104
+ setConfig(data)
105
+ })
106
+ .catch(err => {
107
+ console.error('Failed to fetch config:', configUrl, err)
108
+ })
109
+ }, [configUrl])
110
+
111
+ return config
112
+ }
113
+
114
+ type MapStory = StoryObj<typeof CdcMap>
115
+ type ChartStory = StoryObj<typeof Chart>
116
+ type DashboardStory = StoryObj<typeof Dashboard>
117
+
118
+ // Helper function to test map rendering (supports both SVG and canvas-based maps)
119
+ const testMapRendering = async (canvasElement: HTMLElement, storyName: string) => {
120
+ await step('Wait for map to render', async () => {
121
+ await new Promise<void>((resolve, reject) => {
122
+ const startTime = Date.now()
123
+ const timeout = 15000
124
+
125
+ const checkMap = () => {
126
+ const svgMap = canvasElement.querySelector('svg[role="img"]')
127
+ const canvasMap = canvasElement.querySelector('canvas')
128
+ if (svgMap || canvasMap) {
129
+ resolve()
130
+ } else if (Date.now() - startTime > timeout) {
131
+ reject(new Error(`Timeout: No map element (svg or canvas) found after ${timeout}ms`))
132
+ } else {
133
+ setTimeout(checkMap, 100)
134
+ }
135
+ }
136
+ checkMap()
137
+ })
138
+ })
139
+
140
+ await step('Verify map visualization is present', async () => {
141
+ const svgMap = canvasElement.querySelector('svg[role="img"]')
142
+ const canvasMap = canvasElement.querySelector('canvas')
143
+ expect(svgMap || canvasMap).toBeTruthy()
144
+ })
145
+
146
+ await step('Verify COVE module wrapper is present', async () => {
147
+ const coveModule = canvasElement.querySelector('.cdc-open-viz-module')
148
+ expect(coveModule).toBeInTheDocument()
149
+ })
150
+
151
+ console.log(` ${storyName} map rendered successfully`)
152
+ }
153
+
154
+ // Helper function to test chart rendering
155
+ const testChartRendering = async (canvasElement: HTMLElement, storyName: string) => {
156
+ const canvas = within(canvasElement)
157
+
158
+ await step('Wait for chart to render', async () => {
159
+ const svgElement = await canvas.findByRole('img', { hidden: true }, { timeout: 10000 })
160
+ expect(svgElement).toBeInTheDocument()
161
+ })
162
+
163
+ await step('Verify chart SVG is present', async () => {
164
+ const chartSvg = canvasElement.querySelector('svg')
165
+ expect(chartSvg).toBeInTheDocument()
166
+ })
167
+
168
+ await step('Verify COVE module wrapper is present', async () => {
169
+ const coveModule = canvasElement.querySelector('.cdc-open-viz-module')
170
+ expect(coveModule).toBeInTheDocument()
171
+ })
172
+
173
+ console.log(` ${storyName} chart rendered successfully`)
174
+ }
175
+
176
+ // Helper function to test dashboard rendering
177
+ const testDashboardRendering = async (canvasElement: HTMLElement, storyName: string) => {
178
+ await step('Wait for dashboard to render', async () => {
179
+ await new Promise<void>((resolve, reject) => {
180
+ const startTime = Date.now()
181
+ const timeout = 15000
182
+
183
+ const checkDashboard = () => {
184
+ const dashboardElement = canvasElement.querySelector('.type-dashboard')
185
+ if (dashboardElement) {
186
+ resolve()
187
+ } else if (Date.now() - startTime > timeout) {
188
+ reject(new Error(`Timeout: Dashboard element not found after ${timeout}ms`))
189
+ } else {
190
+ setTimeout(checkDashboard, 100)
191
+ }
192
+ }
193
+ checkDashboard()
194
+ })
195
+ })
196
+
197
+ await step('Verify dashboard wrapper is present', async () => {
198
+ const dashboard = canvasElement.querySelector('.type-dashboard')
199
+ expect(dashboard).toBeInTheDocument()
200
+ })
201
+
202
+ console.log(` ${storyName} dashboard rendered successfully`)
203
+ }
204
+
205
+ /**
206
+ * NWSS Home Page - Summary Modules
207
+ *
208
+ * Multi-virus wastewater surveillance summary from the main NWSS landing page.
209
+ */
210
+ export const Home_Page_Modules: DashboardStory = {
211
+ render: () => {
212
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.homePageModules)
213
+ if (!config) return <div>Loading...</div>
214
+ return <Dashboard config={config} />
215
+ },
216
+ play: async ({ canvasElement }) => {
217
+ await testDashboardRendering(canvasElement, 'Home Page Modules')
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Measles - Top Summary Modules
223
+ *
224
+ * Key metrics for measles wastewater detections nationwide.
225
+ */
226
+ export const Measles_Top_Modules: DashboardStory = {
227
+ render: () => {
228
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.measlesTopModules)
229
+ if (!config) return <div>Loading...</div>
230
+ return <Dashboard config={config} />
231
+ },
232
+ play: async ({ canvasElement }) => {
233
+ await testDashboardRendering(canvasElement, 'Measles Top Modules')
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Measles - US Map
239
+ *
240
+ * Geographic distribution of measles wastewater detections across the United States.
241
+ */
242
+ export const Measles_Map: MapStory = {
243
+ render: () => {
244
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.measlesMap)
245
+ if (!config) return <div>Loading...</div>
246
+ return <CdcMap config={config} />
247
+ },
248
+ play: async ({ canvasElement }) => {
249
+ await testMapRendering(canvasElement, 'Measles Map')
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Measles - Time Period
255
+ *
256
+ * Timeline information for measles wastewater surveillance data.
257
+ */
258
+ export const Measles_Time_Period: DashboardStory = {
259
+ render: () => {
260
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.measlesTimePeriod)
261
+ if (!config) return <div>Loading...</div>
262
+ return <Dashboard config={config} />
263
+ },
264
+ play: async ({ canvasElement }) => {
265
+ await testDashboardRendering(canvasElement, 'Measles Time Period')
266
+ }
267
+ }
268
+
269
+ /**
270
+ * COVID-19 - Top Summary Modules
271
+ *
272
+ * Key metrics for COVID-19 wastewater surveillance at the national level.
273
+ */
274
+ export const COVID_Top_Modules: DashboardStory = {
275
+ render: () => {
276
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidTopModules)
277
+ if (!config) return <div>Loading...</div>
278
+ return <Dashboard config={config} />
279
+ },
280
+ play: async ({ canvasElement }) => {
281
+ await testDashboardRendering(canvasElement, 'COVID Top Modules')
282
+ }
283
+ }
284
+
285
+ /**
286
+ * COVID-19 - State Map with Time Period
287
+ *
288
+ * State-level COVID-19 wastewater activity levels across the US.
289
+ */
290
+ export const COVID_Time_Period_Map: MapStory = {
291
+ render: () => {
292
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidTimePeriodMap)
293
+ if (!config) return <div>Loading...</div>
294
+ return <CdcMap config={config} />
295
+ },
296
+ play: async ({ canvasElement }) => {
297
+ await testMapRendering(canvasElement, 'COVID Time Period Map')
298
+ }
299
+ }
300
+
301
+ /**
302
+ * COVID-19 - State Level Data
303
+ *
304
+ * COVID-19 wastewater data visualization by state.
305
+ */
306
+ export const COVID_State_Level: MapStory = {
307
+ render: () => {
308
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidStateLevel)
309
+ if (!config) return <div>Loading...</div>
310
+ return <CdcMap config={config} />
311
+ },
312
+ play: async ({ canvasElement }) => {
313
+ await testMapRendering(canvasElement, 'COVID State Level')
314
+ }
315
+ }
316
+
317
+ /**
318
+ * COVID-19 - National and Regional Trends
319
+ *
320
+ * Trends in COVID-19 wastewater viral activity at national and HHS regional levels.
321
+ */
322
+ export const COVID_National_Regional_Trends: ChartStory = {
323
+ render: () => {
324
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidNationalRegionalTrends)
325
+ if (!config) return <div>Loading...</div>
326
+ return <Chart config={config} />
327
+ },
328
+ play: async ({ canvasElement }) => {
329
+ await testChartRendering(canvasElement, 'COVID National Regional Trends')
330
+ }
331
+ }
332
+
333
+ /**
334
+ * COVID-19 - State Trend Data (Alternative View)
335
+ *
336
+ * State-level COVID-19 wastewater trend visualization from the state trend page.
337
+ */
338
+ export const COVID_State_Level_Rest: ChartStory = {
339
+ render: () => {
340
+ const config = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidStateLevelRest)
341
+ if (!config) return <div>Loading...</div>
342
+ return <Dashboard config={config} />
343
+ },
344
+ play: async ({ canvasElement }) => {
345
+ await testDashboardRendering(canvasElement, 'COVID State Level Rest')
346
+ }
347
+ }
348
+
349
+ /**
350
+ * All Wastewater Visualizations - Combined Test
351
+ *
352
+ * Tests all visualizations from the NWSS pages to ensure they all render correctly together.
353
+ */
354
+ export const All_Wastewater_Visualizations: StoryObj = {
355
+ render: () => {
356
+ const homePageConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.homePageModules)
357
+ const measlesTopConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.measlesTopModules)
358
+ const measlesMapConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.measlesMap)
359
+ const measlesTimePeriodConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.measlesTimePeriod)
360
+ const covidTopConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidTopModules)
361
+ const covidMapConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidTimePeriodMap)
362
+ const covidStateLevelConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidStateLevel)
363
+ const covidNationalRegionalConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidNationalRegionalTrends)
364
+ const covidStateRestConfig = useConfigWithAbsoluteDataUrl(CONFIG_URLS.covidStateLevelRest)
365
+
366
+ const allLoaded =
367
+ homePageConfig &&
368
+ measlesTopConfig &&
369
+ measlesMapConfig &&
370
+ measlesTimePeriodConfig &&
371
+ covidTopConfig &&
372
+ covidMapConfig &&
373
+ covidStateLevelConfig &&
374
+ covidNationalRegionalConfig &&
375
+ covidStateRestConfig
376
+
377
+ if (!allLoaded) {
378
+ return <div>Loading...</div>
379
+ }
380
+
381
+ return (
382
+ <div className='container-fluid p-4'>
383
+ <h1 className='mb-4'>NWSS - All Wastewater Visualizations</h1>
384
+
385
+ <section className='mb-5'>
386
+ <h2>NWSS Home Page</h2>
387
+ <Dashboard config={homePageConfig} />
388
+ </section>
389
+
390
+ <section className='mb-5'>
391
+ <h2>Measles - Summary Modules</h2>
392
+ <Dashboard config={measlesTopConfig} />
393
+ </section>
394
+
395
+ <section className='mb-5'>
396
+ <h2>Measles - US Map</h2>
397
+ <CdcMap config={measlesMapConfig} />
398
+ </section>
399
+
400
+ <section className='mb-5'>
401
+ <h2>Measles - Time Period</h2>
402
+ <Dashboard config={measlesTimePeriodConfig} />
403
+ </section>
404
+
405
+ <section className='mb-5'>
406
+ <h2>COVID-19 - Summary Modules</h2>
407
+ <Dashboard config={covidTopConfig} />
408
+ </section>
409
+
410
+ <section className='mb-5'>
411
+ <h2>COVID-19 - State Map</h2>
412
+ <CdcMap config={covidMapConfig} />
413
+ </section>
414
+
415
+ <section className='mb-5'>
416
+ <h2>COVID-19 - State Level Data</h2>
417
+ <Chart config={covidStateLevelConfig} />
418
+ </section>
419
+
420
+ <section className='mb-5'>
421
+ <h2>COVID-19 - National and Regional Trends</h2>
422
+ <Chart config={covidNationalRegionalConfig} />
423
+ </section>
424
+
425
+ <section className='mb-5'>
426
+ <h2>COVID-19 - State Trends</h2>
427
+ <Chart config={covidStateRestConfig} />
428
+ </section>
429
+ </div>
430
+ )
431
+ },
432
+ play: async ({ canvasElement }) => {
433
+ await step('Wait for all configs to load', async () => {
434
+ await new Promise<void>(resolve => {
435
+ const checkLoading = () => {
436
+ const loadingDiv = canvasElement.querySelector('div:not(.container-fluid)')
437
+ if (!loadingDiv || !loadingDiv.textContent?.includes('Loading')) {
438
+ resolve()
439
+ } else {
440
+ setTimeout(checkLoading, 100)
441
+ }
442
+ }
443
+ checkLoading()
444
+ })
445
+ })
446
+
447
+ await step('Wait for visualizations to start rendering', async () => {
448
+ await new Promise<void>(resolve => setTimeout(resolve, 2000))
449
+ })
450
+
451
+ await step('Wait for all 9 COVE modules to render', async () => {
452
+ await new Promise<void>((resolve, reject) => {
453
+ const startTime = Date.now()
454
+ const timeout = 30000
455
+
456
+ const checkModules = () => {
457
+ const coveModules = canvasElement.querySelectorAll('.cdc-open-viz-module')
458
+ if (coveModules.length >= 9) {
459
+ resolve()
460
+ } else if (Date.now() - startTime > timeout) {
461
+ reject(new Error(`Timeout: Only ${coveModules.length}/9 COVE modules found after ${timeout}ms`))
462
+ } else {
463
+ setTimeout(checkModules, 200)
464
+ }
465
+ }
466
+ checkModules()
467
+ })
468
+ })
469
+
470
+ await step('Verify at least 9 visualizations are present', async () => {
471
+ const coveModules = canvasElement.querySelectorAll('.cdc-open-viz-module')
472
+ expect(coveModules.length).toBeGreaterThanOrEqual(9)
473
+ })
474
+
475
+ console.log(` All 9+ wastewater visualizations rendered successfully`)
476
+ }
477
+ }