@cdc/dashboard 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 (109) hide show
  1. package/CONFIG.md +172 -0
  2. package/README.md +60 -20
  3. package/dist/cdcdashboard-CY9IcPSi.es.js +6 -0
  4. package/dist/cdcdashboard-DlpiY3fQ.es.js +4 -0
  5. package/dist/cdcdashboard.js +56686 -50281
  6. package/examples/__data__/data-2.json +6 -0
  7. package/examples/__data__/data-with-metadata.json +18 -0
  8. package/examples/__data__/data.json +6 -0
  9. package/examples/default.json +7 -36
  10. package/examples/legend-issue.json +1 -1
  11. package/examples/minimal-example.json +34 -0
  12. package/examples/private/dengue.json +4640 -0
  13. package/examples/private/inline-markup.json +775 -0
  14. package/examples/private/link_to_file.json +16662 -0
  15. package/examples/private/recent-update.json +1456 -0
  16. package/examples/private/toggle.json +10137 -0
  17. package/examples/sankey.json +3 -3
  18. package/examples/test-api-filter-reset.json +4 -4
  19. package/examples/tp5-test.json +86 -4
  20. package/package.json +9 -9
  21. package/src/CdcDashboard.tsx +2 -1
  22. package/src/CdcDashboardComponent.tsx +48 -28
  23. package/src/_stories/Dashboard.DataSetup.stories.tsx +6 -1
  24. package/src/_stories/Dashboard.Pages.smoke.stories.tsx +22 -0
  25. package/src/_stories/Dashboard.smoke.stories.tsx +33 -0
  26. package/src/_stories/Dashboard.stories.tsx +4523 -83
  27. package/src/_stories/_mock/dashboard-data-driven-colors.json +171 -0
  28. package/src/_stories/_mock/tab-simple-filter.json +153 -0
  29. package/src/_stories/_mock/tp5-test.json +86 -5
  30. package/src/components/DashboardEditors.tsx +15 -0
  31. package/src/components/DashboardFilters/DashboardFilters.test.tsx +129 -0
  32. package/src/components/DashboardFilters/DashboardFilters.tsx +29 -10
  33. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +12 -8
  34. package/src/components/DashboardFilters/DashboardFiltersEditor/components/APIModal.tsx +6 -4
  35. package/src/components/DashboardFilters/DashboardFiltersEditor/components/DeleteFilterModal.tsx +59 -58
  36. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.test.tsx +127 -0
  37. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +29 -6
  38. package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +10 -9
  39. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +8 -8
  40. package/src/components/DashboardFilters/_stories/DashboardFilters.stories.tsx +1 -1
  41. package/src/components/DashboardFilters/dashboardfilter.styles.css +3 -3
  42. package/src/components/DataDesignerModal.tsx +2 -2
  43. package/src/components/ExpandCollapseButtons.tsx +6 -4
  44. package/src/components/Grid.tsx +4 -3
  45. package/src/components/Header/Header.tsx +27 -5
  46. package/src/components/Header/index.scss +1 -1
  47. package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +141 -140
  48. package/src/components/MultiConfigTabs/multiconfigtabs.styles.css +6 -6
  49. package/src/components/Row.tsx +30 -8
  50. package/src/components/Toggle/toggle-style.css +7 -7
  51. package/src/components/VisualizationRow.tsx +81 -22
  52. package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +2 -55
  53. package/src/components/VisualizationsPanel/visualizations-panel-styles.css +2 -2
  54. package/src/components/Widget/Widget.tsx +7 -6
  55. package/src/components/Widget/widget.styles.css +48 -17
  56. package/src/data/initial-state.js +2 -1
  57. package/src/helpers/addVisualization.ts +73 -0
  58. package/src/helpers/formatConfigBeforeSave.ts +1 -1
  59. package/src/helpers/getVizConfig.ts +13 -3
  60. package/src/helpers/iconHash.tsx +45 -36
  61. package/src/helpers/processDataLegacy.ts +19 -14
  62. package/src/helpers/tests/addVisualization.test.ts +52 -0
  63. package/src/helpers/tests/formatConfigBeforeSave.test.ts +81 -1
  64. package/src/scss/editor-panel.scss +1 -1
  65. package/src/scss/grid.scss +38 -8
  66. package/src/scss/main.scss +237 -40
  67. package/src/store/dashboard.reducer.ts +2 -1
  68. package/src/test/CdcDashboard.test.jsx +26 -2
  69. package/src/test/CdcDashboardComponent.test.tsx +74 -0
  70. package/src/types/FilterStyles.ts +2 -1
  71. package/src/types/SharedFilter.ts +1 -0
  72. package/tests/fixtures/dashboard-config-with-metadata.json +89 -0
  73. package/vite.config.js +2 -2
  74. package/dist/cdcdashboard-Cf9_fbQf.es.js +0 -6
  75. package/examples/DEV-6574.json +0 -2224
  76. package/examples/api-dashboard-data.json +0 -272
  77. package/examples/api-dashboard-years.json +0 -11
  78. package/examples/api-geographies-data.json +0 -11
  79. package/examples/chart-data.json +0 -5409
  80. package/examples/custom/css/respiratory.css +0 -236
  81. package/examples/custom/js/respiratory.js +0 -242
  82. package/examples/default-data.json +0 -368
  83. package/examples/default-filter-control.json +0 -209
  84. package/examples/default-multi-dataset-shared-filter.json +0 -1729
  85. package/examples/default-multi-dataset.json +0 -506
  86. package/examples/ed-visits-county-file.json +0 -402
  87. package/examples/filters/Alabama.json +0 -72
  88. package/examples/filters/Alaska.json +0 -1737
  89. package/examples/filters/Arkansas.json +0 -4713
  90. package/examples/filters/California.json +0 -212
  91. package/examples/filters/Colorado.json +0 -1500
  92. package/examples/filters/Connecticut.json +0 -559
  93. package/examples/filters/Delaware.json +0 -63
  94. package/examples/filters/DistrictofColumbia.json +0 -63
  95. package/examples/filters/Florida.json +0 -4217
  96. package/examples/filters/States.json +0 -146
  97. package/examples/state-level.json +0 -90136
  98. package/examples/state-points.json +0 -10474
  99. package/examples/temp-example-data.json +0 -130
  100. package/examples/test-dashboard-simple.json +0 -503
  101. package/examples/test-example.json +0 -752
  102. package/examples/test-file.json +0 -147
  103. package/examples/test.json +0 -752
  104. package/examples/testing.json +0 -94456
  105. /package/examples/{legend-issue-data.json → __data__/legend-issue-data.json} +0 -0
  106. /package/examples/api-test/{categories.json → __data__/categories.json} +0 -0
  107. /package/examples/api-test/{chart-data.json → __data__/chart-data.json} +0 -0
  108. /package/examples/api-test/{topics.json → __data__/topics.json} +0 -0
  109. /package/examples/api-test/{years.json → __data__/years.json} +0 -0
@@ -4238,7 +4238,7 @@
4238
4238
  }
4239
4239
  ],
4240
4240
  "dataFileSize": 72,
4241
- "dataFileName": "data.json",
4241
+ "dataFileName": "__data__/data.json",
4242
4242
  "dataFileSourceType": "file",
4243
4243
  "dataFileFormat": "JSON",
4244
4244
  "preview": false
@@ -4251,7 +4251,7 @@
4251
4251
  }
4252
4252
  ],
4253
4253
  "dataFileSize": 59,
4254
- "dataFileName": "data-2.json",
4254
+ "dataFileName": "__data__/data-2.json",
4255
4255
  "dataFileSourceType": "file",
4256
4256
  "dataFileFormat": "JSON",
4257
4257
  "preview": false
@@ -5215,4 +5215,4 @@
5215
5215
  "type": "dashboard",
5216
5216
  "uuid": 1707343065225,
5217
5217
  "runtime": {}
5218
- }
5218
+ }
@@ -7,7 +7,7 @@
7
7
  "type": "urlfilter",
8
8
  "columnName": "category",
9
9
  "apiFilter": {
10
- "apiEndpoint": "/examples/api-test/categories.json",
10
+ "apiEndpoint": "/examples/api-test/__data__/categories.json",
11
11
  "valueSelector": "category",
12
12
  "textSelector": "category"
13
13
  },
@@ -24,7 +24,7 @@
24
24
  "type": "urlfilter",
25
25
  "columnName": "topic",
26
26
  "apiFilter": {
27
- "apiEndpoint": "/examples/api-test/topics.json",
27
+ "apiEndpoint": "/examples/api-test/__data__/topics.json",
28
28
  "valueSelector": "topic",
29
29
  "textSelector": "topic"
30
30
  },
@@ -43,7 +43,7 @@
43
43
  "type": "urlfilter",
44
44
  "columnName": "year",
45
45
  "apiFilter": {
46
- "apiEndpoint": "/examples/api-test/years.json",
46
+ "apiEndpoint": "/examples/api-test/__data__/years.json",
47
47
  "valueSelector": "year",
48
48
  "textSelector": "year"
49
49
  },
@@ -128,7 +128,7 @@
128
128
  },
129
129
  "datasets": {
130
130
  "chartData": {
131
- "dataUrl": "http://localhost:8080/examples/api-test/chart-data.json"
131
+ "dataUrl": "http://localhost:8080/examples/api-test/__data__/chart-data.json"
132
132
  }
133
133
  },
134
134
  "filterBehavior": "Apply Button",
@@ -40,13 +40,23 @@
40
40
  { "width": 4, "widget": "gauge1" },
41
41
  { "width": 4, "widget": "gauge2" },
42
42
  { "width": 4, "widget": "gauge3" }
43
+ ],
44
+ [
45
+ { "width": 4, "widget": "markup1" },
46
+ { "width": 4, "widget": "markup2" },
47
+ { "width": 4, "widget": "markup3" }
48
+ ],
49
+ [
50
+ { "width": 4, "widget": "waffle2" },
51
+ { "width": 4, "widget": "bite3" },
52
+ { "width": 4, "widget": "markup2" }
43
53
  ]
44
54
  ],
45
55
  "visualizations": {
46
56
  "waffle1": {
47
57
  "uid": "waffle1",
48
58
  "type": "waffle-chart",
49
- "title": "Vaccination Coverage",
59
+ "title": "Vaccination",
50
60
  "visualizationType": "TP5 Waffle",
51
61
  "visualizationSubType": "linear",
52
62
  "showPercent": true,
@@ -71,7 +81,7 @@
71
81
  "waffle2": {
72
82
  "uid": "waffle2",
73
83
  "type": "waffle-chart",
74
- "title": "Health Insurance Coverage Rate",
84
+ "title": "Health",
75
85
  "visualizationType": "TP5 Waffle",
76
86
  "visualizationSubType": "linear",
77
87
  "showPercent": true,
@@ -96,7 +106,7 @@
96
106
  "waffle3": {
97
107
  "uid": "waffle3",
98
108
  "type": "waffle-chart",
99
- "title": "Cancer Screening Completion",
109
+ "title": "Cancer",
100
110
  "visualizationType": "TP5 Waffle",
101
111
  "visualizationSubType": "linear",
102
112
  "showPercent": true,
@@ -245,7 +255,7 @@
245
255
  "bite3": {
246
256
  "uid": "bite3",
247
257
  "type": "data-bite",
248
- "title": "Cancer Screening Completion",
258
+ "title": "Cancer Screening Completion, and a little something else to make the title taller",
249
259
  "biteStyle": "tp5",
250
260
  "dataColumn": "Screening Rate",
251
261
  "dataFunction": "Mean (Average)",
@@ -261,6 +271,78 @@
261
271
  "visual": {
262
272
  "hideBackgroundColor": false
263
273
  }
274
+ },
275
+ "markup1": {
276
+ "uid": "markup1",
277
+ "type": "markup-include",
278
+ "title": "",
279
+ "theme": "theme-blue",
280
+ "contentEditor": {
281
+ "style": "tp5",
282
+ "title": "TP5 Markup Include",
283
+ "titleStyle": "small",
284
+ "useInlineHTML": true,
285
+ "inlineHTML": "<p><strong>68.5%</strong> of adults were vaccinated against seasonal flu in this sample dataset.</p>",
286
+ "srcUrl": "#example",
287
+ "showHeader": true,
288
+ "showNoDataMessage": false,
289
+ "noDataMessageText": "No Data Available"
290
+ },
291
+ "visual": {
292
+ "border": true,
293
+ "accent": false,
294
+ "whiteBackground": false,
295
+ "hideBackgroundColor": false,
296
+ "borderColorTheme": false
297
+ }
298
+ },
299
+ "markup2": {
300
+ "uid": "markup2",
301
+ "type": "markup-include",
302
+ "title": "",
303
+ "theme": "theme-teal",
304
+ "contentEditor": {
305
+ "style": "tp5",
306
+ "title": "White Background TP5",
307
+ "titleStyle": "small",
308
+ "useInlineHTML": true,
309
+ "inlineHTML": "<p>This TP5 markup include uses white-background styling with border enabled for comparison.</p>",
310
+ "srcUrl": "#example",
311
+ "showHeader": true,
312
+ "showNoDataMessage": false,
313
+ "noDataMessageText": "No Data Available"
314
+ },
315
+ "visual": {
316
+ "border": true,
317
+ "accent": false,
318
+ "whiteBackground": true,
319
+ "hideBackgroundColor": false,
320
+ "borderColorTheme": false
321
+ }
322
+ },
323
+ "markup3": {
324
+ "uid": "markup3",
325
+ "type": "markup-include",
326
+ "title": "",
327
+ "theme": "theme-purple",
328
+ "contentEditor": {
329
+ "style": "tp5",
330
+ "title": "TP5 Markup Include (No White BG)",
331
+ "titleStyle": "small",
332
+ "useInlineHTML": true,
333
+ "inlineHTML": "<p>This TP5 markup include uses the standard background and border settings for side-by-side alignment testing.</p>",
334
+ "srcUrl": "#example",
335
+ "showHeader": true,
336
+ "showNoDataMessage": false,
337
+ "noDataMessageText": "No Data Available"
338
+ },
339
+ "visual": {
340
+ "border": true,
341
+ "accent": false,
342
+ "whiteBackground": false,
343
+ "hideBackgroundColor": false,
344
+ "borderColorTheme": false
345
+ }
264
346
  }
265
347
  }
266
348
  }
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@cdc/dashboard",
3
- "version": "4.26.2",
3
+ "version": "4.26.4",
4
4
  "description": "React component for combining multiple visualizations into a single dashboard",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Matthew Pallansch <mpallansch@adittech.com>",
7
7
  "bugs": "https://github.com/CDCgov/cdc-open-viz/issues",
8
8
  "dependencies": {
9
- "@cdc/chart": "^4.26.2",
10
- "@cdc/core": "^4.26.2",
11
- "@cdc/data-bite": "^4.26.2",
12
- "@cdc/filtered-text": "^4.26.2",
13
- "@cdc/map": "^4.26.2",
14
- "@cdc/markup-include": "^4.26.2",
15
- "@cdc/waffle-chart": "^4.26.2",
9
+ "@cdc/chart": "^4.26.4",
10
+ "@cdc/core": "^4.26.4",
11
+ "@cdc/data-bite": "^4.26.4",
12
+ "@cdc/filtered-text": "^4.26.4",
13
+ "@cdc/map": "^4.26.4",
14
+ "@cdc/markup-include": "^4.26.4",
15
+ "@cdc/waffle-chart": "^4.26.4",
16
16
  "js-base64": "^2.5.2",
17
17
  "react-accessible-accordion": "^5.0.1",
18
18
  "react-dnd": "^16.0.1",
@@ -26,7 +26,7 @@
26
26
  "vite-plugin-css-injected-by-js": "^2.4.0",
27
27
  "vite-plugin-svgr": "^4.2.0"
28
28
  },
29
- "gitHead": "be3413e8e1149abf94225108f86a7910f56e0616",
29
+ "gitHead": "6097de1ff814001880d9ac64bd66becdc092d63c",
30
30
  "main": "dist/cdcdashboard",
31
31
  "moduleName": "CdcDashboard",
32
32
  "peerDependencies": {
@@ -91,11 +91,12 @@ const MultiDashboardWrapper: React.FC<MultiDashboardProps> = ({
91
91
  return prepareDatasets(newConfig)
92
92
  } else {
93
93
  const dataKey = newConfig.dataFileName || 'backwards-compatibility'
94
- const data = await processDataLegacy(config)
94
+ const { data, dataMetadata } = await processDataLegacy(config)
95
95
 
96
96
  const datasetsFull = {}
97
97
  datasetsFull[dataKey] = {
98
98
  data,
99
+ dataMetadata,
99
100
  dataDescription: newConfig.dataDescription
100
101
  }
101
102
  newConfig.datasets = datasetsFull
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useCallback, useMemo, useReducer, useContext } from 'react'
1
+ import React, { useState, useEffect, useCallback, useMemo, useReducer, useContext, useRef } from 'react'
2
2
  import 'whatwg-fetch'
3
3
  import ResizeObserver from 'resize-observer-polyfill'
4
4
 
@@ -39,7 +39,9 @@ import { type SharedFilter } from './types/SharedFilter'
39
39
  import { type WCMSProps } from '@cdc/core/types/WCMSProps'
40
40
  import { type InitialState } from './types/InitialState'
41
41
  import MultiTabs from './components/MultiConfigTabs'
42
- import _ from 'lodash'
42
+ import cloneDeep from 'lodash/cloneDeep'
43
+ import pick from 'lodash/pick'
44
+ import pickBy from 'lodash/pickBy'
43
45
  import EditorContext from '@cdc/core/contexts/EditorContext'
44
46
  import { APIFilterDropdowns } from './components/DashboardFilters'
45
47
  import { ViewPort } from '@cdc/core/types/ViewPort'
@@ -47,7 +49,7 @@ import VisualizationRow from './components/VisualizationRow'
47
49
  import { getVizConfig } from './helpers/getVizConfig'
48
50
  import { getFilteredData } from './helpers/getFilteredData'
49
51
  import { getVizRowColumnLocator } from './helpers/getVizRowColumnLocator'
50
- import Layout from '@cdc/core/components/Layout'
52
+ import { Responsive, VisualizationContainer } from '@cdc/core/components/Layout'
51
53
  import * as reloadURLHelpers from './helpers/reloadURLHelpers'
52
54
  import { addValuesToDashboardFilters } from './helpers/addValuesToDashboardFilters'
53
55
  import { DashboardFilters } from './types/DashboardFilters'
@@ -207,9 +209,9 @@ export default function CdcDashboard({
207
209
 
208
210
  setAPILoading(true)
209
211
  await fetchRemoteData(dataUrlFinal)
210
- .then(responseData => {
211
- let data: any[] = responseData
212
- if (responseData && dataset.dataDescription) {
212
+ .then(({ data: fetchedData, dataMetadata }) => {
213
+ let data: any[] = fetchedData
214
+ if (data && dataset.dataDescription) {
213
215
  try {
214
216
  data = transform.autoStandardize(data)
215
217
  data = transform.developerStandardize(data, dataset.dataDescription)
@@ -219,6 +221,7 @@ export default function CdcDashboard({
219
221
  }
220
222
  }
221
223
  newDatasets[datasetKey].data = data
224
+ newDatasets[datasetKey].dataMetadata = dataMetadata
222
225
  newDatasets[datasetKey].runtimeDataUrl = dataUrlFinal
223
226
  newData[datasetKey] = data
224
227
  })
@@ -236,7 +239,7 @@ export default function CdcDashboard({
236
239
  }
237
240
  }
238
241
 
239
- const datasetsWithFiles = _.pickBy(newDatasets, dataset => !dataset.dataUrl)
242
+ const datasetsWithFiles = pickBy(newDatasets, dataset => !dataset.dataUrl)
240
243
 
241
244
  if (dataWasFetched || Object.keys(datasetsWithFiles).length) {
242
245
  const dataFiles = Object.keys(datasetsWithFiles).reduce((acc, key) => {
@@ -291,7 +294,7 @@ export default function CdcDashboard({
291
294
  }
292
295
 
293
296
  const setSharedFilter = (key, datum) => {
294
- const { config: newConfig, filteredData } = _.cloneDeep(state)
297
+ const { config: newConfig, filteredData } = cloneDeep(state)
295
298
 
296
299
  for (let i = 0; i < newConfig.dashboard.sharedFilters.length; i++) {
297
300
  const filter = newConfig.dashboard.sharedFilters[i]
@@ -319,7 +322,7 @@ export default function CdcDashboard({
319
322
  return acc
320
323
  }, {})
321
324
  const newConfig = { ...state, data: { ...data, ...newDatasets } }
322
- const newFilteredData = getFilteredData(newConfig, _.cloneDeep(filteredData))
325
+ const newFilteredData = getFilteredData(newConfig, cloneDeep(filteredData))
323
326
  dispatch({ type: 'SET_FILTERED_DATA', payload: newFilteredData })
324
327
  dispatch({ type: 'SET_DATA', payload: { ...data, ...newDatasets } })
325
328
  } catch (e) {
@@ -375,14 +378,14 @@ export default function CdcDashboard({
375
378
 
376
379
  const updateChildConfig = (visualizationKey, newConfig) => {
377
380
  const config = cloneConfig(state.config)
378
- const updatedConfig = _.pick(config, ['visualizations', 'multiDashboards'])
381
+ const updatedConfig = pick(config, ['visualizations', 'multiDashboards'])
379
382
  updatedConfig.visualizations[visualizationKey] = newConfig
380
383
  updatedConfig.visualizations[visualizationKey].formattedData = config.visualizations[visualizationKey].formattedData
381
384
  if (config.multiDashboards) {
382
385
  const activeDashboard = config.activeDashboard
383
386
  const multiDashboards = [...config.multiDashboards]
384
387
  const label = multiDashboards[activeDashboard].label
385
- const toSave = { label, visualizations: updatedConfig.visualizations, ..._.pick(config, ['dashboard', 'rows']) }
388
+ const toSave = { label, visualizations: updatedConfig.visualizations, ...pick(config, ['dashboard', 'rows']) }
386
389
  multiDashboards[activeDashboard] = toSave
387
390
  updatedConfig.multiDashboards = multiDashboards
388
391
  }
@@ -394,17 +397,31 @@ export default function CdcDashboard({
394
397
  }
395
398
  }
396
399
 
397
- const resizeObserver = new ResizeObserver(entries => {
398
- for (let entry of entries) {
399
- let newViewport = getViewport(entry.contentRect.width)
400
+ const resizeObserverRef = useRef<ResizeObserver | null>(null)
400
401
 
401
- setCurrentViewport(newViewport)
402
+ const outerContainerRef = useCallback(node => {
403
+ if (resizeObserverRef.current) {
404
+ resizeObserverRef.current.disconnect()
405
+ resizeObserverRef.current = null
402
406
  }
403
- })
404
407
 
405
- const outerContainerRef = useCallback(node => {
406
408
  if (node !== null) {
407
- resizeObserver.observe(node)
409
+ resizeObserverRef.current = new ResizeObserver(entries => {
410
+ for (let entry of entries) {
411
+ const newViewport = getViewport(entry.contentRect.width)
412
+ setCurrentViewport(newViewport)
413
+ }
414
+ })
415
+ resizeObserverRef.current.observe(node)
416
+ }
417
+ }, [])
418
+
419
+ useEffect(() => {
420
+ return () => {
421
+ if (resizeObserverRef.current) {
422
+ resizeObserverRef.current.disconnect()
423
+ resizeObserverRef.current = null
424
+ }
408
425
  }
409
426
  }, [])
410
427
 
@@ -497,7 +514,7 @@ export default function CdcDashboard({
497
514
  autoDismiss={true}
498
515
  />
499
516
  ))}
500
- <Layout.Responsive isEditor={isEditor}>
517
+ <Responsive isEditor={isEditor}>
501
518
  <div className={`cdc-dashboard-inner-container${isEditor ? ' is-editor' : ''}`}>
502
519
  <Title
503
520
  title={title}
@@ -506,7 +523,7 @@ export default function CdcDashboard({
506
523
  classes={[`dashboard-title`, `${config.dashboard.theme ?? 'theme-blue'}`]}
507
524
  />
508
525
  {/* Description */}
509
- {description && <div className='subtext mb-4'>{parse(description)}</div>}
526
+ {description && <div className='subtext cove-prose mb-4'>{parse(description)}</div>}
510
527
  {/* Visualizations */}
511
528
  {filteredRows?.map((row, index) => (
512
529
  <VisualizationRow
@@ -640,16 +657,11 @@ export default function CdcDashboard({
640
657
  })
641
658
  .filter(Boolean)}
642
659
  </div>
643
- </Layout.Responsive>
660
+ </Responsive>
644
661
  </>
645
662
  )
646
663
  }
647
664
 
648
- const dashboardContainerClasses = ['cdc-open-viz-module', 'type-dashboard', `${currentViewport}`]
649
- if (isEditor) {
650
- dashboardContainerClasses.push('isDashboardEditor')
651
- }
652
-
653
665
  return (
654
666
  <GlobalContextProvider>
655
667
  <DashboardContext.Provider
@@ -666,9 +678,17 @@ export default function CdcDashboard({
666
678
  }}
667
679
  >
668
680
  <DashboardDispatchContext.Provider value={dispatch}>
669
- <div className={dashboardContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId}>
681
+ <VisualizationContainer
682
+ className={isEditor ? 'is-dashboard-editor' : undefined}
683
+ config={state.config}
684
+ currentViewport={currentViewport}
685
+ imageId={imageId}
686
+ isEditor={false}
687
+ ref={outerContainerRef}
688
+ renderResponsive={false}
689
+ >
670
690
  {body}
671
- </div>
691
+ </VisualizationContainer>
672
692
  <OverlayFrame />
673
693
  </DashboardDispatchContext.Provider>
674
694
  </DashboardContext.Provider>
@@ -159,7 +159,12 @@ export const MultiVizConfigurationWorkflow: Story = {
159
159
  // ========================================================================
160
160
  // STEP 11: Click "Vertical Values for map" button
161
161
  // ========================================================================
162
- await sleep(500)
162
+ await waitFor(
163
+ () => {
164
+ expect(canvas.getByRole('button', { name: /vertical values for map/i })).toBeTruthy()
165
+ },
166
+ { timeout: 5000 }
167
+ )
163
168
 
164
169
  const verticalValuesButton = canvas.getByRole('button', { name: /vertical values for map/i })
165
170
  await user.click(verticalValuesButton)
@@ -0,0 +1,22 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
3
+ import Dashboard from '../CdcDashboard'
4
+
5
+ const meta: Meta<typeof Dashboard> = {
6
+ title: 'Pages/NWSS',
7
+ component: Dashboard
8
+ }
9
+
10
+ export default meta
11
+ type Story = StoryObj<typeof Dashboard>
12
+
13
+ export const FluA_Top_Modules: Story = {
14
+ name: 'FluA Top Modules',
15
+ args: {
16
+ configUrl: 'https://www.cdc.gov/nwss/rv/modules/flua/flua-top-modules.json',
17
+ isEditor: false
18
+ },
19
+ play: async ({ canvasElement }) => {
20
+ await assertVisualizationRendered(canvasElement)
21
+ }
22
+ }
@@ -0,0 +1,33 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { expect } from 'storybook/test'
3
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
4
+ import Dashboard from '../CdcDashboard'
5
+ import MinimalExampleConfig from '../../examples/minimal-example.json'
6
+
7
+ const meta: Meta<typeof Dashboard> = {
8
+ title: 'Components/Pages/Dashboard',
9
+ component: Dashboard
10
+ }
11
+
12
+ export default meta
13
+ type Story = StoryObj<typeof Dashboard>
14
+
15
+ export const Dashboard_Minimal_Config: Story = {
16
+ args: {
17
+ config: MinimalExampleConfig,
18
+ isEditor: false
19
+ },
20
+ parameters: {
21
+ docs: {
22
+ description: {
23
+ story:
24
+ 'Minimum working consumer config. This story validates the source-of-truth minimal example used by the package README and CONFIG reference.'
25
+ }
26
+ }
27
+ },
28
+ play: async ({ canvasElement }) => {
29
+ await assertVisualizationRendered(canvasElement)
30
+ expect(canvasElement.textContent).toContain('Dashboard Example')
31
+ expect(canvasElement.textContent).toContain('Minimal dashboard example')
32
+ }
33
+ }