@cdc/dashboard 4.23.5 → 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.
- package/dist/cdcdashboard.js +73277 -69815
- package/examples/shared-filters.json +542 -0
- package/index.html +2 -1
- package/package.json +9 -9
- package/src/CdcDashboard.jsx +22 -8
- package/src/components/DataTable.tsx +4 -4
- package/src/components/Header.jsx +151 -149
|
@@ -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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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>
|
|
@@ -49,7 +49,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
49
49
|
dashboardConfig.sharedFilters.splice(index, 1)
|
|
50
50
|
|
|
51
51
|
// Ensures URL filters refresh after filter removal
|
|
52
|
-
if(dashboardConfig.datasets){
|
|
52
|
+
if (dashboardConfig.datasets) {
|
|
53
53
|
Object.keys(dashboardConfig.datasets).forEach(datasetKey => {
|
|
54
54
|
delete dashboardConfig.datasets[datasetKey].runtimeDataUrl
|
|
55
55
|
})
|
|
@@ -165,11 +165,10 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
165
165
|
|
|
166
166
|
newLabels[value] = e.target.value
|
|
167
167
|
|
|
168
|
-
|
|
169
168
|
updateFilterProp('labels', index, newLabels)
|
|
170
169
|
}
|
|
171
170
|
|
|
172
|
-
const removeValue =
|
|
171
|
+
const removeValue = valueIndex => {
|
|
173
172
|
let newLabels = filter.labels || {}
|
|
174
173
|
let newValues = filter.values || []
|
|
175
174
|
|
|
@@ -183,7 +182,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
183
182
|
})
|
|
184
183
|
}
|
|
185
184
|
|
|
186
|
-
const addNewValue =
|
|
185
|
+
const addNewValue = e => {
|
|
187
186
|
e.preventDefault()
|
|
188
187
|
if (!filter.values || filter.values.indexOf(e.target[0].value) === -1) {
|
|
189
188
|
let newValues = filter.values || []
|
|
@@ -216,146 +215,149 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
216
215
|
Remove Filter
|
|
217
216
|
</button>
|
|
218
217
|
<label>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
{filter.type === 'url' &&
|
|
226
|
-
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
<
|
|
238
|
-
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
value={filter.labels ? filter.labels[value] : undefined}
|
|
265
|
-
onChange={(e) => updateLabel(e, value)}
|
|
266
|
-
/>
|
|
267
|
-
<button
|
|
268
|
-
onClick={() => removeValue(valueIndex)}
|
|
269
|
-
>
|
|
270
|
-
X
|
|
271
|
-
</button>
|
|
272
|
-
</li>
|
|
273
|
-
))}
|
|
274
|
-
</ul>
|
|
275
|
-
<form
|
|
276
|
-
onSubmit={addNewValue}
|
|
277
|
-
>
|
|
278
|
-
<input type='text' /> <button type='submit'>Add New Value</button>
|
|
279
|
-
</form>
|
|
280
|
-
</>}
|
|
281
|
-
{filter.type !== 'url' && <>
|
|
282
|
-
<label>
|
|
283
|
-
<span className='edit-label column-heading'>Filter: </span>
|
|
284
|
-
<select
|
|
285
|
-
value={filter.columnName}
|
|
286
|
-
onChange={e => {
|
|
287
|
-
updateFilterProp('columnName', index, e.target.value)
|
|
288
|
-
}}
|
|
289
|
-
>
|
|
290
|
-
<option value=''>- Select Option -</option>
|
|
291
|
-
{columns.map(dataKey => (
|
|
292
|
-
<option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
|
|
293
|
-
{dataKey}
|
|
294
|
-
</option>
|
|
295
|
-
))}
|
|
296
|
-
</select>
|
|
297
|
-
</label>
|
|
298
|
-
<label>
|
|
299
|
-
<span className='edit-label column-heading'>Label: </span>
|
|
300
|
-
<input
|
|
301
|
-
type='text'
|
|
302
|
-
value={filter.key}
|
|
303
|
-
onChange={e => {
|
|
304
|
-
updateFilterProp('key', index, e.target.value)
|
|
305
|
-
}}
|
|
306
|
-
/>
|
|
307
|
-
</label>
|
|
308
|
-
<label>
|
|
309
|
-
<span className='edit-label column-heading'>Show Dropdown</span>
|
|
310
|
-
<input
|
|
311
|
-
type='checkbox'
|
|
312
|
-
defaultChecked={filter.showDropdown === true}
|
|
313
|
-
onChange={e => {
|
|
314
|
-
updateFilterProp('showDropdown', index, !filter.showDropdown)
|
|
315
|
-
}}
|
|
316
|
-
/>
|
|
317
|
-
</label>
|
|
318
|
-
<label>
|
|
319
|
-
<span className='edit-label column-heading'>Set By: </span>
|
|
320
|
-
<select value={filter.setBy} onChange={e => updateFilterProp('setBy', index, e.target.value)}>
|
|
321
|
-
<option value=''>- Select Option -</option>
|
|
322
|
-
{Object.keys(config.visualizations).map(vizKey => (
|
|
323
|
-
<option value={vizKey} key={`set-by-select-item-${vizKey}`}>
|
|
324
|
-
{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
|
|
325
|
-
</option>
|
|
326
|
-
))}
|
|
327
|
-
</select>
|
|
328
|
-
</label>
|
|
329
|
-
<label>
|
|
330
|
-
<span className='edit-label column-heading'>Used By:</span>
|
|
331
|
-
<ul>
|
|
332
|
-
{filter.usedBy &&
|
|
333
|
-
filter.usedBy.map(vizKey => (
|
|
334
|
-
<li key={`used-by-list-item-${vizKey}`}>
|
|
335
|
-
<span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
|
|
336
|
-
<button
|
|
337
|
-
onClick={e => {
|
|
338
|
-
e.preventDefault()
|
|
339
|
-
removeFilterUsedBy(filter, index, vizKey)
|
|
340
|
-
}}
|
|
341
|
-
>
|
|
342
|
-
X
|
|
343
|
-
</button>
|
|
218
|
+
<span className='edit-label column-heading'>Filter Type:</span>
|
|
219
|
+
<select defaultValue={filter.type || 'data'} onChange={e => updateFilterProp('type', index, e.target.value)}>
|
|
220
|
+
<option value='url'>URL</option>
|
|
221
|
+
<option value='data'>Data</option>
|
|
222
|
+
</select>
|
|
223
|
+
</label>
|
|
224
|
+
{filter.type === 'url' && (
|
|
225
|
+
<>
|
|
226
|
+
<label>
|
|
227
|
+
<span className='edit-label column-heading'>Label: </span>
|
|
228
|
+
<input
|
|
229
|
+
type='text'
|
|
230
|
+
value={filter.key}
|
|
231
|
+
onChange={e => {
|
|
232
|
+
updateFilterProp('key', index, e.target.value)
|
|
233
|
+
}}
|
|
234
|
+
/>
|
|
235
|
+
</label>
|
|
236
|
+
<label>
|
|
237
|
+
<span className='edit-label column-heading'>URL to Filter: </span>
|
|
238
|
+
<select defaultValue={filter.datasetKey || ''} onChange={e => updateFilterProp('datasetKey', index, e.target.value)}>
|
|
239
|
+
<option value=''>- Select Option -</option>
|
|
240
|
+
{Object.keys(config.datasets).map(datasetKey => {
|
|
241
|
+
if (config.datasets[datasetKey].dataUrl) {
|
|
242
|
+
return (
|
|
243
|
+
<option key={datasetKey} value={datasetKey}>
|
|
244
|
+
{config.datasets[datasetKey].dataUrl.substring(0, 50)}
|
|
245
|
+
</option>
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
return <React.Fragment key={datasetKey}></React.Fragment>
|
|
249
|
+
})}
|
|
250
|
+
</select>
|
|
251
|
+
</label>
|
|
252
|
+
<label>
|
|
253
|
+
<span className='edit-label column-heading'>Query string parameter</span> <input type='text' defaultValue={filter.queryParameter} onChange={e => updateFilterProp('queryParameter', index, e.target.value)} />
|
|
254
|
+
</label>
|
|
255
|
+
<span className='edit-label column-heading'>Values</span>{' '}
|
|
256
|
+
<ul className='value-list'>
|
|
257
|
+
{filter.values &&
|
|
258
|
+
filter.values.map((value, valueIndex) => (
|
|
259
|
+
<li>
|
|
260
|
+
{value}
|
|
261
|
+
<input type='text' value={filter.labels ? filter.labels[value] : undefined} onChange={e => updateLabel(e, value)} />
|
|
262
|
+
<button onClick={() => removeValue(valueIndex)}>X</button>
|
|
344
263
|
</li>
|
|
345
264
|
))}
|
|
346
265
|
</ul>
|
|
347
|
-
<
|
|
348
|
-
<
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
266
|
+
<form onSubmit={addNewValue}>
|
|
267
|
+
<input type='text' /> <button type='submit'>Add New Value</button>
|
|
268
|
+
</form>
|
|
269
|
+
</>
|
|
270
|
+
)}
|
|
271
|
+
{filter.type !== 'url' && (
|
|
272
|
+
<>
|
|
273
|
+
<label>
|
|
274
|
+
<span className='edit-label column-heading'>Filter: </span>
|
|
275
|
+
<select
|
|
276
|
+
value={filter.columnName}
|
|
277
|
+
onChange={e => {
|
|
278
|
+
updateFilterProp('columnName', index, e.target.value)
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
<option value=''>- Select Option -</option>
|
|
282
|
+
{columns.map(dataKey => (
|
|
283
|
+
<option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
|
|
284
|
+
{dataKey}
|
|
285
|
+
</option>
|
|
286
|
+
))}
|
|
287
|
+
</select>
|
|
288
|
+
</label>
|
|
289
|
+
<label>
|
|
290
|
+
<span className='edit-label column-heading'>Label: </span>
|
|
291
|
+
<input
|
|
292
|
+
type='text'
|
|
293
|
+
value={filter.key}
|
|
294
|
+
onChange={e => {
|
|
295
|
+
updateFilterProp('key', index, e.target.value)
|
|
296
|
+
}}
|
|
297
|
+
/>
|
|
298
|
+
</label>
|
|
299
|
+
<label>
|
|
300
|
+
<span className='edit-label column-heading'>Show Dropdown</span>
|
|
301
|
+
<input
|
|
302
|
+
type='checkbox'
|
|
303
|
+
defaultChecked={filter.showDropdown === true}
|
|
304
|
+
onChange={e => {
|
|
305
|
+
updateFilterProp('showDropdown', index, !filter.showDropdown)
|
|
306
|
+
}}
|
|
307
|
+
/>
|
|
308
|
+
</label>
|
|
309
|
+
<label>
|
|
310
|
+
<span className='edit-label column-heading'>Set By: </span>
|
|
311
|
+
<select value={filter.setBy} onChange={e => updateFilterProp('setBy', index, e.target.value)}>
|
|
312
|
+
<option value=''>- Select Option -</option>
|
|
313
|
+
{Object.keys(config.visualizations).map(vizKey => (
|
|
314
|
+
<option value={vizKey} key={`set-by-select-item-${vizKey}`}>
|
|
353
315
|
{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
|
|
354
316
|
</option>
|
|
355
317
|
))}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
318
|
+
</select>
|
|
319
|
+
</label>
|
|
320
|
+
<label>
|
|
321
|
+
<span className='edit-label column-heading'>Used By:</span>
|
|
322
|
+
<ul>
|
|
323
|
+
{filter.usedBy &&
|
|
324
|
+
filter.usedBy.map(vizKey => (
|
|
325
|
+
<li key={`used-by-list-item-${vizKey}`}>
|
|
326
|
+
<span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
|
|
327
|
+
<button
|
|
328
|
+
onClick={e => {
|
|
329
|
+
e.preventDefault()
|
|
330
|
+
removeFilterUsedBy(filter, index, vizKey)
|
|
331
|
+
}}
|
|
332
|
+
>
|
|
333
|
+
X
|
|
334
|
+
</button>
|
|
335
|
+
</li>
|
|
336
|
+
))}
|
|
337
|
+
</ul>
|
|
338
|
+
<select onChange={e => addFilterUsedBy(filter, index, e.target.value)}>
|
|
339
|
+
<option value=''>- Select Option -</option>
|
|
340
|
+
{Object.keys(config.visualizations)
|
|
341
|
+
.filter(vizKey => filter.setBy !== vizKey && (!filter.usedBy || filter.usedBy.indexOf(vizKey) === -1) && !config.visualizations[vizKey].usesSharedFilter)
|
|
342
|
+
.map(vizKey => (
|
|
343
|
+
<option value={vizKey} key={`used-by-select-item-${vizKey}`}>
|
|
344
|
+
{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
|
|
345
|
+
</option>
|
|
346
|
+
))}
|
|
347
|
+
</select>
|
|
348
|
+
</label>
|
|
349
|
+
<label>
|
|
350
|
+
<span className='edit-label column-heading'>Reset Label: </span>
|
|
351
|
+
<input
|
|
352
|
+
type='text'
|
|
353
|
+
value={filter.resetLabel ? filter.resetLabel : ''}
|
|
354
|
+
onChange={e => {
|
|
355
|
+
updateFilterProp('resetLabel', index, e.target.value)
|
|
356
|
+
}}
|
|
357
|
+
/>
|
|
358
|
+
</label>
|
|
359
|
+
</>
|
|
360
|
+
)}
|
|
359
361
|
</fieldset>
|
|
360
362
|
<button type='button' className='btn btn-primary' style={{ display: 'inline-block', 'margin-right': '1em' }} onClick={overlay?.actions.toggleOverlay}>
|
|
361
363
|
Cancel
|
|
@@ -382,7 +384,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
382
384
|
</div>
|
|
383
385
|
)}
|
|
384
386
|
{!subEditor && (
|
|
385
|
-
<div className=
|
|
387
|
+
<div className='toggle-bar__wrapper'>
|
|
386
388
|
<ul className='toggle-bar'>
|
|
387
389
|
<li
|
|
388
390
|
className={tabSelected === 0 ? 'active' : 'inactive'}
|
|
@@ -441,17 +443,18 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
441
443
|
)}
|
|
442
444
|
{tabSelected === 2 && (
|
|
443
445
|
<>
|
|
444
|
-
|
|
445
|
-
<div className="wrap">
|
|
446
|
+
<div className='wrap'>
|
|
446
447
|
<label>
|
|
447
448
|
<input type='checkbox' defaultChecked={config.table.show} onChange={e => changeConfigValue('table', 'show', e.target.checked)} />
|
|
448
449
|
Show Data Table(s)
|
|
449
|
-
</label
|
|
450
|
+
</label>
|
|
451
|
+
<br />
|
|
450
452
|
|
|
451
453
|
<label>
|
|
452
454
|
<input type='checkbox' defaultChecked={config.table.expanded} onChange={e => changeConfigValue('table', 'expanded', e.target.checked)} />
|
|
453
455
|
Expanded by Default
|
|
454
|
-
</label
|
|
456
|
+
</label>
|
|
457
|
+
<br />
|
|
455
458
|
</div>
|
|
456
459
|
|
|
457
460
|
{/* <div className="wrap">
|
|
@@ -465,7 +468,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
465
468
|
</label>
|
|
466
469
|
</div> */}
|
|
467
470
|
|
|
468
|
-
<div className=
|
|
471
|
+
<div className='wrap'>
|
|
469
472
|
<label>
|
|
470
473
|
<input type='checkbox' defaultChecked={config.table.limitHeight} onChange={e => changeConfigValue('table', 'limitHeight', e.target.checked)} />
|
|
471
474
|
Limit Table Height
|
|
@@ -473,7 +476,7 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
473
476
|
{config.table.limitHeight && <input class='table-height-input' type='text' placeholder='Height (px)' defaultValue={config.table.height} onChange={e => changeConfigValue('table', 'height', e.target.value)} />}
|
|
474
477
|
</div>
|
|
475
478
|
|
|
476
|
-
<div className=
|
|
479
|
+
<div className='wrap'>
|
|
477
480
|
<label>
|
|
478
481
|
<input type='checkbox' defaultChecked={config.table.download} onChange={e => changeConfigValue('table', 'download', e.target.checked)} />
|
|
479
482
|
Show Download CSV Link
|
|
@@ -486,10 +489,9 @@ const Header = ({ setPreview, tabSelected, setTabSelected, back, subEditor = nul
|
|
|
486
489
|
</>
|
|
487
490
|
)}
|
|
488
491
|
</div>
|
|
489
|
-
</div
|
|
490
|
-
)
|
|
491
|
-
|
|
492
|
-
</div >
|
|
492
|
+
</div>
|
|
493
|
+
)}
|
|
494
|
+
</div>
|
|
493
495
|
)
|
|
494
496
|
}
|
|
495
497
|
|