@cdc/dashboard 4.22.10 → 4.23.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.
- package/README.md +36 -36
- package/dist/495.js +3 -0
- package/dist/703.js +1 -0
- package/dist/cdcdashboard.js +2405 -134
- package/examples/default-data.json +367 -367
- package/examples/default-filter-control.json +205 -174
- package/examples/default-multi-dataset.json +505 -497
- package/examples/default.json +161 -155
- package/examples/private/chart-issue.json +3462 -3466
- package/examples/private/no-issue.json +3462 -3466
- package/examples/private/totals-two.json +103 -103
- package/examples/private/totals.json +102 -102
- package/examples/temp-example-data.json +1 -1
- package/examples/test-example.json +163 -1
- package/package.json +7 -7
- package/src/CdcDashboard.js +200 -124
- package/src/CdcDashboard.jsx +292 -279
- package/src/ConfigContext.js +3 -3
- package/src/components/Column.jsx +10 -9
- package/src/components/DataTable.tsx +97 -108
- package/src/components/EditorPanel.js +184 -246
- package/src/components/Grid.jsx +8 -4
- package/src/components/Header.jsx +244 -119
- package/src/components/Row.js +30 -29
- package/src/components/Row.jsx +36 -52
- package/src/components/Widget.js +68 -61
- package/src/components/Widget.jsx +85 -62
- package/src/data/initial-state.js +5 -10
- package/src/index.html +3 -3
- package/src/index.js +12 -12
- package/src/scss/editor-panel.scss +494 -489
- package/src/scss/grid.scss +20 -19
- package/src/scss/main.scss +66 -17
- package/src/scss/variables.scss +1 -1
package/src/CdcDashboard.jsx
CHANGED
|
@@ -25,25 +25,27 @@ import CdcChart from '@cdc/chart'
|
|
|
25
25
|
import CdcDataBite from '@cdc/data-bite'
|
|
26
26
|
import CdcWaffleChart from '@cdc/waffle-chart'
|
|
27
27
|
import CdcMarkupInclude from '@cdc/markup-include'
|
|
28
|
-
import CdcFilteredText from '@cdc/filtered-text'
|
|
28
|
+
import CdcFilteredText from '@cdc/filtered-text'
|
|
29
29
|
|
|
30
30
|
import Grid from './components/Grid'
|
|
31
31
|
import Header from './components/Header'
|
|
32
32
|
import defaults from './data/initial-state'
|
|
33
33
|
import Widget from './components/Widget'
|
|
34
34
|
import DataTable from './components/DataTable'
|
|
35
|
+
import CoveMediaControls from '@cdc/core/helpers/CoveMediaControls'
|
|
36
|
+
|
|
35
37
|
|
|
36
38
|
import './scss/main.scss'
|
|
37
39
|
import '@cdc/core/styles/v2/main.scss'
|
|
38
40
|
|
|
39
41
|
const addVisualization = (type, subType) => {
|
|
40
|
-
let modalWillOpen = type ===
|
|
42
|
+
let modalWillOpen = type === 'markup-include' ? false : true
|
|
41
43
|
let newVisualizationConfig = {
|
|
42
44
|
newViz: true,
|
|
43
45
|
openModal: modalWillOpen,
|
|
44
46
|
uid: type + Date.now(),
|
|
45
|
-
type
|
|
46
|
-
}
|
|
47
|
+
type
|
|
48
|
+
}
|
|
47
49
|
|
|
48
50
|
switch (type) {
|
|
49
51
|
case 'chart':
|
|
@@ -74,44 +76,45 @@ const addVisualization = (type, subType) => {
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
const VisualizationsPanel = () => (
|
|
77
|
-
<div className=
|
|
79
|
+
<div className='visualizations-panel'>
|
|
78
80
|
<p style={{ fontSize: '14px' }}>Click and drag an item onto the grid to add it to your dashboard.</p>
|
|
79
|
-
<span className=
|
|
80
|
-
<div className=
|
|
81
|
-
<Widget addVisualization={() => addVisualization('chart', 'Bar')} type=
|
|
82
|
-
<Widget addVisualization={() => addVisualization('chart', 'Line')} type=
|
|
83
|
-
<Widget addVisualization={() => addVisualization('chart', 'Pie')} type=
|
|
81
|
+
<span className='subheading-3'>Chart</span>
|
|
82
|
+
<div className='drag-grid'>
|
|
83
|
+
<Widget addVisualization={() => addVisualization('chart', 'Bar')} type='Bar' />
|
|
84
|
+
<Widget addVisualization={() => addVisualization('chart', 'Line')} type='Line' />
|
|
85
|
+
<Widget addVisualization={() => addVisualization('chart', 'Pie')} type='Pie' />
|
|
84
86
|
</div>
|
|
85
|
-
<span className=
|
|
86
|
-
<div className=
|
|
87
|
-
<Widget addVisualization={() => addVisualization('map', 'us')} type=
|
|
88
|
-
<Widget addVisualization={() => addVisualization('map', 'world')} type=
|
|
89
|
-
<Widget addVisualization={() => addVisualization('map', 'single-state')} type=
|
|
87
|
+
<span className='subheading-3'>Map</span>
|
|
88
|
+
<div className='drag-grid'>
|
|
89
|
+
<Widget addVisualization={() => addVisualization('map', 'us')} type='us' />
|
|
90
|
+
<Widget addVisualization={() => addVisualization('map', 'world')} type='world' />
|
|
91
|
+
<Widget addVisualization={() => addVisualization('map', 'single-state')} type='single-state' />
|
|
90
92
|
</div>
|
|
91
|
-
<span className=
|
|
92
|
-
<div className=
|
|
93
|
-
<Widget addVisualization={() => addVisualization('data-bite', '')} type=
|
|
94
|
-
<Widget addVisualization={() => addVisualization('waffle-chart', '')} type=
|
|
95
|
-
<Widget addVisualization={() => addVisualization('markup-include', '')} type=
|
|
96
|
-
<Widget addVisualization={() => addVisualization('filtered-text', '')} type=
|
|
93
|
+
<span className='subheading-3'>Misc.</span>
|
|
94
|
+
<div className='drag-grid'>
|
|
95
|
+
<Widget addVisualization={() => addVisualization('data-bite', '')} type='data-bite' />
|
|
96
|
+
<Widget addVisualization={() => addVisualization('waffle-chart', '')} type='waffle-chart' />
|
|
97
|
+
<Widget addVisualization={() => addVisualization('markup-include', '')} type='markup-include' />
|
|
98
|
+
<Widget addVisualization={() => addVisualization('filtered-text', '')} type='filtered-text' />
|
|
97
99
|
</div>
|
|
98
100
|
</div>
|
|
99
101
|
)
|
|
100
102
|
|
|
101
103
|
export default function CdcDashboard({ configUrl = '', config: configObj = undefined, isEditor = false, setConfig: setParentConfig }) {
|
|
102
|
-
const [
|
|
103
|
-
const [
|
|
104
|
-
const [
|
|
105
|
-
const [
|
|
106
|
-
const [
|
|
107
|
-
const [
|
|
108
|
-
const [
|
|
104
|
+
const [config, setConfig] = useState(configObj ?? {})
|
|
105
|
+
const [data, setData] = useState([])
|
|
106
|
+
const [filteredData, setFilteredData] = useState()
|
|
107
|
+
const [loading, setLoading] = useState(true)
|
|
108
|
+
const [preview, setPreview] = useState(false)
|
|
109
|
+
const [tabSelected, setTabSelected] = useState(0)
|
|
110
|
+
const [currentViewport, setCurrentViewport] = useState('lg')
|
|
111
|
+
const [imageId, setImageId] = useState(`cove-${Math.random().toString(16).slice(-4)}`)
|
|
109
112
|
|
|
110
113
|
const { title, description } = config.dashboard || config
|
|
111
114
|
|
|
112
115
|
const transform = new DataTransform()
|
|
113
116
|
|
|
114
|
-
const processData = async
|
|
117
|
+
const processData = async config => {
|
|
115
118
|
let dataset = config.formattedData || config.data
|
|
116
119
|
|
|
117
120
|
if (config.dataUrl) {
|
|
@@ -131,44 +134,46 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
const loadConfig = async () => {
|
|
134
|
-
let response = configObj || await (await fetch(configUrl)).json()
|
|
137
|
+
let response = configObj || (await (await fetch(configUrl)).json())
|
|
135
138
|
let newConfig = { ...defaults, ...response }
|
|
136
139
|
let datasets = {}
|
|
137
140
|
|
|
138
141
|
if (response.datasets) {
|
|
139
|
-
await Promise.all(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
await Promise.all(
|
|
143
|
+
Object.keys(response.datasets).map(async key => {
|
|
144
|
+
datasets[key] = await processData(response.datasets[key])
|
|
145
|
+
})
|
|
146
|
+
)
|
|
142
147
|
} else {
|
|
143
148
|
let dataKey = newConfig.dataFileName || 'backwards-compatibility'
|
|
144
149
|
datasets[dataKey] = await processData(response)
|
|
145
150
|
|
|
146
|
-
let datasetsFull = {}
|
|
151
|
+
let datasetsFull = {}
|
|
147
152
|
datasetsFull[dataKey] = {
|
|
148
153
|
data: datasets[dataKey],
|
|
149
154
|
dataDescription: newConfig.dataDescription
|
|
150
155
|
}
|
|
151
|
-
newConfig.datasets = datasetsFull
|
|
152
|
-
|
|
156
|
+
newConfig.datasets = datasetsFull
|
|
157
|
+
|
|
153
158
|
Object.keys(newConfig.visualizations).forEach(vizKey => {
|
|
154
159
|
newConfig.visualizations[vizKey].dataKey = dataKey
|
|
155
160
|
newConfig.visualizations[vizKey].dataDescription = newConfig.dataDescription
|
|
156
161
|
newConfig.visualizations[vizKey].formattedData = newConfig.formattedData
|
|
157
162
|
})
|
|
158
|
-
|
|
163
|
+
|
|
159
164
|
delete newConfig.data
|
|
160
165
|
delete newConfig.dataUrl
|
|
161
166
|
delete newConfig.dataFileName
|
|
162
167
|
delete newConfig.dataFileSourceType
|
|
163
168
|
delete newConfig.dataDescription
|
|
164
169
|
delete newConfig.formattedData
|
|
165
|
-
|
|
170
|
+
|
|
166
171
|
if (newConfig.dashboard && newConfig.dashboard.filters) {
|
|
167
172
|
newConfig.dashboard.sharedFilters = newConfig.dashboard.sharedFilters || []
|
|
168
173
|
newConfig.dashboard.filters.forEach(filter => {
|
|
169
174
|
newConfig.dashboard.sharedFilters.push({ ...filter, key: filter.label, showDropdown: true, usedBy: Object.keys(newConfig.visualizations) })
|
|
170
175
|
})
|
|
171
|
-
|
|
176
|
+
|
|
172
177
|
delete newConfig.dashboard.filters
|
|
173
178
|
}
|
|
174
179
|
}
|
|
@@ -182,11 +187,11 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
182
187
|
const filterData = (filters, data) => {
|
|
183
188
|
let filteredData = []
|
|
184
189
|
|
|
185
|
-
if(data){
|
|
186
|
-
data.forEach(
|
|
190
|
+
if (data) {
|
|
191
|
+
data.forEach(row => {
|
|
187
192
|
let add = true
|
|
188
193
|
|
|
189
|
-
filters.forEach(
|
|
194
|
+
filters.forEach(filter => {
|
|
190
195
|
if (row[filter.columnName] !== filter.active) {
|
|
191
196
|
add = false
|
|
192
197
|
}
|
|
@@ -211,11 +216,11 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
211
216
|
}
|
|
212
217
|
|
|
213
218
|
Object.keys(newConfig.visualizations).forEach(visualizationKey => {
|
|
214
|
-
let applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1)
|
|
215
|
-
|
|
219
|
+
let applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1)
|
|
220
|
+
|
|
216
221
|
if (applicableFilters.length > 0) {
|
|
217
222
|
const visualization = newConfig.visualizations[visualizationKey]
|
|
218
|
-
|
|
223
|
+
|
|
219
224
|
newFilteredData[visualizationKey] = filterData(applicableFilters, visualization.formattedData || data[visualization.dataKey])
|
|
220
225
|
}
|
|
221
226
|
})
|
|
@@ -229,7 +234,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
229
234
|
const values = []
|
|
230
235
|
|
|
231
236
|
Object.keys(data).forEach(key => {
|
|
232
|
-
data[key].forEach(
|
|
237
|
+
data[key].forEach(row => {
|
|
233
238
|
const value = row[columnName]
|
|
234
239
|
if (value && false === values.includes(value)) {
|
|
235
240
|
values.push(value)
|
|
@@ -248,7 +253,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
248
253
|
newConfig.dashboard.sharedFilters.forEach((filter, i) => {
|
|
249
254
|
for (let j = 0; j < visualizationKeys.length; j++) {
|
|
250
255
|
if (visualizationKeys[j] === filter.setBy) {
|
|
251
|
-
const filterValues = generateValuesForFilter(filter.columnName,
|
|
256
|
+
const filterValues = generateValuesForFilter(filter.columnName, dataOverride || data)
|
|
252
257
|
|
|
253
258
|
if (newConfig.dashboard.sharedFilters[i].order === 'asc') {
|
|
254
259
|
filterValues.sort()
|
|
@@ -258,23 +263,23 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
258
263
|
}
|
|
259
264
|
|
|
260
265
|
newConfig.dashboard.sharedFilters[i].values = filterValues
|
|
261
|
-
if(filterValues.length > 0){
|
|
262
|
-
newConfig.dashboard.sharedFilters[i].active = newConfig.dashboard.sharedFilters[i].active || newConfig.dashboard.sharedFilters[i].values[0]
|
|
266
|
+
if (filterValues.length > 0) {
|
|
267
|
+
newConfig.dashboard.sharedFilters[i].active = newConfig.dashboard.sharedFilters[i].active || newConfig.dashboard.sharedFilters[i].values[0]
|
|
263
268
|
}
|
|
264
269
|
break
|
|
265
270
|
}
|
|
266
271
|
}
|
|
267
272
|
|
|
268
273
|
if ((!newConfig.dashboard.sharedFilters[i].values || newConfig.dashboard.sharedFilters[i].values.length === 0) && newConfig.dashboard.sharedFilters[i].showDropdown) {
|
|
269
|
-
newConfig.dashboard.sharedFilters[i].values = generateValuesForFilter(filter.columnName,
|
|
270
|
-
if(newConfig.dashboard.sharedFilters[i].values.length > 0){
|
|
271
|
-
newConfig.dashboard.sharedFilters[i].active = newConfig.dashboard.sharedFilters[i].active || newConfig.dashboard.sharedFilters[i].values[0]
|
|
274
|
+
newConfig.dashboard.sharedFilters[i].values = generateValuesForFilter(filter.columnName, dataOverride || data)
|
|
275
|
+
if (newConfig.dashboard.sharedFilters[i].values.length > 0) {
|
|
276
|
+
newConfig.dashboard.sharedFilters[i].active = newConfig.dashboard.sharedFilters[i].active || newConfig.dashboard.sharedFilters[i].values[0]
|
|
272
277
|
}
|
|
273
278
|
}
|
|
274
279
|
})
|
|
275
280
|
|
|
276
281
|
visualizationKeys.forEach(visualizationKey => {
|
|
277
|
-
let applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1)
|
|
282
|
+
let applicableFilters = newConfig.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1)
|
|
278
283
|
|
|
279
284
|
if (applicableFilters.length > 0) {
|
|
280
285
|
newFilteredData[visualizationKey] = filterData(applicableFilters, newConfig.visualizations[visualizationKey].formattedData || newConfig.visualizations[visualizationKey].data || (dataOverride || data)[newConfig.visualizations[visualizationKey].dataKey])
|
|
@@ -299,7 +304,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
299
304
|
if (setParentConfig && isEditor) {
|
|
300
305
|
setParentConfig(config)
|
|
301
306
|
}
|
|
302
|
-
}, [
|
|
307
|
+
}, [config])
|
|
303
308
|
|
|
304
309
|
const updateChildConfig = (visualizationKey, newConfig) => {
|
|
305
310
|
let updatedConfig = { ...config }
|
|
@@ -321,7 +326,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
321
326
|
let newFilteredData = {}
|
|
322
327
|
Object.keys(config.visualizations).forEach(key => {
|
|
323
328
|
let applicableFilters = dashboardConfig.sharedFilters.filter(sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(key) !== -1)
|
|
324
|
-
if(applicableFilters.length > 0){
|
|
329
|
+
if (applicableFilters.length > 0) {
|
|
325
330
|
newFilteredData[key] = filterData(applicableFilters, config.visualizations[key].formattedData || data[config.visualizations[key].dataKey])
|
|
326
331
|
}
|
|
327
332
|
})
|
|
@@ -329,7 +334,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
329
334
|
setFilteredData(newFilteredData)
|
|
330
335
|
}
|
|
331
336
|
|
|
332
|
-
const announceChange =
|
|
337
|
+
const announceChange = text => { }
|
|
333
338
|
|
|
334
339
|
return config.dashboard.sharedFilters.map((singleFilter, index) => {
|
|
335
340
|
if (!singleFilter.showDropdown) return
|
|
@@ -337,22 +342,22 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
337
342
|
const values = []
|
|
338
343
|
|
|
339
344
|
singleFilter.values.forEach((filterOption, index) => {
|
|
340
|
-
values.push(
|
|
341
|
-
key={`${singleFilter.key}-option-${index}`}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
+
values.push(
|
|
346
|
+
<option key={`${singleFilter.key}-option-${index}`} value={filterOption}>
|
|
347
|
+
{filterOption}
|
|
348
|
+
</option>
|
|
349
|
+
)
|
|
345
350
|
})
|
|
346
351
|
|
|
347
352
|
return (
|
|
348
|
-
<section className=
|
|
353
|
+
<section className='dashboard-filters-section' key={`${singleFilter.key}-filtersection-${index}`}>
|
|
349
354
|
<label htmlFor={`filter-${index}`}>{singleFilter.key}</label>
|
|
350
355
|
<select
|
|
351
356
|
id={`filter-${index}`}
|
|
352
|
-
className=
|
|
353
|
-
data-index=
|
|
357
|
+
className='filter-select'
|
|
358
|
+
data-index='0'
|
|
354
359
|
value={singleFilter.active}
|
|
355
|
-
onChange={
|
|
360
|
+
onChange={val => {
|
|
356
361
|
changeFilterActive(index, val.target.value)
|
|
357
362
|
announceChange(`Filter ${singleFilter.key} value has been changed to ${val.target.value}, please reference the data table to see updated values.`)
|
|
358
363
|
}}
|
|
@@ -379,7 +384,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
379
384
|
}, [])
|
|
380
385
|
|
|
381
386
|
// Prevent render if loading
|
|
382
|
-
if (loading) return <Loading/>
|
|
387
|
+
if (loading) return <Loading />
|
|
383
388
|
|
|
384
389
|
let body = null
|
|
385
390
|
|
|
@@ -394,7 +399,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
394
399
|
|
|
395
400
|
visualizationConfig.data = filteredData && filteredData[visualizationKey] ? filteredData[visualizationKey] : data[dataKey]
|
|
396
401
|
if (visualizationConfig.formattedData) {
|
|
397
|
-
visualizationConfig.originalFormattedData = visualizationConfig.formattedData
|
|
402
|
+
visualizationConfig.originalFormattedData = visualizationConfig.formattedData
|
|
398
403
|
visualizationConfig.formattedData = visualizationConfig.data
|
|
399
404
|
}
|
|
400
405
|
|
|
@@ -412,82 +417,61 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
412
417
|
setConfig(newConfig)
|
|
413
418
|
}
|
|
414
419
|
|
|
415
|
-
const updateConfig =
|
|
416
|
-
let dataCorrectedConfig = visualizationConfig.originalFormattedData ? {...newConfig, formattedData: visualizationConfig.originalFormattedData} : newConfig
|
|
420
|
+
const updateConfig = newConfig => {
|
|
421
|
+
let dataCorrectedConfig = visualizationConfig.originalFormattedData ? { ...newConfig, formattedData: visualizationConfig.originalFormattedData } : newConfig
|
|
417
422
|
updateChildConfig(visualizationKey, dataCorrectedConfig)
|
|
418
423
|
}
|
|
419
424
|
|
|
420
425
|
switch (visualizationConfig.type) {
|
|
421
426
|
case 'chart':
|
|
422
|
-
body =
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
config={visualizationConfig}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
429
|
-
isDashboard={true}
|
|
430
|
-
/>
|
|
431
|
-
</>
|
|
427
|
+
body = (
|
|
428
|
+
<>
|
|
429
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Chart' />
|
|
430
|
+
<CdcChart key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={updateConfig} setSharedFilter={setsSharedFilter ? setSharedFilter : undefined} isDashboard={true} />
|
|
431
|
+
</>
|
|
432
|
+
)
|
|
432
433
|
break
|
|
433
434
|
case 'map':
|
|
434
|
-
body =
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
config={visualizationConfig}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
441
|
-
setSharedFilterValue={setSharedFilterValue}
|
|
442
|
-
isDashboard={true}
|
|
443
|
-
/>
|
|
444
|
-
</>
|
|
435
|
+
body = (
|
|
436
|
+
<>
|
|
437
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Map' />
|
|
438
|
+
<CdcMap key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={updateConfig} setSharedFilter={setsSharedFilter ? setSharedFilter : undefined} setSharedFilterValue={setSharedFilterValue} isDashboard={true} />
|
|
439
|
+
</>
|
|
440
|
+
)
|
|
445
441
|
break
|
|
446
442
|
case 'data-bite':
|
|
447
443
|
visualizationConfig = { ...visualizationConfig, newViz: true }
|
|
448
|
-
body =
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
config={visualizationConfig}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
isDashboard={true}
|
|
455
|
-
/>
|
|
456
|
-
</>
|
|
444
|
+
body = (
|
|
445
|
+
<>
|
|
446
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Data Bite' />
|
|
447
|
+
<CdcDataBite key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={updateConfig} isDashboard={true} />
|
|
448
|
+
</>
|
|
449
|
+
)
|
|
457
450
|
break
|
|
458
451
|
case 'waffle-chart':
|
|
459
|
-
body =
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
config={visualizationConfig}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
isDashboard={true}
|
|
466
|
-
/>
|
|
467
|
-
</>
|
|
452
|
+
body = (
|
|
453
|
+
<>
|
|
454
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Waffle Chart' />
|
|
455
|
+
<CdcWaffleChart key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={updateConfig} isDashboard={true} />
|
|
456
|
+
</>
|
|
457
|
+
)
|
|
468
458
|
break
|
|
469
459
|
case 'markup-include':
|
|
470
|
-
body =
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
config={visualizationConfig}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
isDashboard={true}
|
|
477
|
-
/>
|
|
478
|
-
</>
|
|
460
|
+
body = (
|
|
461
|
+
<>
|
|
462
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Markup Include' />
|
|
463
|
+
<CdcMarkupInclude key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={updateConfig} isDashboard={true} />
|
|
464
|
+
</>
|
|
465
|
+
)
|
|
479
466
|
break
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
isEditor={true}
|
|
486
|
-
setConfig={updateConfig}
|
|
487
|
-
isDashboard={true}
|
|
488
|
-
/>
|
|
467
|
+
case 'filtered-text':
|
|
468
|
+
body = (
|
|
469
|
+
<>
|
|
470
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Filtered Text' />
|
|
471
|
+
<CdcFilteredText key={visualizationKey} config={visualizationConfig} isEditor={true} setConfig={updateConfig} isDashboard={true} />
|
|
489
472
|
</>
|
|
490
|
-
|
|
473
|
+
)
|
|
474
|
+
break
|
|
491
475
|
default:
|
|
492
476
|
body = <></>
|
|
493
477
|
break
|
|
@@ -498,10 +482,10 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
498
482
|
if (!subVisualizationEditing) {
|
|
499
483
|
body = (
|
|
500
484
|
<DndProvider backend={HTML5Backend}>
|
|
501
|
-
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} preview={preview} setPreview={setPreview}/>
|
|
502
|
-
<div className=
|
|
503
|
-
<VisualizationsPanel/>
|
|
504
|
-
<Grid/>
|
|
485
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} preview={preview} setPreview={setPreview} />
|
|
486
|
+
<div className='layout-container'>
|
|
487
|
+
<VisualizationsPanel />
|
|
488
|
+
<Grid />
|
|
505
489
|
</div>
|
|
506
490
|
</DndProvider>
|
|
507
491
|
)
|
|
@@ -509,164 +493,185 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
509
493
|
} else {
|
|
510
494
|
body = (
|
|
511
495
|
<>
|
|
512
|
-
|
|
513
|
-
{isEditor && <Header tabSelected={tabSelected} setTabSelected={setTabSelected} preview={preview} setPreview={setPreview}/>}
|
|
496
|
+
{isEditor && <Header tabSelected={tabSelected} setTabSelected={setTabSelected} preview={preview} setPreview={setPreview} />}
|
|
514
497
|
<div className={`cdc-dashboard-inner-container${isEditor ? ' is-editor' : ''}`}>
|
|
515
498
|
{/* Title */}
|
|
516
|
-
{title &&
|
|
517
|
-
<div role=
|
|
499
|
+
{title && (
|
|
500
|
+
<div role='heading' aria-level='3' className={`dashboard-title ${config.dashboard.theme ?? 'theme-blue'}`}>
|
|
501
|
+
{title}
|
|
502
|
+
</div>
|
|
503
|
+
)}
|
|
518
504
|
{/* Description */}
|
|
519
|
-
{description && <div className=
|
|
505
|
+
{description && <div className='subtext'>{parse(description)}</div>}
|
|
520
506
|
{/* Filters */}
|
|
521
|
-
{config.dashboard.sharedFilters &&
|
|
507
|
+
{config.dashboard.sharedFilters && (
|
|
508
|
+
<div className='cove-dashboard-filters'>
|
|
509
|
+
{' '}
|
|
510
|
+
<Filters />
|
|
511
|
+
</div>
|
|
512
|
+
)}
|
|
522
513
|
|
|
523
514
|
{/* Visualizations */}
|
|
524
|
-
{config.rows &&
|
|
515
|
+
{config.rows &&
|
|
516
|
+
config.rows
|
|
517
|
+
.filter(row => row.filter(col => col.widget).length !== 0)
|
|
518
|
+
.map((row, index) => {
|
|
519
|
+
return (
|
|
520
|
+
<div className={`dashboard-row ${row.equalHeight ? 'equal-height' : ''}`} key={`row__${index}`}>
|
|
521
|
+
{row.map((col, colIndex) => {
|
|
522
|
+
if (col.width) {
|
|
523
|
+
if (!col.widget) return <div key={`row__${index}__col__${colIndex}`} className={`dashboard-col dashboard-col-${col.width}`}></div>
|
|
524
|
+
|
|
525
|
+
let visualizationConfig = { ...config.visualizations[col.widget] }
|
|
526
|
+
|
|
527
|
+
const dataKey = visualizationConfig.dataKey || 'backwards-compatibility'
|
|
528
|
+
|
|
529
|
+
visualizationConfig.data = filteredData && filteredData[col.widget] ? filteredData[col.widget] : data[dataKey]
|
|
530
|
+
if (visualizationConfig.formattedData) {
|
|
531
|
+
visualizationConfig.formattedData = visualizationConfig.data
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const setsSharedFilter = config.dashboard.sharedFilters && config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === col.widget).length > 0
|
|
535
|
+
const setSharedFilterValue = setsSharedFilter ? config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === col.widget)[0].active : undefined
|
|
536
|
+
const tableLink = <a href={`#data-table-${visualizationConfig.dataKey}`}>{visualizationConfig.dataKey} (Go to Table)</a>
|
|
537
|
+
|
|
538
|
+
return (
|
|
539
|
+
<React.Fragment key={`vis__${index}__${colIndex}`}>
|
|
540
|
+
<div className={`dashboard-col dashboard-col-${col.width}`}>
|
|
541
|
+
{visualizationConfig.type === 'chart' && (
|
|
542
|
+
<CdcChart
|
|
543
|
+
key={col.widget}
|
|
544
|
+
config={visualizationConfig}
|
|
545
|
+
isEditor={false}
|
|
546
|
+
setConfig={newConfig => {
|
|
547
|
+
updateChildConfig(col.widget, newConfig)
|
|
548
|
+
}}
|
|
549
|
+
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
550
|
+
isDashboard={true}
|
|
551
|
+
link={config.table && config.table.show && config.datasets ? tableLink : undefined}
|
|
552
|
+
/>
|
|
553
|
+
)}
|
|
554
|
+
{visualizationConfig.type === 'map' && (
|
|
555
|
+
<CdcMap
|
|
556
|
+
key={col.widget}
|
|
557
|
+
config={visualizationConfig}
|
|
558
|
+
isEditor={false}
|
|
559
|
+
setConfig={newConfig => {
|
|
560
|
+
updateChildConfig(col.widget, newConfig)
|
|
561
|
+
}}
|
|
562
|
+
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
563
|
+
setSharedFilterValue={setSharedFilterValue}
|
|
564
|
+
isDashboard={true}
|
|
565
|
+
link={config.table && config.table.show && config.datasets ? tableLink : undefined}
|
|
566
|
+
/>
|
|
567
|
+
)}
|
|
568
|
+
{visualizationConfig.type === 'data-bite' && (
|
|
569
|
+
<CdcDataBite
|
|
570
|
+
key={col.widget}
|
|
571
|
+
config={visualizationConfig}
|
|
572
|
+
isEditor={false}
|
|
573
|
+
setConfig={newConfig => {
|
|
574
|
+
updateChildConfig(col.widget, newConfig)
|
|
575
|
+
}}
|
|
576
|
+
isDashboard={true}
|
|
577
|
+
/>
|
|
578
|
+
)}
|
|
579
|
+
{visualizationConfig.type === 'waffle-chart' && (
|
|
580
|
+
<CdcWaffleChart
|
|
581
|
+
key={col.widget}
|
|
582
|
+
config={visualizationConfig}
|
|
583
|
+
isEditor={false}
|
|
584
|
+
setConfig={newConfig => {
|
|
585
|
+
updateChildConfig(col.widget, newConfig)
|
|
586
|
+
}}
|
|
587
|
+
isDashboard={true}
|
|
588
|
+
link={config.table && config.table.show && config.datasets ? tableLink : undefined}
|
|
589
|
+
/>
|
|
590
|
+
)}
|
|
591
|
+
{visualizationConfig.type === 'markup-include' && (
|
|
592
|
+
<CdcMarkupInclude
|
|
593
|
+
key={col.widget}
|
|
594
|
+
config={visualizationConfig}
|
|
595
|
+
isEditor={false}
|
|
596
|
+
setConfig={newConfig => {
|
|
597
|
+
updateChildConfig(col.widget, newConfig)
|
|
598
|
+
}}
|
|
599
|
+
isDashboard={true}
|
|
600
|
+
/>
|
|
601
|
+
)}
|
|
602
|
+
{visualizationConfig.type === 'filtered-text' && (
|
|
603
|
+
<CdcFilteredText
|
|
604
|
+
key={col.widget}
|
|
605
|
+
config={visualizationConfig}
|
|
606
|
+
isEditor={false}
|
|
607
|
+
setConfig={newConfig => {
|
|
608
|
+
updateChildConfig(col.widget, newConfig)
|
|
609
|
+
}}
|
|
610
|
+
isDashboard={true}
|
|
611
|
+
/>
|
|
612
|
+
)}
|
|
613
|
+
</div>
|
|
614
|
+
</React.Fragment>
|
|
615
|
+
)
|
|
616
|
+
}
|
|
617
|
+
return <React.Fragment key={`vis__${index}__${colIndex}`}></React.Fragment>
|
|
618
|
+
})}
|
|
619
|
+
</div>
|
|
620
|
+
)
|
|
621
|
+
})}
|
|
622
|
+
|
|
623
|
+
{/* Image or PDF Inserts */}
|
|
624
|
+
<section className='download-buttons'>
|
|
625
|
+
{config.table.downloadImageButton && <CoveMediaControls.Button title='Download Dashboard as Image' type='image' state={config} text='Download Dashboard Image' elementToCapture={imageId} />}
|
|
626
|
+
{config.table.downloadPdfButton && <CoveMediaControls.Button title='Download Dashboard as PDF' type='pdf' state={config} text='Download Dashboard PDF' elementToCapture={imageId} />}
|
|
627
|
+
</section>
|
|
525
628
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
629
|
+
{/* Data Table */}
|
|
630
|
+
{config.table && config.data && <DataTable data={config.data} config={config} imageRef={imageId} />}
|
|
631
|
+
{config.table &&
|
|
632
|
+
config.datasets &&
|
|
633
|
+
Object.keys(config.datasets).map(datasetKey => {
|
|
634
|
+
//For each dataset, find any shared filters that apply to all visualizations using the dataset
|
|
635
|
+
//Apply these filters to the table
|
|
636
|
+
let filteredTableData
|
|
637
|
+
if (config.dashboard.sharedFilters && config.dashboard.sharedFilters.length > 0) {
|
|
638
|
+
//Gets list of visuailzations using the dataset
|
|
639
|
+
let vizKeysUsingDataset = []
|
|
640
|
+
Object.keys(config.visualizations).forEach(visualizationKey => {
|
|
641
|
+
if (config.visualizations[visualizationKey].dataKey === datasetKey) {
|
|
642
|
+
vizKeysUsingDataset.push(visualizationKey)
|
|
643
|
+
}
|
|
644
|
+
})
|
|
645
|
+
|
|
646
|
+
//Checks shared filters against list to see if all visualizations are represented
|
|
647
|
+
let applicableFilters = []
|
|
648
|
+
config.dashboard.sharedFilters.forEach(sharedFilter => {
|
|
649
|
+
let allMatch = true
|
|
650
|
+
vizKeysUsingDataset.forEach(visualizationKey => {
|
|
651
|
+
if (sharedFilter.usedBy.indexOf(visualizationKey) === -1) {
|
|
652
|
+
allMatch = false
|
|
540
653
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
const tableLink = <a href={`#data-table-${visualizationConfig.dataKey}`}>{visualizationConfig.dataKey} (Go to Table)</a>;
|
|
545
|
-
|
|
546
|
-
return (
|
|
547
|
-
<React.Fragment key={`vis__${index}__${colIndex}`}>
|
|
548
|
-
<div className={`dashboard-col dashboard-col-${col.width}`}>
|
|
549
|
-
{visualizationConfig.type === 'chart' && (
|
|
550
|
-
<CdcChart
|
|
551
|
-
key={col.widget}
|
|
552
|
-
config={visualizationConfig}
|
|
553
|
-
isEditor={false}
|
|
554
|
-
setConfig={(newConfig) => {
|
|
555
|
-
updateChildConfig(col.widget, newConfig)
|
|
556
|
-
}}
|
|
557
|
-
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
558
|
-
isDashboard={true}
|
|
559
|
-
link = { config.table && config.table.show && config.datasets ? tableLink : undefined }
|
|
560
|
-
/>
|
|
561
|
-
)}
|
|
562
|
-
{visualizationConfig.type === 'map' && (
|
|
563
|
-
<CdcMap
|
|
564
|
-
key={col.widget}
|
|
565
|
-
config={visualizationConfig}
|
|
566
|
-
isEditor={false}
|
|
567
|
-
setConfig={(newConfig) => {
|
|
568
|
-
updateChildConfig(col.widget, newConfig)
|
|
569
|
-
}}
|
|
570
|
-
setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
|
|
571
|
-
setSharedFilterValue={setSharedFilterValue}
|
|
572
|
-
isDashboard={true}
|
|
573
|
-
link = { config.table && config.table.show && config.datasets ? tableLink : undefined }
|
|
574
|
-
/>
|
|
575
|
-
)}
|
|
576
|
-
{visualizationConfig.type === 'data-bite' && (
|
|
577
|
-
<CdcDataBite
|
|
578
|
-
key={col.widget}
|
|
579
|
-
config={visualizationConfig}
|
|
580
|
-
isEditor={false}
|
|
581
|
-
setConfig={(newConfig) => {
|
|
582
|
-
updateChildConfig(col.widget, newConfig)
|
|
583
|
-
}}
|
|
584
|
-
isDashboard={true}
|
|
585
|
-
/>
|
|
586
|
-
)}
|
|
587
|
-
{visualizationConfig.type === 'waffle-chart' && (
|
|
588
|
-
<CdcWaffleChart
|
|
589
|
-
key={col.widget}
|
|
590
|
-
config={visualizationConfig}
|
|
591
|
-
isEditor={false}
|
|
592
|
-
setConfig={(newConfig) => {
|
|
593
|
-
updateChildConfig(col.widget, newConfig)
|
|
594
|
-
}}
|
|
595
|
-
isDashboard={true}
|
|
596
|
-
link = { config.table && config.table.show && config.datasets ? tableLink : undefined }
|
|
597
|
-
/>
|
|
598
|
-
)}
|
|
599
|
-
{visualizationConfig.type === 'markup-include' && (
|
|
600
|
-
<CdcMarkupInclude
|
|
601
|
-
key={col.widget}
|
|
602
|
-
config={visualizationConfig}
|
|
603
|
-
isEditor={false}
|
|
604
|
-
setConfig={(newConfig) => {
|
|
605
|
-
updateChildConfig(col.widget, newConfig)
|
|
606
|
-
}}
|
|
607
|
-
isDashboard={true}
|
|
608
|
-
/>
|
|
609
|
-
)}
|
|
610
|
-
{visualizationConfig.type === 'filtered-text' && (
|
|
611
|
-
<CdcFilteredText
|
|
612
|
-
key={col.widget}
|
|
613
|
-
config={visualizationConfig}
|
|
614
|
-
isEditor={false}
|
|
615
|
-
setConfig={(newConfig) => {
|
|
616
|
-
updateChildConfig(col.widget, newConfig)
|
|
617
|
-
}}
|
|
618
|
-
isDashboard={true}
|
|
619
|
-
/>
|
|
620
|
-
)}
|
|
621
|
-
</div>
|
|
622
|
-
</React.Fragment>
|
|
623
|
-
)
|
|
654
|
+
})
|
|
655
|
+
if (allMatch) {
|
|
656
|
+
applicableFilters.push(sharedFilter)
|
|
624
657
|
}
|
|
625
|
-
|
|
626
|
-
})}
|
|
627
|
-
</div>)
|
|
628
|
-
})}
|
|
658
|
+
})
|
|
629
659
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
//For each dataset, find any shared filters that apply to all visualizations using the dataset
|
|
635
|
-
//Apply these filters to the table
|
|
636
|
-
let filteredTableData;
|
|
637
|
-
if(config.dashboard.sharedFilters && config.dashboard.sharedFilters.length > 0){
|
|
638
|
-
//Gets list of visuailzations using the dataset
|
|
639
|
-
let vizKeysUsingDataset = [];
|
|
640
|
-
Object.keys(config.visualizations).forEach(visualizationKey => {
|
|
641
|
-
if(config.visualizations[visualizationKey].dataKey === datasetKey){
|
|
642
|
-
vizKeysUsingDataset.push(visualizationKey);
|
|
660
|
+
//Applys any applicable filters
|
|
661
|
+
if (applicableFilters.length > 0) {
|
|
662
|
+
filteredTableData = filterData(applicableFilters, config.datasets[datasetKey].data)
|
|
643
663
|
}
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
//Checks shared filters against list to see if all visualizations are represented
|
|
647
|
-
let applicableFilters = [];
|
|
648
|
-
config.dashboard.sharedFilters.forEach(sharedFilter => {
|
|
649
|
-
let allMatch = true;
|
|
650
|
-
vizKeysUsingDataset.forEach(visualizationKey => {
|
|
651
|
-
if(sharedFilter.usedBy.indexOf(visualizationKey) === -1){
|
|
652
|
-
allMatch = false;
|
|
653
|
-
}
|
|
654
|
-
});
|
|
655
|
-
if(allMatch){
|
|
656
|
-
applicableFilters.push(sharedFilter);
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
//Applys any applicable filters
|
|
661
|
-
if(applicableFilters.length > 0){
|
|
662
|
-
filteredTableData = filterData(applicableFilters, config.datasets[datasetKey].data)
|
|
663
664
|
}
|
|
664
|
-
}
|
|
665
665
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
666
|
+
|
|
667
|
+
let dataFileSourceType = config.datasets[datasetKey]?.dataFileSourceType
|
|
668
|
+
|
|
669
|
+
return (
|
|
670
|
+
<div className='multi-table-container' id={`data-table-${datasetKey}`} key={`data-table-${datasetKey}`}>
|
|
671
|
+
<DataTable data={filteredTableData || config.datasets[datasetKey].data} dataFileSourceType={dataFileSourceType} datasetKey={datasetKey} config={config} imageRef={imageId}></DataTable>
|
|
672
|
+
</div>
|
|
673
|
+
)
|
|
674
|
+
})}
|
|
670
675
|
</div>
|
|
671
676
|
</>
|
|
672
677
|
)
|
|
@@ -681,16 +686,24 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
681
686
|
loading,
|
|
682
687
|
updateConfig,
|
|
683
688
|
setParentConfig,
|
|
684
|
-
setPreview
|
|
689
|
+
setPreview,
|
|
690
|
+
outerContainerRef
|
|
685
691
|
}
|
|
686
692
|
|
|
693
|
+
|
|
694
|
+
const dashboardContainerClasses = [
|
|
695
|
+
'cdc-open-viz-module',
|
|
696
|
+
'type-dashboard',
|
|
697
|
+
`${currentViewport}`
|
|
698
|
+
]
|
|
699
|
+
|
|
687
700
|
return (
|
|
688
701
|
<GlobalContextProvider>
|
|
689
702
|
<ConfigContext.Provider value={contextValues}>
|
|
690
|
-
<div className={
|
|
703
|
+
<div className={dashboardContainerClasses.join(' ')} ref={outerContainerRef} data-download-id={imageId} >
|
|
691
704
|
{body}
|
|
692
705
|
</div>
|
|
693
|
-
<OverlayFrame/>
|
|
706
|
+
<OverlayFrame />
|
|
694
707
|
</ConfigContext.Provider>
|
|
695
708
|
</GlobalContextProvider>
|
|
696
709
|
)
|