@cdc/dashboard 4.23.4 → 4.23.6

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.
@@ -11,7 +11,6 @@ import { HTML5Backend } from 'react-dnd-html5-backend'
11
11
  import parse from 'html-react-parser'
12
12
 
13
13
  import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
14
- import cacheBustingString from '@cdc/core/helpers/cacheBustingString'
15
14
  import { GlobalContextProvider } from '@cdc/core/components/GlobalContext'
16
15
  import ConfigContext from './ConfigContext'
17
16
 
@@ -32,10 +31,11 @@ import Header from './components/Header'
32
31
  import defaults from './data/initial-state'
33
32
  import Widget from './components/Widget'
34
33
  import DataTable from './components/DataTable'
35
- import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
34
+ import MediaControls from '@cdc/core/components/MediaControls'
36
35
 
37
36
  import './scss/main.scss'
38
37
  import '@cdc/core/styles/v2/main.scss'
38
+ import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
39
39
 
40
40
  /* eslint-disable react-hooks/exhaustive-deps */
41
41
 
@@ -76,7 +76,7 @@ const addVisualization = (type, subType) => {
76
76
  return newVisualizationConfig
77
77
  }
78
78
 
79
- const VisualizationsPanel = () => (
79
+ const VisualizationsPanel = ({ loadConfig, config }) => (
80
80
  <div className='visualizations-panel'>
81
81
  <p style={{ fontSize: '14px' }}>Click and drag an item onto the grid to add it to your dashboard.</p>
82
82
  <span className='subheading-3'>Chart</span>
@@ -98,6 +98,8 @@ const VisualizationsPanel = () => (
98
98
  <Widget addVisualization={() => addVisualization('markup-include', '')} type='markup-include' />
99
99
  <Widget addVisualization={() => addVisualization('filtered-text', '')} type='filtered-text' />
100
100
  </div>
101
+ <span className='subheading-3'>Advanced</span>
102
+ <AdvancedEditor loadConfig={loadConfig} state={config} />
101
103
  </div>
102
104
  )
103
105
 
@@ -115,25 +117,106 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
115
117
 
116
118
  const transform = new DataTransform()
117
119
 
120
+ const getFormattedData = (data, dataDescription) => {
121
+ if (data && dataDescription) {
122
+ try {
123
+ let formattedData = transform.autoStandardize(data)
124
+ formattedData = transform.developerStandardize(data, dataDescription)
125
+ return formattedData
126
+ } catch (e) {
127
+ return data
128
+ }
129
+ }
130
+
131
+ return data
132
+ }
133
+
118
134
  const processData = async config => {
119
135
  let dataset = config.formattedData || config.data
120
136
 
121
137
  if (config.dataUrl) {
122
- dataset = await fetchRemoteData(`${config.dataUrl}?v=${cacheBustingString()}`)
123
-
124
- if (dataset && config.dataDescription) {
125
- try {
126
- dataset = transform.autoStandardize(data)
127
- dataset = transform.developerStandardize(data, config.dataDescription)
128
- } catch (e) {
129
- //Data not able to be standardized, leave as is
130
- }
131
- }
138
+ dataset = await fetchRemoteData(`${config.dataUrl}`)
139
+
140
+ dataset = getFormattedData(dataset, config.dataDescription)
132
141
  }
133
142
 
134
143
  return dataset
135
144
  }
136
145
 
146
+ const reloadURLData = async () => {
147
+ if (config.datasets) {
148
+ let newData = { ...data }
149
+ let newDatasets = { ...config.datasets }
150
+ let datasetsNeedsUpdate = false
151
+ let datasetKeys = Object.keys(config.datasets)
152
+ for (let i = 0; i < datasetKeys.length; i++) {
153
+ const dataset = config.datasets[datasetKeys[i]]
154
+ if (dataset.dataUrl && config.dashboard && config.dashboard.sharedFilters) {
155
+ const dataUrl = new URL(dataset.runtimeDataUrl || dataset.dataUrl)
156
+ let qsParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
157
+
158
+ let isUpdateNeeded = false
159
+
160
+ config.dashboard.sharedFilters.forEach(filter => {
161
+ if (filter.type === 'url' && qsParams[filter.queryParameter] !== decodeURIComponent(filter.active)) {
162
+ qsParams[filter.queryParameter] = filter.active
163
+ isUpdateNeeded = true
164
+ }
165
+ })
166
+
167
+ if (!isUpdateNeeded) return
168
+
169
+ let dataUrlFinal = `${dataUrl.origin}${dataUrl.pathname}${Object.keys(qsParams)
170
+ .map((param, i) => {
171
+ let qs = i === 0 ? '?' : '&'
172
+ qs += param + '='
173
+ qs += qsParams[param]
174
+ return qs
175
+ })
176
+ .join('')}`
177
+
178
+ let newDataset = await fetchRemoteData(`${dataUrlFinal}`)
179
+
180
+ if (newDataset && dataset.dataDescription) {
181
+ try {
182
+ newDataset = transform.autoStandardize(newDataset)
183
+ newDataset = transform.developerStandardize(newDataset, dataset.dataDescription)
184
+ } catch (e) {
185
+ //Data not able to be standardized, leave as is
186
+ }
187
+ }
188
+
189
+ newDatasets[datasetKeys[i]].runtimeDataUrl = dataUrlFinal
190
+ newData[datasetKeys[i]] = newDataset
191
+ datasetsNeedsUpdate = true
192
+ }
193
+ }
194
+
195
+ if (datasetsNeedsUpdate) {
196
+ setData(newData)
197
+
198
+ let newFilteredData = {}
199
+ let newConfig = { ...config }
200
+ Object.keys(config.visualizations).forEach(key => {
201
+ let dataKey = config.visualizations[key].dataKey
202
+
203
+ let applicableFilters = config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(key) !== -1)
204
+ if (applicableFilters.length > 0) {
205
+ newFilteredData[key] = filterData(applicableFilters, newData[dataKey])
206
+ }
207
+
208
+ if (newData[dataKey]) {
209
+ newConfig.visualizations[key].formattedData = newData[dataKey]
210
+ }
211
+ })
212
+ setFilteredData(newFilteredData)
213
+
214
+ newConfig.datasets = newDatasets
215
+ setConfig(newConfig)
216
+ }
217
+ }
218
+ }
219
+
137
220
  const loadConfig = async () => {
138
221
  let response = configObj || (await (await fetch(configUrl)).json())
139
222
  let newConfig = { ...defaults, ...response }
@@ -188,7 +271,6 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
188
271
  }
189
272
 
190
273
  setData(datasets)
191
-
192
274
  updateConfig(newConfig, datasets)
193
275
  setLoading(false)
194
276
  }
@@ -201,7 +283,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
201
283
  let add = true
202
284
 
203
285
  filters.forEach(filter => {
204
- if (row[filter.columnName] !== filter.active) {
286
+ if (filter.type !== 'url' && row[filter.columnName] != filter.active) {
205
287
  add = false
206
288
  }
207
289
  })
@@ -230,7 +312,9 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
230
312
  if (applicableFilters.length > 0) {
231
313
  const visualization = newConfig.visualizations[visualizationKey]
232
314
 
233
- newFilteredData[visualizationKey] = filterData(applicableFilters, visualization.formattedData || data[visualization.dataKey])
315
+ const formattedData = visualization.dataDescription ? getFormattedData(data[visualization.dataKey] || visualization.data, visualization.dataDescription) : undefined
316
+
317
+ newFilteredData[visualizationKey] = filterData(applicableFilters, formattedData || data[visualization.dataKey])
234
318
  }
235
319
  })
236
320
 
@@ -291,7 +375,11 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
291
375
  let applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1)
292
376
 
293
377
  if (applicableFilters.length > 0) {
294
- newFilteredData[visualizationKey] = filterData(applicableFilters, newConfig.visualizations[visualizationKey].formattedData || newConfig.visualizations[visualizationKey].data || (dataOverride || data)[newConfig.visualizations[visualizationKey].dataKey])
378
+ const visualization = newConfig.visualizations[visualizationKey]
379
+
380
+ const formattedData = getFormattedData(newConfig.datasets[visualization.dataKey] && newConfig.datasets[visualization.dataKey].data ? newConfig.datasets[visualization.dataKey].data : visualization.data, visualization.dataDescription)
381
+
382
+ newFilteredData[visualizationKey] = filterData(applicableFilters, formattedData || visualization.data || (dataOverride || data)[visualization.dataKey])
295
383
  }
296
384
  })
297
385
  }
@@ -315,6 +403,10 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
315
403
  }
316
404
  }, [config])
317
405
 
406
+ useEffect(() => {
407
+ reloadURLData()
408
+ }, [JSON.stringify(config.dashboard ? config.dashboard.sharedFilters : undefined)])
409
+
318
410
  const updateChildConfig = (visualizationKey, newConfig) => {
319
411
  let updatedConfig = { ...config }
320
412
 
@@ -336,24 +428,39 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
336
428
  Object.keys(config.visualizations).forEach(key => {
337
429
  let applicableFilters = dashboardConfig.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(key) !== -1)
338
430
  if (applicableFilters.length > 0) {
339
- newFilteredData[key] = filterData(applicableFilters, config.visualizations[key].formattedData || data[config.visualizations[key].dataKey])
431
+ const visualization = config.visualizations[key]
432
+
433
+ const formattedData = visualization.dataDescription ? getFormattedData(data[config.visualizations[key].dataKey] || visualization.data, visualization.dataDescription) : undefined
434
+
435
+ newFilteredData[key] = filterData(applicableFilters, formattedData || data[config.visualizations[key].dataKey])
340
436
  }
341
437
  })
342
438
 
343
439
  setFilteredData(newFilteredData)
440
+ if (dashboardConfig.sharedFilters[index].active === dashboardConfig.sharedFilters[index].resetLabel) {
441
+ setFilteredData(data)
442
+ }
344
443
  }
345
444
 
346
445
  const announceChange = text => {}
347
446
 
348
447
  return config.dashboard.sharedFilters.map((singleFilter, index) => {
349
- if (!singleFilter.showDropdown) return <></>
448
+ if (singleFilter.type !== 'url' && !singleFilter.showDropdown) return <></>
350
449
 
351
450
  const values = []
352
451
 
452
+ if (singleFilter.resetLabel) {
453
+ values.push(
454
+ <option key={`${singleFilter.resetLabel}-option-${index}`} value={singleFilter.resetLabel}>
455
+ {singleFilter.resetLabel}
456
+ </option>
457
+ )
458
+ }
459
+
353
460
  singleFilter.values.forEach((filterOption, index) => {
354
461
  values.push(
355
462
  <option key={`${singleFilter.key}-option-${index}`} value={filterOption}>
356
- {filterOption}
463
+ {singleFilter.labels ? singleFilter.labels[filterOption] || filterOption : filterOption}
357
464
  </option>
358
465
  )
359
466
  })
@@ -444,7 +551,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
444
551
  body = (
445
552
  <>
446
553
  <Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Chart' />
447
- <CdcChart key={visualizationKey} config={visualizationConfig} isEditor={true} isDebug={isDebug} setConfig={updateConfig} setSharedFilter={setsSharedFilter ? setSharedFilter : undefined} isDashboard={true} />
554
+ <CdcChart key={visualizationKey} config={visualizationConfig} isEditor={true} isDebug={isDebug} setConfig={updateConfig} setSharedFilter={setsSharedFilter ? setSharedFilter : undefined} setSharedFilterValue={setSharedFilterValue} dashboardConfig={config} isDashboard={true} />
448
555
  </>
449
556
  )
450
557
  break
@@ -501,7 +608,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
501
608
  <DndProvider backend={HTML5Backend}>
502
609
  <Header tabSelected={tabSelected} setTabSelected={setTabSelected} preview={preview} setPreview={setPreview} />
503
610
  <div className='layout-container'>
504
- <VisualizationsPanel />
611
+ <VisualizationsPanel loadConfig={loadConfig} config={config} />
505
612
  <Grid />
506
613
  </div>
507
614
  </DndProvider>
@@ -572,13 +679,14 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
572
679
  <CdcChart
573
680
  key={col.widget}
574
681
  config={visualizationConfig}
682
+ dashboardConfig={config}
575
683
  isEditor={false}
576
684
  setConfig={newConfig => {
577
685
  updateChildConfig(col.widget, newConfig)
578
686
  }}
579
687
  setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
580
688
  isDashboard={true}
581
- link={config.table && config.table.show && config.datasets && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
689
+ link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
582
690
  />
583
691
  )}
584
692
  {visualizationConfig.type === 'map' && (
@@ -592,7 +700,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
592
700
  setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
593
701
  setSharedFilterValue={setSharedFilterValue}
594
702
  isDashboard={true}
595
- link={config.table && config.table.show && config.datasets && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
703
+ link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
596
704
  />
597
705
  )}
598
706
  {visualizationConfig.type === 'data-bite' && (
@@ -615,7 +723,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
615
723
  updateChildConfig(col.widget, newConfig)
616
724
  }}
617
725
  isDashboard={true}
618
- link={config.table && config.table.show && config.datasets && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
726
+ link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
619
727
  />
620
728
  )}
621
729
  {visualizationConfig.type === 'markup-include' && (
@@ -652,8 +760,8 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
652
760
 
653
761
  {/* Image or PDF Inserts */}
654
762
  <section className='download-buttons'>
655
- {config.table.downloadImageButton && <CoveMediaControls.Button title='Download Dashboard as Image' type='image' state={config} text='Download Dashboard Image' elementToCapture={imageId} />}
656
- {config.table.downloadPdfButton && <CoveMediaControls.Button title='Download Dashboard as PDF' type='pdf' state={config} text='Download Dashboard PDF' elementToCapture={imageId} />}
763
+ {config.table.downloadImageButton && <MediaControls.Button title='Download Dashboard as Image' type='image' state={config} text='Download Dashboard Image' elementToCapture={imageId} />}
764
+ {config.table.downloadPdfButton && <MediaControls.Button title='Download Dashboard as PDF' type='pdf' state={config} text='Download Dashboard PDF' elementToCapture={imageId} />}
657
765
  </section>
658
766
 
659
767
  {/* Data Table */}
@@ -678,7 +786,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
678
786
  config.dashboard.sharedFilters.forEach(sharedFilter => {
679
787
  let allMatch = true
680
788
  vizKeysUsingDataset.forEach(visualizationKey => {
681
- if (sharedFilter.usedBy.indexOf(visualizationKey) === -1) {
789
+ if (sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) === -1) {
682
790
  allMatch = false
683
791
  }
684
792
  })
@@ -697,7 +805,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
697
805
 
698
806
  return (
699
807
  <div className='multi-table-container' id={`data-table-${datasetKey}`} key={`data-table-${datasetKey}`}>
700
- <DataTable data={filteredTableData || config.datasets[datasetKey].data} downloadData={config.datasets[datasetKey].data} dataFileSourceType={dataFileSourceType} datasetKey={datasetKey} config={config} imageRef={imageId}></DataTable>
808
+ <DataTable data={filteredTableData || config.datasets[datasetKey].data || []} downloadData={config.datasets[datasetKey].data} dataFileSourceType={dataFileSourceType} datasetKey={datasetKey} config={config} imageRef={imageId}></DataTable>
701
809
  </div>
702
810
  )
703
811
  })}
@@ -2,7 +2,7 @@ import React, { useEffect, useState, useMemo, memo } from 'react'
2
2
  import { useTable, useSortBy, useResizeColumns, useBlockLayout } from 'react-table'
3
3
  import Papa from 'papaparse'
4
4
  import { Base64 } from 'js-base64'
5
- import CoveMediaControls from '@cdc/core/components/CoveMediaControls'
5
+ import MediaControls from '@cdc/core/components/MediaControls'
6
6
  import Icon from '@cdc/core/components/ui/Icon'
7
7
 
8
8
  import ErrorBoundary from '@cdc/core/components/ErrorBoundary'
@@ -94,7 +94,7 @@ export default function DataTable(props) {
94
94
 
95
95
  return (
96
96
  <ErrorBoundary component='DataTable'>
97
- <CoveMediaControls.Section classes={['download-links']}>
97
+ <MediaControls.Section classes={['download-links']}>
98
98
  {config.table.showDownloadUrl && dataFileSourceType === 'url' && (
99
99
  <a className='dashboard-download-link' href={config.datasets[datasetKey].dataFileName} title='Link to View Dataset' target='_blank'>
100
100
  {' '}
@@ -103,7 +103,7 @@ export default function DataTable(props) {
103
103
  </a>
104
104
  )}
105
105
  {config.table.download && <DownloadButton data={data} />}
106
- </CoveMediaControls.Section>
106
+ </MediaControls.Section>
107
107
  {config.table.show && (
108
108
  <section className={`data-table-container`} aria-label={accessibilityLabel}>
109
109
  <div
@@ -119,7 +119,7 @@ export default function DataTable(props) {
119
119
  }
120
120
  }}
121
121
  >
122
- <Icon display={tableExpanded ? 'minus' : 'plus'} base/>
122
+ <Icon display={tableExpanded ? 'minus' : 'plus'} base />
123
123
  {config.table.label}
124
124
  {datasetKey ? `: ${datasetKey}` : ''}
125
125
  </div>