@cdc/dashboard 4.24.7 → 4.24.9-1

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 (41) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcdashboard.js +131733 -123191
  3. package/examples/single-state-dashboard-filters.json +421 -0
  4. package/examples/state-level.json +90136 -0
  5. package/examples/state-points.json +10474 -0
  6. package/examples/test-file.json +147 -0
  7. package/examples/testing.json +94456 -0
  8. package/index.html +18 -6
  9. package/package.json +9 -9
  10. package/src/CdcDashboardComponent.tsx +154 -90
  11. package/src/DashboardContext.tsx +7 -1
  12. package/src/_stories/Dashboard.stories.tsx +124 -10
  13. package/src/_stories/_mock/api-filter-map.json +1 -1
  14. package/src/_stories/_mock/bump-chart.json +3554 -0
  15. package/src/_stories/_mock/methodology.json +412 -0
  16. package/src/_stories/_mock/methodologyAPI.ts +90 -0
  17. package/src/_stories/_mock/multi-viz.json +1 -1
  18. package/src/_stories/_mock/single-state-dashboard-filters.json +390 -0
  19. package/src/components/DashboardFilters/DashboardFilters.tsx +39 -17
  20. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +2 -2
  21. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +141 -31
  22. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +66 -18
  23. package/src/components/Header/Header.tsx +0 -5
  24. package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +20 -8
  25. package/src/components/Row.tsx +1 -1
  26. package/src/components/VisualizationRow.tsx +98 -17
  27. package/src/components/Widget.tsx +1 -0
  28. package/src/helpers/FilterBehavior.ts +4 -0
  29. package/src/helpers/addValuesToDashboardFilters.ts +49 -0
  30. package/src/helpers/apiFilterHelpers.ts +70 -18
  31. package/src/helpers/changeFilterActive.ts +17 -8
  32. package/src/helpers/getFilteredData.ts +4 -4
  33. package/src/helpers/iconHash.tsx +2 -0
  34. package/src/helpers/loadAPIFilters.ts +74 -0
  35. package/src/helpers/reloadURLHelpers.ts +41 -7
  36. package/src/helpers/tests/addValuesToDashboardFilters.test.ts +44 -0
  37. package/src/helpers/tests/apiFilterHelpers.test.ts +155 -0
  38. package/src/helpers/tests/getFilteredData.test.ts +86 -0
  39. package/src/helpers/tests/loadAPIFiltersWrapper.test.ts +220 -0
  40. package/src/helpers/tests/reloadURLHelpers.test.ts +232 -0
  41. package/src/types/SharedFilter.ts +2 -1
package/index.html CHANGED
@@ -17,24 +17,36 @@
17
17
  }
18
18
  </style>
19
19
  <link rel="stylesheet prefetch" href="examples/custom/css/respiratory.css" />
20
- <link rel="stylesheet prefetch" href="https://www.cdc.gov/TemplatePackage/contrib/libs/bootstrap/latest/css/bootstrap.min.css?_=39423" />
20
+ <link
21
+ rel="stylesheet prefetch"
22
+ href="https://www.cdc.gov/TemplatePackage/contrib/libs/bootstrap/latest/css/bootstrap.min.css?_=39423"
23
+ />
21
24
  <link rel="stylesheet prefetch" href="https://www.cdc.gov/TemplatePackage/4.0/assets/css/app.min.css?_=39423" />
22
25
  </head>
23
26
 
24
27
  <body>
25
- <div class="react-container" data-config="/examples/dev-8332.json"></div>
26
- <div class="react-container" data-config="/examples/default-multi-dataset.json"></div>
28
+ <!-- <div class="react-container" data-config="/examples/chart-data-2.json"></div> -->
29
+ <!-- <div class="react-container" data-config="/examples/bump-chart.json"></div> -->
30
+ <!-- <div class="react-container" data-config="/examples/full-dashboard.json"></div> -->
31
+ <!-- <div class="react-container" data-config="/examples/dashboard-gallery.json"></div> -->
32
+ <!-- <div class="react-container" data-config="/examples/dev-8332.json"></div> -->
33
+ <!-- <div class="react-container" data-config="/examples/default-multi-dataset.json"></div> -->
34
+ <!-- <div class="react-container" data-config="/examples/dev-8332.json"></div> -->
27
35
  <!-- <div class="react-container" data-config="/examples/dashboard-gallery.json"></div> -->
28
36
  <!-- <div class="react-container" data-config="/examples/filtered-dash.json"></div> -->
29
- <!-- <div class="react-container" data-config="/examples/all-components.json"></div> -->
37
+ <div class="react-container" data-config="/examples/all-components.json"></div>
30
38
  <!-- <div class="react-container" data-config="/examples/sankey.json"></div> -->
31
39
  <!-- <div class="react-container" data-config="/examples/DEV-6574.json"></div> -->
32
40
  <!-- <div class="react-container" data-config="/examples/default-multi-dataset-2.json"></div> -->
33
- <!-- <div class="react-container" data-config="/examples/default-multi-dataset.json"></div> -->
41
+ <div class="react-container" data-config="/examples/default-multi-dataset.json"></div>
34
42
  <!-- <div class="react-container" data-config="/examples/default-filter-control.json"></div> -->
35
43
 
36
44
  <!-- Respiratory custom example -->
37
- <div class="react-container wcms-viz-container" data-config-url="/respiratory-viruses/modules/ed-visits-county-file.json" data-config="/examples/ed-visits-county-file.json"></div>
45
+ <div
46
+ class="react-container wcms-viz-container"
47
+ data-config-url="/respiratory-viruses/modules/ed-visits-county-file.json"
48
+ data-config="/examples/ed-visits-county-file.json"
49
+ ></div>
38
50
 
39
51
  <script src="https://www.cdc.gov/TemplatePackage/contrib/libs/jquery/latest/jquery.min.js?_=91329"></script>
40
52
  <!-- <script src="examples/custom/js/respiratory.js"></script> -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cdc/dashboard",
3
- "version": "4.24.7",
3
+ "version": "4.24.9-1",
4
4
  "description": "React component for combining multiple visualizations into a single dashboard",
5
5
  "moduleName": "CdcDashboard",
6
6
  "main": "dist/cdcdashboard",
@@ -27,13 +27,13 @@
27
27
  },
28
28
  "license": "Apache-2.0",
29
29
  "dependencies": {
30
- "@cdc/chart": "^4.24.7",
31
- "@cdc/core": "^4.24.7",
32
- "@cdc/data-bite": "^4.24.7",
33
- "@cdc/filtered-text": "^4.24.7",
34
- "@cdc/map": "^4.24.7",
35
- "@cdc/markup-include": "^4.24.7",
36
- "@cdc/waffle-chart": "^4.24.7",
30
+ "@cdc/chart": "^4.24.9-1",
31
+ "@cdc/core": "^4.24.9-1",
32
+ "@cdc/data-bite": "^4.24.9-1",
33
+ "@cdc/filtered-text": "^4.24.9-1",
34
+ "@cdc/map": "^4.24.9-1",
35
+ "@cdc/markup-include": "^4.24.9-1",
36
+ "@cdc/waffle-chart": "^4.24.9-1",
37
37
  "html-react-parser": "^3.0.8",
38
38
  "js-base64": "^2.5.2",
39
39
  "papaparse": "^5.3.0",
@@ -49,5 +49,5 @@
49
49
  "react": "^18.2.0",
50
50
  "react-dom": "^18.2.0"
51
51
  },
52
- "gitHead": "7a7372d03778cb977e0e92e3b3bc1a35f8a5bfaf"
52
+ "gitHead": "4a77c2fa79c8fa6074da3b6dfee3d8e32f0b2586"
53
53
  }
@@ -58,13 +58,13 @@ import { getFilteredData } from './helpers/getFilteredData'
58
58
  import { getVizRowColumnLocator } from './helpers/getVizRowColumnLocator'
59
59
  import Layout from '@cdc/core/components/Layout'
60
60
  import FootnotesStandAlone from '@cdc/core/components/Footnotes/FootnotesStandAlone'
61
- import * as apiFilterHelpers from './helpers/apiFilterHelpers'
62
61
  import * as reloadURLHelpers from './helpers/reloadURLHelpers'
63
- import { addValuesToFilters } from '@cdc/core/helpers/addValuesToFilters'
62
+ import { addValuesToDashboardFilters } from './helpers/addValuesToDashboardFilters'
64
63
  import { DashboardFilters } from './types/DashboardFilters'
65
64
  import DashboardSharedFilters from './components/DashboardFilters'
66
65
  import ExpandCollapseButtons from './components/ExpandCollapseButtons'
67
66
  import { hasDashboardApplyBehavior } from './helpers/hasDashboardApplyBehavior'
67
+ import { loadAPIFiltersFactory } from './helpers/loadAPIFilters'
68
68
 
69
69
  type DashboardProps = Omit<WCMSProps, 'configUrl'> & {
70
70
  initialState: InitialState
@@ -96,64 +96,7 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
96
96
  .reduce((acc, viz: DashboardFilters) => (viz.autoLoad ? [...acc, ...viz.sharedFilterIndexes] : acc), [])
97
97
  }, [state.config.visualizations])
98
98
 
99
- const setAutoLoadDefaultValue = (sharedFilterIndex: number, dropdownOptions: DropdownOptions, sharedFilters): SharedFilter => {
100
- const sharedFilter = _.cloneDeep(sharedFilters[sharedFilterIndex])
101
- if (!autoLoadFilterIndexes.length || !dropdownOptions) return sharedFilter // no autoLoading happening
102
- if (!sharedFilter.active && autoLoadFilterIndexes.includes(sharedFilterIndex)) {
103
- const filterParents = sharedFilters.filter(f => sharedFilter.parents?.includes(f.key))
104
- const notAllParentFiltersSelected = filterParents.some(p => !(p.active || p.queuedActive))
105
- if (filterParents && notAllParentFiltersSelected) return sharedFilter
106
- // TODO get default value from query parameter
107
- const defaultValue = dropdownOptions[0].value
108
- sharedFilter.active = defaultValue
109
- }
110
- return sharedFilter
111
- }
112
-
113
- const loadAPIFilters = (sharedFilters: SharedFilter[], dropdowns = apiFilterDropdowns, recursiveLimit = 3): Promise<SharedFilter[]> => {
114
- if (!sharedFilters) return
115
- sharedFilters = sharedFilters.map((filter, index) => setAutoLoadDefaultValue(index, dropdowns[filter.apiFilter?.apiEndpoint], sharedFilters))
116
- const sharedAPIFilters = sharedFilters.filter(f => f.apiFilter)
117
- const loadingFilterMemo = apiFilterHelpers.getLoadingFilterMemo(sharedAPIFilters, dropdowns)
118
- setAPIFilterDropdowns({ ...dropdowns, ...loadingFilterMemo })
119
- const filterLookup = new Map(sharedAPIFilters.map(filter => [filter.apiFilter.apiEndpoint, filter.apiFilter]))
120
- const toFetch = apiFilterHelpers.getToFetch(sharedAPIFilters, dropdowns)
121
- const newDropdowns = _.cloneDeep(dropdowns)
122
- return Promise.all(
123
- Object.keys(toFetch).map(
124
- endpoint =>
125
- new Promise<void>(resolve => {
126
- fetch(endpoint)
127
- .then(resp => resp.json())
128
- .then(data => {
129
- const [_key, index] = toFetch[endpoint]
130
- if (!Array.isArray(data)) throw new Error('COVE only supports response data in the shape Array<Object>')
131
- const apiFilter = filterLookup.get(_key) as APIFilter
132
- const _filterValues = apiFilterHelpers.getFilterValues(data, apiFilter)
133
- newDropdowns[_key] = _filterValues
134
- const newDefaultSelectedFilter = setAutoLoadDefaultValue(index, _filterValues, sharedFilters)
135
- sharedFilters[index] = newDefaultSelectedFilter
136
- })
137
- .catch(console.error)
138
- .finally(() => {
139
- resolve()
140
- })
141
- })
142
- )
143
- ).then(() => {
144
- const finishedLoading = sharedFilters.reduce((acc, curr, index) => {
145
- if (autoLoadFilterIndexes.includes(index) && !curr.active) return false
146
- return acc
147
- }, true)
148
- if (finishedLoading || recursiveLimit === 0) {
149
- setAPIFilterDropdowns(dropdowns => ({ ...dropdowns, ...newDropdowns }))
150
- dispatch({ type: 'SET_SHARED_FILTERS', payload: sharedFilters })
151
- return sharedFilters
152
- } else {
153
- return loadAPIFilters(sharedFilters, newDropdowns, recursiveLimit - 1)
154
- }
155
- })
156
- }
99
+ const loadAPIFilters = loadAPIFiltersFactory(dispatch, setAPIFilterDropdowns, autoLoadFilterIndexes)
157
100
 
158
101
  const reloadURLData = async (newFilters?: SharedFilter[]) => {
159
102
  const config = _.cloneDeep(state.config)
@@ -175,8 +118,10 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
175
118
  const currentQSParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
176
119
  const updatedQSParams = {}
177
120
  filters.forEach(filter => {
178
- // filter.active is always a string when filter.type is 'urlfilter'
179
- if (filter.type === 'urlfilter' && !Array.isArray(filter.active)) {
121
+ if (
122
+ filter.type === 'urlfilter' &&
123
+ reloadURLHelpers.filterUsedByDataUrl(filter, datasetKey, config.visualizations)
124
+ ) {
180
125
  if (filter.filterBy === 'File Name') {
181
126
  newFileName = reloadURLHelpers.getNewFileName(newFileName, filter, datasetKey)
182
127
  }
@@ -197,7 +142,11 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
197
142
 
198
143
  if (!!newFilters || reloadURLHelpers.isUpdateNeeded(filters, currentQSParams, updatedQSParams)) {
199
144
  dataWasFetched = true
200
- const dataUrlFinal = reloadURLHelpers.getDataURL({ ...currentQSParams, ...updatedQSParams }, dataUrl, newFileName)
145
+ const dataUrlFinal = reloadURLHelpers.getDataURL(
146
+ { ...currentQSParams, ...updatedQSParams },
147
+ dataUrl,
148
+ newFileName
149
+ )
201
150
 
202
151
  await fetchRemoteData(dataUrlFinal).then(responseData => {
203
152
  let data: any[] = responseData
@@ -219,9 +168,15 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
219
168
 
220
169
  if (dataWasFetched) {
221
170
  dispatch({ type: 'SET_DATA', payload: newData })
222
- const filtersWithNewValues = addValuesToFilters<SharedFilter>(filters, newData)
223
- const dashboardConfig = newFilters ? { ...config.dashboard, sharedFilters: filtersWithNewValues } : config.dashboard
224
- const filteredData = getFilteredData({ ...state, config: { ...state.config, dashboard: dashboardConfig } }, {}, newData)
171
+ const filtersWithNewValues = addValuesToDashboardFilters(filters, newData)
172
+ const dashboardConfig = newFilters
173
+ ? { ...config.dashboard, sharedFilters: filtersWithNewValues }
174
+ : config.dashboard
175
+ const filteredData = getFilteredData(
176
+ { ...state, config: { ...state.config, dashboard: dashboardConfig } },
177
+ {},
178
+ newData
179
+ )
225
180
  dispatch({ type: 'SET_FILTERED_DATA', payload: filteredData })
226
181
  const visualizations = reloadURLHelpers.getVisualizationsWithFormattedData(config.visualizations, newData)
227
182
  dispatch({ type: 'SET_CONFIG', payload: { dashboard: dashboardConfig, datasets: newDatasets, visualizations } })
@@ -265,9 +220,9 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
265
220
  reloadURLData()
266
221
  }
267
222
 
268
- const sharedFiltersWithValues = addValuesToFilters<SharedFilter>(config.dashboard.sharedFilters, state.data)
269
- loadAPIFilters(sharedFiltersWithValues)
270
- updateFilteredData()
223
+ const sharedFiltersWithValues = addValuesToDashboardFilters(config.dashboard.sharedFilters, state.data)
224
+ loadAPIFilters(sharedFiltersWithValues, apiFilterDropdowns)
225
+ updateFilteredData(sharedFiltersWithValues)
271
226
  }, [isEditor, isPreview, state.config?.activeDashboard])
272
227
 
273
228
  const updateChildConfig = (visualizationKey, newConfig) => {
@@ -322,17 +277,29 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
322
277
 
323
278
  getVizKeys(state.config).forEach(visualizationKey => {
324
279
  const rowNumber = vizRowColumnLocator[visualizationKey]?.row
325
- const visualizationConfig = getVizConfig(visualizationKey, rowNumber, state.config, state.data, state.filteredData)
280
+ const visualizationConfig = getVizConfig(
281
+ visualizationKey,
282
+ rowNumber,
283
+ state.config,
284
+ state.data,
285
+ state.filteredData
286
+ )
326
287
  visualizationConfig.uid = visualizationKey
327
288
  if (visualizationConfig.type === 'footnotes') visualizationConfig.formattedData = undefined
328
- const setsSharedFilter = state.config.dashboard.sharedFilters && state.config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === visualizationKey).length > 0
329
- const setSharedFilterValue = setsSharedFilter ? state.config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === visualizationKey)[0].active : undefined
289
+ const setsSharedFilter =
290
+ state.config.dashboard.sharedFilters &&
291
+ state.config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === visualizationKey).length > 0
292
+ const setSharedFilterValue = setsSharedFilter
293
+ ? state.config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === visualizationKey)[0].active
294
+ : undefined
330
295
 
331
296
  if (visualizationConfig.editing) {
332
297
  subVisualizationEditing = true
333
298
 
334
299
  const _updateConfig = newConfig => {
335
- let dataCorrectedConfig = visualizationConfig.originalFormattedData ? { ...newConfig, formattedData: visualizationConfig.originalFormattedData } : newConfig
300
+ let dataCorrectedConfig = visualizationConfig.originalFormattedData
301
+ ? { ...newConfig, formattedData: visualizationConfig.originalFormattedData }
302
+ : newConfig
336
303
  updateChildConfig(visualizationKey, dataCorrectedConfig)
337
304
  }
338
305
 
@@ -382,7 +349,13 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
382
349
  body = (
383
350
  <>
384
351
  <Header visualizationKey={visualizationKey} subEditor='Data Bite' />
385
- <CdcDataBite key={visualizationKey} config={{ ...visualizationConfig, newViz: true }} isEditor={true} setConfig={_updateConfig} isDashboard={true} />
352
+ <CdcDataBite
353
+ key={visualizationKey}
354
+ config={{ ...visualizationConfig, newViz: true }}
355
+ isEditor={true}
356
+ setConfig={_updateConfig}
357
+ isDashboard={true}
358
+ />
386
359
  </>
387
360
  )
388
361
  break
@@ -390,7 +363,14 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
390
363
  body = (
391
364
  <>
392
365
  <Header visualizationKey={visualizationKey} subEditor='Waffle Chart' />
393
- <CdcWaffleChart key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={_updateConfig} isDashboard={true} configUrl={undefined} />
366
+ <CdcWaffleChart
367
+ key={visualizationKey}
368
+ config={visualizationConfig}
369
+ isEditor={true}
370
+ setConfig={_updateConfig}
371
+ isDashboard={true}
372
+ configUrl={undefined}
373
+ />
394
374
  </>
395
375
  )
396
376
  break
@@ -398,7 +378,14 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
398
378
  body = (
399
379
  <>
400
380
  <Header visualizationKey={visualizationKey} subEditor='Markup Include' />
401
- <CdcMarkupInclude key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={_updateConfig} isDashboard={true} configUrl={undefined} />
381
+ <CdcMarkupInclude
382
+ key={visualizationKey}
383
+ config={visualizationConfig}
384
+ isEditor={true}
385
+ setConfig={_updateConfig}
386
+ isDashboard={true}
387
+ configUrl={undefined}
388
+ />
402
389
  </>
403
390
  )
404
391
  break
@@ -406,7 +393,14 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
406
393
  body = (
407
394
  <>
408
395
  <Header visualizationKey={visualizationKey} subEditor='Filtered Text' />
409
- <CdcFilteredText key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={_updateConfig} isDashboard={true} configUrl={undefined} />
396
+ <CdcFilteredText
397
+ key={visualizationKey}
398
+ config={visualizationConfig}
399
+ isEditor={true}
400
+ setConfig={_updateConfig}
401
+ isDashboard={true}
402
+ configUrl={undefined}
403
+ />
410
404
  </>
411
405
  )
412
406
  break
@@ -415,7 +409,12 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
415
409
  body = !hideFilter ? (
416
410
  <>
417
411
  <Header visualizationKey={visualizationKey} subEditor='Filter Dropdowns' />
418
- <DashboardSharedFilters isEditor={true} visualizationConfig={visualizationConfig} apiFilterDropdowns={apiFilterDropdowns} setConfig={_updateConfig} />
412
+ <DashboardSharedFilters
413
+ isEditor={true}
414
+ visualizationConfig={visualizationConfig}
415
+ apiFilterDropdowns={apiFilterDropdowns}
416
+ setConfig={_updateConfig}
417
+ />
419
418
  </>
420
419
  ) : (
421
420
  <></>
@@ -425,7 +424,12 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
425
424
  body = (
426
425
  <>
427
426
  <Header visualizationKey={visualizationKey} subEditor='Table' />
428
- <DataTableStandAlone visualizationKey={visualizationKey} config={visualizationConfig} isEditor={true} updateConfig={_updateConfig} />
427
+ <DataTableStandAlone
428
+ visualizationKey={visualizationKey}
429
+ config={visualizationConfig}
430
+ isEditor={true}
431
+ updateConfig={_updateConfig}
432
+ />
429
433
  </>
430
434
  )
431
435
  break
@@ -433,10 +437,14 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
433
437
  body = (
434
438
  <>
435
439
  <Header visualizationKey={visualizationKey} subEditor='Footnotes' />
436
- <FootnotesStandAlone visualizationKey={visualizationKey} config={{ ...visualizationConfig, datasets: state.config.datasets }} isEditor={true} updateConfig={_updateConfig} />
440
+ <FootnotesStandAlone
441
+ visualizationKey={visualizationKey}
442
+ config={{ ...visualizationConfig, datasets: state.config.datasets }}
443
+ isEditor={true}
444
+ updateConfig={_updateConfig}
445
+ />
437
446
  </>
438
447
  )
439
- break
440
448
  default:
441
449
  body = <></>
442
450
  break
@@ -468,7 +476,11 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
468
476
  <MultiTabs isEditor={isEditor && !isPreview} />
469
477
  <Layout.Responsive isEditor={isEditor}>
470
478
  <div className={`cdc-dashboard-inner-container${isEditor ? ' is-editor' : ''}`}>
471
- <Title title={title} isDashboard={true} classes={[`dashboard-title`, `${config.dashboard.theme ?? 'theme-blue'}`]} />
479
+ <Title
480
+ title={title}
481
+ isDashboard={true}
482
+ classes={[`dashboard-title`, `${config.dashboard.theme ?? 'theme-blue'}`]}
483
+ />
472
484
  {/* Description */}
473
485
  {description && <div className='subtext'>{parse(description)}</div>}
474
486
  {/* Visualizations */}
@@ -488,7 +500,9 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
488
500
  return (
489
501
  <>
490
502
  {/* Expand/Collapse All */}
491
- {row.expandCollapseAllButtons === true && <ExpandCollapseButtons setAllExpanded={setAllExpanded} />}
503
+ {row.expandCollapseAllButtons === true && (
504
+ <ExpandCollapseButtons setAllExpanded={setAllExpanded} />
505
+ )}
492
506
  {Object.keys(dataGroups).map(groupName => {
493
507
  const dataValue = dataGroups[groupName]
494
508
  return (
@@ -509,14 +523,42 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
509
523
  </>
510
524
  )
511
525
  } else {
512
- return <VisualizationRow key={`row__${index}`} allExpanded={false} groupName={''} row={row} rowIndex={index} setSharedFilter={setSharedFilter} updateChildConfig={updateChildConfig} apiFilterDropdowns={apiFilterDropdowns} currentViewport={currentViewport} />
526
+ return (
527
+ <VisualizationRow
528
+ key={`row__${index}`}
529
+ allExpanded={false}
530
+ groupName={''}
531
+ row={row}
532
+ rowIndex={index}
533
+ setSharedFilter={setSharedFilter}
534
+ updateChildConfig={updateChildConfig}
535
+ apiFilterDropdowns={apiFilterDropdowns}
536
+ currentViewport={currentViewport}
537
+ />
538
+ )
513
539
  }
514
540
  })}
515
541
 
516
542
  {/* Image or PDF Inserts */}
517
543
  <section className='download-buttons'>
518
- {config.table?.downloadImageButton && <MediaControls.Button title='Download Dashboard as Image' type='image' state={config} text='Download Dashboard Image' elementToCapture={imageId} />}
519
- {config.table?.downloadPdfButton && <MediaControls.Button title='Download Dashboard as PDF' type='pdf' state={config} text='Download Dashboard PDF' elementToCapture={imageId} />}
544
+ {config.table?.downloadImageButton && (
545
+ <MediaControls.Button
546
+ title='Download Dashboard as Image'
547
+ type='image'
548
+ state={config}
549
+ text='Download Dashboard Image'
550
+ elementToCapture={imageId}
551
+ />
552
+ )}
553
+ {config.table?.downloadPdfButton && (
554
+ <MediaControls.Button
555
+ title='Download Dashboard as PDF'
556
+ type='pdf'
557
+ state={config}
558
+ text='Download Dashboard PDF'
559
+ elementToCapture={imageId}
560
+ />
561
+ )}
520
562
  </section>
521
563
 
522
564
  {/* Data Table */}
@@ -561,14 +603,26 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
561
603
  })
562
604
 
563
605
  //Applys any applicable filters to the Table
564
- const filteredTableData = applicableFilters.length > 0 ? filterData(applicableFilters, config.datasets[datasetKey].data) : undefined
606
+ const filteredTableData =
607
+ applicableFilters.length > 0
608
+ ? filterData(applicableFilters, config.datasets[datasetKey].data)
609
+ : undefined
565
610
  return (
566
- <div className='multi-table-container' id={`data-table-${datasetKey}`} key={`data-table-${datasetKey}`}>
611
+ <div
612
+ className='multi-table-container'
613
+ id={`data-table-${datasetKey}`}
614
+ key={`data-table-${datasetKey}`}
615
+ >
567
616
  <DataTable
568
617
  config={config as TableConfig}
569
618
  dataConfig={config.datasets[datasetKey]}
570
619
  rawData={config.datasets[datasetKey].data?.[0]?.tableData || config.datasets[datasetKey].data}
571
- runtimeData={config.datasets[datasetKey].data?.[0]?.tableData || filteredTableData || config.datasets[datasetKey].data || []}
620
+ runtimeData={
621
+ config.datasets[datasetKey].data?.[0]?.tableData ||
622
+ filteredTableData ||
623
+ config.datasets[datasetKey].data ||
624
+ []
625
+ }
572
626
  expandDataTable={config.table.expanded}
573
627
  tableTitle={datasetKey}
574
628
  viewport={currentViewport}
@@ -587,7 +641,17 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
587
641
 
588
642
  return (
589
643
  <GlobalContextProvider>
590
- <DashboardContext.Provider value={{ ...state, setParentConfig: editorContext.setTempConfig, outerContainerRef, isDebug, loadAPIFilters, reloadURLData }}>
644
+ <DashboardContext.Provider
645
+ value={{
646
+ ...state,
647
+ setParentConfig: editorContext.setTempConfig,
648
+ outerContainerRef,
649
+ isDebug,
650
+ loadAPIFilters,
651
+ setAPIFilterDropdowns,
652
+ reloadURLData
653
+ }}
654
+ >
591
655
  <DashboardDispatchContext.Provider value={dispatch}>
592
656
  <div className={dashboardContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId}>
593
657
  {body}
@@ -11,7 +11,12 @@ type ConfigCTX = DashboardState & {
11
11
  setParentConfig: any
12
12
  isDebug: boolean
13
13
  reloadURLData: (newFilters?: SharedFilter[]) => void
14
- loadAPIFilters: (sharedFilters: SharedFilter[], dropdowns?: APIFilterDropdowns, recursiveLimit?: number) => Promise<SharedFilter[]>
14
+ loadAPIFilters: (
15
+ sharedFilters: SharedFilter[],
16
+ dropdowns: APIFilterDropdowns,
17
+ recursiveLimit?: number
18
+ ) => Promise<SharedFilter[]>
19
+ setAPIFilterDropdowns: (dropdowns: APIFilterDropdowns) => void
15
20
  }
16
21
 
17
22
  const firstTab: Tab = 'Dashboard Description'
@@ -27,6 +32,7 @@ export const initialState = {
27
32
  const initialContext: ConfigCTX = {
28
33
  outerContainerRef: () => {},
29
34
  setParentConfig: () => {},
35
+ setAPIFilterDropdowns: () => {},
30
36
  reloadURLData: () => {},
31
37
  loadAPIFilters: () => Promise.resolve([]),
32
38
  isDebug: false,