@cdc/dashboard 4.23.6 → 4.23.8
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/dist/cdcdashboard.js +74310 -73096
- package/examples/default-filter-control.json +3 -0
- package/examples/shared-filters.json +60 -0
- package/index.html +2 -2
- package/package.json +9 -9
- package/src/CdcDashboard.jsx +32 -24
- package/src/components/Header.jsx +15 -1
- package/src/components/Widget.jsx +61 -6
- package/src/scss/main.scss +5 -7
|
@@ -535,6 +535,66 @@
|
|
|
535
535
|
"state": "Florida"
|
|
536
536
|
}
|
|
537
537
|
]
|
|
538
|
+
},
|
|
539
|
+
"data3": {
|
|
540
|
+
"data": [
|
|
541
|
+
{
|
|
542
|
+
"Insured Rate": 8,
|
|
543
|
+
"Coverage Status": "Insured",
|
|
544
|
+
"FIPS Codes": "06071",
|
|
545
|
+
"Year (Good filter option)": "2010",
|
|
546
|
+
"link": ""
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"Insured Rate": 0,
|
|
550
|
+
"Coverage Status": "Insured",
|
|
551
|
+
"FIPS Codes": "55005",
|
|
552
|
+
"Year (Good filter option)": "2010",
|
|
553
|
+
"link": "https://cdc.gov"
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
"Insured Rate": 25,
|
|
557
|
+
"Coverage Status": "Insured",
|
|
558
|
+
"FIPS Codes": "12103",
|
|
559
|
+
"Year (Good filter option)": "2010",
|
|
560
|
+
"link": ""
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
"Insured Rate": 25,
|
|
564
|
+
"Coverage Status": "Insured",
|
|
565
|
+
"FIPS Codes": "32005",
|
|
566
|
+
"Year (Good filter option)": "2010",
|
|
567
|
+
"link": ""
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
"Insured Rate": 18,
|
|
571
|
+
"Coverage Status": "Insured",
|
|
572
|
+
"FIPS Codes": "06075",
|
|
573
|
+
"Year (Good filter option)": "2010",
|
|
574
|
+
"link": ""
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
"Insured Rate": 3,
|
|
578
|
+
"Coverage Status": "Insured",
|
|
579
|
+
"FIPS Codes": "06027",
|
|
580
|
+
"Year (Good filter option)": "2010",
|
|
581
|
+
"link": ""
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
"Insured Rate": 25,
|
|
585
|
+
"Coverage Status": "Insured",
|
|
586
|
+
"FIPS Codes": "06029",
|
|
587
|
+
"Year (Good filter option)": "2010",
|
|
588
|
+
"link": ""
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
"Insured Rate": 60,
|
|
592
|
+
"Coverage Status": "Insured",
|
|
593
|
+
"FIPS Codes": "06065",
|
|
594
|
+
"Year (Good filter option)": "2010",
|
|
595
|
+
"link": ""
|
|
596
|
+
}
|
|
597
|
+
]
|
|
538
598
|
}
|
|
539
599
|
},
|
|
540
600
|
"runtime": {},
|
package/index.html
CHANGED
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
<body>
|
|
22
22
|
<!-- <div class="react-container" data-config="/examples/default.json"></div> -->
|
|
23
23
|
<!-- <div class="react-container" data-config="/examples/default-multi-dataset.json"></div> -->
|
|
24
|
-
|
|
25
|
-
<div class="react-container" data-config="/examples/
|
|
24
|
+
<div class="react-container" data-config="/examples/default-filter-control.json"></div>
|
|
25
|
+
<!-- <div class="react-container" data-config="/examples/private/bar-color.json"></div> -->
|
|
26
26
|
<!-- <div class="react-container" data-config="/examples/private/totals.json"></div> -->
|
|
27
27
|
<!-- <div class="react-container" data-config="/examples/private/totals-two.json"></div> -->
|
|
28
28
|
<script type="module" src="./src/index.jsx"></script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/dashboard",
|
|
3
|
-
"version": "4.23.
|
|
3
|
+
"version": "4.23.8",
|
|
4
4
|
"description": "React component for combining multiple visualizations into a single dashboard",
|
|
5
5
|
"moduleName": "CdcDashboard",
|
|
6
6
|
"main": "dist/cdcdashboard",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
},
|
|
26
26
|
"license": "Apache-2.0",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@cdc/chart": "^4.23.
|
|
29
|
-
"@cdc/core": "^4.23.
|
|
30
|
-
"@cdc/data-bite": "^4.23.
|
|
31
|
-
"@cdc/filtered-text": "^4.23.
|
|
32
|
-
"@cdc/map": "^4.23.
|
|
33
|
-
"@cdc/markup-include": "^4.23.
|
|
34
|
-
"@cdc/waffle-chart": "^4.23.
|
|
28
|
+
"@cdc/chart": "^4.23.8",
|
|
29
|
+
"@cdc/core": "^4.23.8",
|
|
30
|
+
"@cdc/data-bite": "^4.23.8",
|
|
31
|
+
"@cdc/filtered-text": "^4.23.8",
|
|
32
|
+
"@cdc/map": "^4.23.8",
|
|
33
|
+
"@cdc/markup-include": "^4.23.8",
|
|
34
|
+
"@cdc/waffle-chart": "^4.23.8",
|
|
35
35
|
"html-react-parser": "^3.0.8",
|
|
36
36
|
"js-base64": "^2.5.2",
|
|
37
37
|
"papaparse": "^5.3.0",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"react": "^18.2.0",
|
|
48
48
|
"react-dom": "^18.2.0"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "ba0a072a40c430baf121ad5ece0165f52a414b86"
|
|
51
51
|
}
|
package/src/CdcDashboard.jsx
CHANGED
|
@@ -97,6 +97,7 @@ const VisualizationsPanel = ({ loadConfig, config }) => (
|
|
|
97
97
|
<Widget addVisualization={() => addVisualization('waffle-chart', '')} type='waffle-chart' />
|
|
98
98
|
<Widget addVisualization={() => addVisualization('markup-include', '')} type='markup-include' />
|
|
99
99
|
<Widget addVisualization={() => addVisualization('filtered-text', '')} type='filtered-text' />
|
|
100
|
+
<Widget addVisualization={() => addVisualization('filter-dropdowns', '')} type='filter-dropdowns' />
|
|
100
101
|
</div>
|
|
101
102
|
<span className='subheading-3'>Advanced</span>
|
|
102
103
|
<AdvancedEditor loadConfig={loadConfig} state={config} />
|
|
@@ -283,6 +284,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
283
284
|
let add = true
|
|
284
285
|
|
|
285
286
|
filters.forEach(filter => {
|
|
287
|
+
// eslint-disable-next-line eqeqeq
|
|
286
288
|
if (filter.type !== 'url' && row[filter.columnName] != filter.active) {
|
|
287
289
|
add = false
|
|
288
290
|
}
|
|
@@ -416,7 +418,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
416
418
|
setConfig(updatedConfig)
|
|
417
419
|
}
|
|
418
420
|
|
|
419
|
-
const Filters = () => {
|
|
421
|
+
const Filters = ({ hide }) => {
|
|
420
422
|
const changeFilterActive = (index, value) => {
|
|
421
423
|
let dashboardConfig = { ...config.dashboard }
|
|
422
424
|
|
|
@@ -445,7 +447,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
445
447
|
const announceChange = text => {}
|
|
446
448
|
|
|
447
449
|
return config.dashboard.sharedFilters.map((singleFilter, index) => {
|
|
448
|
-
if (singleFilter.type !== 'url' && !singleFilter.showDropdown) return <></>
|
|
450
|
+
if ((singleFilter.type !== 'url' && !singleFilter.showDropdown) || (hide && hide.indexOf(index) !== -1)) return <></>
|
|
449
451
|
|
|
450
452
|
const values = []
|
|
451
453
|
|
|
@@ -466,21 +468,23 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
466
468
|
})
|
|
467
469
|
|
|
468
470
|
return (
|
|
469
|
-
<
|
|
470
|
-
<
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
471
|
+
<div className='cove-dashboard-filters'>
|
|
472
|
+
<section className='dashboard-filters-section' key={`${singleFilter.key}-filtersection-${index}`}>
|
|
473
|
+
<label htmlFor={`filter-${index}`}>{singleFilter.key}</label>
|
|
474
|
+
<select
|
|
475
|
+
id={`filter-${index}`}
|
|
476
|
+
className='filter-select'
|
|
477
|
+
data-index='0'
|
|
478
|
+
value={singleFilter.active}
|
|
479
|
+
onChange={val => {
|
|
480
|
+
changeFilterActive(index, val.target.value)
|
|
481
|
+
announceChange(`Filter ${singleFilter.key} value has been changed to ${val.target.value}, please reference the data table to see updated values.`)
|
|
482
|
+
}}
|
|
483
|
+
>
|
|
484
|
+
{values}
|
|
485
|
+
</select>
|
|
486
|
+
</section>
|
|
487
|
+
</div>
|
|
484
488
|
)
|
|
485
489
|
})
|
|
486
490
|
}
|
|
@@ -596,6 +600,14 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
596
600
|
</>
|
|
597
601
|
)
|
|
598
602
|
break
|
|
603
|
+
case 'filter-dropdowns':
|
|
604
|
+
body = (
|
|
605
|
+
<>
|
|
606
|
+
<Header tabSelected={tabSelected} setTabSelected={setTabSelected} back={back} subEditor='Filter Dropdowns' />
|
|
607
|
+
<Filters hide={visualizationConfig.hide} />
|
|
608
|
+
</>
|
|
609
|
+
)
|
|
610
|
+
break
|
|
599
611
|
default:
|
|
600
612
|
body = <></>
|
|
601
613
|
break
|
|
@@ -622,18 +634,13 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
622
634
|
{/* Title */}
|
|
623
635
|
{title && (
|
|
624
636
|
<div role='heading' aria-level='3' className={`dashboard-title ${config.dashboard.theme ?? 'theme-blue'}`}>
|
|
625
|
-
{title}
|
|
637
|
+
{parse(title)}
|
|
626
638
|
</div>
|
|
627
639
|
)}
|
|
628
640
|
{/* Description */}
|
|
629
641
|
{description && <div className='subtext'>{parse(description)}</div>}
|
|
630
642
|
{/* Filters */}
|
|
631
|
-
{config.dashboard.sharedFilters && (
|
|
632
|
-
<div className='cove-dashboard-filters'>
|
|
633
|
-
{' '}
|
|
634
|
-
<Filters />
|
|
635
|
-
</div>
|
|
636
|
-
)}
|
|
643
|
+
{config.dashboard.sharedFilters && Object.keys(config.visualizations).filter(vizKey => config.visualizations[vizKey].visualizationType === 'filter-dropdowns').length === 0 && <Filters />}
|
|
637
644
|
|
|
638
645
|
{/* Visualizations */}
|
|
639
646
|
{config.rows &&
|
|
@@ -748,6 +755,7 @@ export default function CdcDashboard({ configUrl = '', config: configObj = undef
|
|
|
748
755
|
isDashboard={true}
|
|
749
756
|
/>
|
|
750
757
|
)}
|
|
758
|
+
{visualizationConfig.type === 'filter-dropdowns' && <Filters hide={visualizationConfig.hide} />}
|
|
751
759
|
</div>
|
|
752
760
|
</React.Fragment>
|
|
753
761
|
)
|
|
@@ -45,9 +45,23 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
45
45
|
|
|
46
46
|
const removeFilter = index => {
|
|
47
47
|
let dashboardConfig = { ...config.dashboard }
|
|
48
|
+
let visualizations = { ...config.visualizations }
|
|
48
49
|
|
|
49
50
|
dashboardConfig.sharedFilters.splice(index, 1)
|
|
50
51
|
|
|
52
|
+
Object.keys(visualizations).forEach(vizKey => {
|
|
53
|
+
if(visualizations[vizKey].visualizationType === 'filter-dropdowns' && visualizations[vizKey].hide && visualizations[vizKey].hide.length > 0){
|
|
54
|
+
if(visualizations[vizKey].hide.indexOf(index) !== -1){
|
|
55
|
+
visualizations[vizKey].hide.splice(visualizations[vizKey].hide.indexOf(index), 1)
|
|
56
|
+
}
|
|
57
|
+
visualizations[vizKey].hide.forEach((hideIndex, i) => {
|
|
58
|
+
if(hideIndex > index){
|
|
59
|
+
visualizations[vizKey].hide[i] = hideIndex - 1
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
51
65
|
// Ensures URL filters refresh after filter removal
|
|
52
66
|
if (dashboardConfig.datasets) {
|
|
53
67
|
Object.keys(dashboardConfig.datasets).forEach(datasetKey => {
|
|
@@ -55,7 +69,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
55
69
|
})
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
updateConfig({ ...config, dashboard: dashboardConfig })
|
|
72
|
+
updateConfig({ ...config, visualizations, dashboard: dashboardConfig })
|
|
59
73
|
|
|
60
74
|
overlay?.actions.toggleOverlay()
|
|
61
75
|
}
|
|
@@ -25,7 +25,8 @@ const iconHash = {
|
|
|
25
25
|
'single-state': <Icon display='mapAl' base />,
|
|
26
26
|
gear: <Icon display='gear' base />,
|
|
27
27
|
tools: <Icon display='tools' base />,
|
|
28
|
-
'filtered-text': <Icon display='filtered-text' base
|
|
28
|
+
'filtered-text': <Icon display='filtered-text' base />,
|
|
29
|
+
'filter-dropdowns': <Icon display='filter-dropdowns' base />
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const labelHash = {
|
|
@@ -40,7 +41,8 @@ const labelHash = {
|
|
|
40
41
|
'us-county': 'United States (State- or County-Level)',
|
|
41
42
|
world: 'World',
|
|
42
43
|
'single-state': 'U.S. State',
|
|
43
|
-
'filtered-text': '
|
|
44
|
+
'filtered-text': 'Filtered Text',
|
|
45
|
+
'filter-dropdowns': 'Filter Dropdowns'
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
const Widget = ({ data = {}, addVisualization, type }) => {
|
|
@@ -175,9 +177,62 @@ const Widget = ({ data = {}, addVisualization, type }) => {
|
|
|
175
177
|
)
|
|
176
178
|
}
|
|
177
179
|
|
|
180
|
+
const filterHideModal = (configureData) => {
|
|
181
|
+
|
|
182
|
+
const onFilterHideChange = (e, index) => {
|
|
183
|
+
const visualizations = {...config.visualizations}
|
|
184
|
+
|
|
185
|
+
const currentVizKey = Object.keys(visualizations).find(vizKey => vizKey === configureData.uid)
|
|
186
|
+
|
|
187
|
+
if(currentVizKey){
|
|
188
|
+
const currentVizConfig = visualizations[currentVizKey]
|
|
189
|
+
|
|
190
|
+
if(currentVizConfig){
|
|
191
|
+
if(!currentVizConfig.hide) currentVizConfig.hide = []
|
|
192
|
+
if(!e.target.checked && currentVizConfig.hide.indexOf(index) === -1) {
|
|
193
|
+
visualizations[currentVizKey].hide.push(index)
|
|
194
|
+
} else if(e.target.checked && currentVizConfig.hide.indexOf(index) !== -1) {
|
|
195
|
+
visualizations[currentVizKey].hide.splice(currentVizConfig.hide.indexOf(index), 1)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
updateConfig({...config, visualizations})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
overlay?.actions.toggleOverlay()
|
|
204
|
+
|
|
205
|
+
return (
|
|
206
|
+
<Modal>
|
|
207
|
+
<Modal.Content>
|
|
208
|
+
<div>
|
|
209
|
+
Choose which filters to display:
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
{config.dashboard.sharedFilters && config.dashboard.sharedFilters.length > 0 && config.dashboard.sharedFilters.map((sharedFilter, index) => (
|
|
213
|
+
<label>
|
|
214
|
+
<input type="checkbox" defaultChecked={!configureData.hide || configureData.hide.indexOf(index) === -1} onChange={(e) => onFilterHideChange(e, index)} />
|
|
215
|
+
{sharedFilter.key}
|
|
216
|
+
</label>
|
|
217
|
+
))}
|
|
218
|
+
|
|
219
|
+
{(!config.dashboard.sharedFilters || config.dashboard.sharedFilters.length === 0) && (
|
|
220
|
+
<>No dashboard filters added yet.</>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
<div>
|
|
224
|
+
<button style={{ margin: '1em' }} className='cove-button' onClick={() => overlay?.actions.toggleOverlay()}>
|
|
225
|
+
Continue
|
|
226
|
+
</button>
|
|
227
|
+
</div>
|
|
228
|
+
</Modal.Content>
|
|
229
|
+
</Modal>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
178
233
|
useEffect(() => {
|
|
179
234
|
if (data.openModal) {
|
|
180
|
-
overlay?.actions.openOverlay(dataDesignerModal(dataRef.current))
|
|
235
|
+
overlay?.actions.openOverlay(type === 'filter-dropdowns' ? filterHideModal(dataRef.current) : dataDesignerModal(dataRef.current))
|
|
181
236
|
|
|
182
237
|
visualizations[data.uid].openModal = false
|
|
183
238
|
|
|
@@ -192,7 +247,7 @@ const Widget = ({ data = {}, addVisualization, type }) => {
|
|
|
192
247
|
<div className='widget__content'>
|
|
193
248
|
{data.rowIdx !== undefined && (
|
|
194
249
|
<div className='widget-menu'>
|
|
195
|
-
{((data.dataKey && data.dataDescription && data.formattedData) || type === 'markup-include') && (
|
|
250
|
+
{((data.dataKey && data.dataDescription && data.formattedData) || type === 'markup-include') && type !== 'filter-dropdowns' && (
|
|
196
251
|
<button title='Configure Visualization' className='btn btn-configure' onClick={editWidget}>
|
|
197
252
|
{iconHash['tools']}
|
|
198
253
|
</button>
|
|
@@ -202,7 +257,7 @@ const Widget = ({ data = {}, addVisualization, type }) => {
|
|
|
202
257
|
title='Configure Data'
|
|
203
258
|
className='btn btn-configure'
|
|
204
259
|
onClick={() => {
|
|
205
|
-
overlay?.actions.openOverlay(dataDesignerModal(data))
|
|
260
|
+
overlay?.actions.openOverlay(type === 'filter-dropdowns' ? filterHideModal(data) : dataDesignerModal(data))
|
|
206
261
|
}}
|
|
207
262
|
>
|
|
208
263
|
{iconHash['gear']}
|
|
@@ -215,7 +270,7 @@ const Widget = ({ data = {}, addVisualization, type }) => {
|
|
|
215
270
|
)}
|
|
216
271
|
{iconHash[type]}
|
|
217
272
|
<span>{labelHash[type]}</span>
|
|
218
|
-
{data.newViz && (
|
|
273
|
+
{data.newViz && type !== 'filter-dropdowns' && (
|
|
219
274
|
<span onClick={editWidget} className='config-needed'>
|
|
220
275
|
Configuration needed
|
|
221
276
|
</span>
|
package/src/scss/main.scss
CHANGED
|
@@ -239,6 +239,11 @@
|
|
|
239
239
|
width: 100%;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
.cove-dashboard-filters {
|
|
243
|
+
display: inline-flex;
|
|
244
|
+
margin: 1em;
|
|
245
|
+
}
|
|
246
|
+
|
|
242
247
|
@include breakpointClass(md) {
|
|
243
248
|
.dashboard-row {
|
|
244
249
|
flex-direction: row;
|
|
@@ -271,13 +276,6 @@
|
|
|
271
276
|
}
|
|
272
277
|
}
|
|
273
278
|
|
|
274
|
-
@include breakpoint(md) {
|
|
275
|
-
.cove-dashboard-filters {
|
|
276
|
-
display: flex;
|
|
277
|
-
gap: 30px;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
279
|
.dashboard-filters-section {
|
|
282
280
|
margin: 0 0 1em;
|
|
283
281
|
}
|