@cdc/dashboard 4.23.11 → 4.24.2

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 (45) hide show
  1. package/dist/cdcdashboard.js +109007 -98738
  2. package/examples/DEV-6574.json +2224 -0
  3. package/examples/filters/Alabama.json +72 -0
  4. package/examples/filters/Alaska.json +1737 -0
  5. package/examples/filters/Arkansas.json +4713 -0
  6. package/examples/filters/California.json +212 -0
  7. package/examples/filters/Colorado.json +1500 -0
  8. package/examples/filters/Connecticut.json +559 -0
  9. package/examples/filters/Delaware.json +63 -0
  10. package/examples/filters/DistrictofColumbia.json +63 -0
  11. package/examples/filters/Florida.json +4217 -0
  12. package/examples/filters/States.json +146 -0
  13. package/examples/test.json +752 -0
  14. package/examples/zika.json +2274 -0
  15. package/index.html +5 -3
  16. package/package.json +9 -9
  17. package/src/CdcDashboard.tsx +124 -963
  18. package/src/CdcDashboardComponent.tsx +903 -0
  19. package/src/_stories/Dashboard.stories.tsx +2 -2
  20. package/src/components/Column.tsx +15 -12
  21. package/src/components/Header/Header.tsx +694 -0
  22. package/src/components/Header/index.tsx +1 -676
  23. package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +106 -0
  24. package/src/components/MultiConfigTabs/MultiTabs.tsx +30 -0
  25. package/src/components/MultiConfigTabs/index.tsx +8 -0
  26. package/src/components/MultiConfigTabs/multiconfigtabs.styles.css +32 -0
  27. package/src/components/Widget.tsx +25 -9
  28. package/src/helpers/filterData.ts +73 -73
  29. package/src/helpers/generateValuesForFilter.ts +25 -29
  30. package/src/helpers/getUpdateConfig.ts +6 -2
  31. package/src/helpers/processData.ts +13 -0
  32. package/src/helpers/processDataLegacy.ts +14 -0
  33. package/src/{index.jsx → index.tsx} +2 -2
  34. package/src/scss/editor-panel.scss +14 -11
  35. package/src/scss/grid.scss +4 -6
  36. package/src/scss/main.scss +2 -8
  37. package/src/store/dashboard.actions.ts +10 -4
  38. package/src/store/dashboard.reducer.ts +74 -3
  39. package/src/types/ConfigRow.ts +6 -0
  40. package/src/types/Dashboard.ts +11 -0
  41. package/src/types/DashboardConfig.ts +23 -0
  42. package/src/types/InitialState.ts +10 -0
  43. package/src/types/MultiDashboard.ts +11 -0
  44. package/src/types/SharedFilter.ts +31 -20
  45. package/src/types/Config.ts +0 -27
@@ -1,678 +1,3 @@
1
- import React, { useState, useEffect, useContext } from 'react'
2
-
3
- import { DashboardContext, DashboardDispatchContext } from '../../DashboardContext'
4
-
5
- // types
6
- import { type APIFilter } from '../../types/APIFilter'
7
- import { type SharedFilter } from '../../types/SharedFilter'
8
- import { type Config } from '../../types/Config'
9
-
10
- import { DataTransform } from '@cdc/core/helpers/DataTransform'
11
- import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
12
- import { useGlobalContext } from '@cdc/core/components/GlobalContext'
13
- import Modal from '@cdc/core/components/ui/Modal'
14
- import Tooltip from '@cdc/core/components/ui/Tooltip'
15
- import Icon from '@cdc/core/components/ui/Icon'
16
- import Select from '@cdc/core/components/ui/Select'
17
- import Button from '@cdc/core/components/elements/Button'
18
-
19
- import './index.scss'
20
-
21
- type HeaderProps = {
22
- setPreview?: any
23
- back?: any
24
- subEditor?: any
25
- visualizationKey?: string
26
- }
27
-
28
- export const FilterBehavior = {
29
- Apply: 'Apply Button',
30
- OnChange: 'Filter Change'
31
- }
32
-
33
- const Header = (props: HeaderProps) => {
34
- const { setPreview, visualizationKey, subEditor } = props
35
- const { config, setParentConfig, tabSelected } = useContext(DashboardContext)
36
- if (!config) return null
37
- const dispatch = useContext(DashboardDispatchContext)
38
- const setTabSelected = (payload: number) => dispatch({ type: 'SET_TAB_SELECTED', payload })
39
- const back = () => {
40
- if (!visualizationKey) return
41
- const newConfig: Config = { ...config } as Config
42
- newConfig.visualizations[visualizationKey].editing = false
43
- dispatch({ type: 'SET_CONFIG', payload: newConfig })
44
- }
45
-
46
- const { overlay } = useGlobalContext()
47
-
48
- const [columns, setColumns] = useState<string[]>([])
49
-
50
- const transform = new DataTransform()
51
-
52
- const changeConfigValue = (parentObj, key, value) => {
53
- let newConfig = { ...config }
54
- if (!newConfig[parentObj]) newConfig[parentObj] = {}
55
- newConfig[parentObj][key] = value
56
- dispatch({ type: 'UPDATE_CONFIG', payload: [newConfig] })
57
- }
58
-
59
- const setTab = index => {
60
- setTabSelected(index)
61
- if (index === 3) {
62
- setPreview(true)
63
- } else {
64
- setPreview(false)
65
- }
66
- }
67
-
68
- const addNewFilter = () => {
69
- let dashboardConfig = { ...config.dashboard }
70
-
71
- dashboardConfig.sharedFilters = dashboardConfig.sharedFilters || []
72
- const newFilter: SharedFilter = { key: 'Dashboard Filter ' + (dashboardConfig.sharedFilters.length + 1) }
73
- dashboardConfig.sharedFilters.push(newFilter)
74
-
75
- dispatch({ type: 'UPDATE_CONFIG', payload: [{ ...config, dashboard: dashboardConfig }] })
76
- }
77
-
78
- const removeFilter = index => {
79
- let dashboardConfig = { ...config.dashboard }
80
- let visualizations = { ...config.visualizations }
81
-
82
- dashboardConfig.sharedFilters?.splice(index, 1)
83
-
84
- Object.keys(visualizations).forEach(vizKey => {
85
- if (visualizations[vizKey].visualizationType === 'filter-dropdowns' && visualizations[vizKey].hide && visualizations[vizKey].hide.length > 0) {
86
- if (visualizations[vizKey].hide.indexOf(index) !== -1) {
87
- visualizations[vizKey].hide.splice(visualizations[vizKey].hide.indexOf(index), 1)
88
- }
89
- visualizations[vizKey].hide.forEach((hideIndex, i) => {
90
- if (hideIndex > index) {
91
- visualizations[vizKey].hide[i] = hideIndex - 1
92
- }
93
- })
94
- }
95
- })
96
-
97
- // Ensures URL filters refresh after filter removal
98
- if (dashboardConfig.datasets) {
99
- Object.keys(dashboardConfig.datasets).forEach(datasetKey => {
100
- dashboardConfig.datasets![datasetKey].runtimeDataUrl = ''
101
- })
102
- }
103
-
104
- const newConfig = { ...config, visualizations, dashboard: dashboardConfig }
105
- dispatch({ type: 'UPDATE_CONFIG', payload: [newConfig] })
106
-
107
- overlay?.actions.toggleOverlay()
108
- }
109
-
110
- const convertStateToConfig = (type = 'JSON') => {
111
- let strippedState = JSON.parse(JSON.stringify(config))
112
- delete strippedState.newViz
113
- delete strippedState.runtime
114
-
115
- if (type === 'JSON') {
116
- return JSON.stringify(strippedState)
117
- }
118
-
119
- return strippedState
120
- }
121
-
122
- useEffect(() => {
123
- const parsedData = convertStateToConfig()
124
-
125
- // Emit the data in a regular JS event so it can be consumed by anything.
126
- const event = new CustomEvent('updateVizConfig', { detail: parsedData })
127
-
128
- window.dispatchEvent(event)
129
-
130
- // Pass up to Editor if needed
131
- if (setParentConfig) {
132
- const newConfig = convertStateToConfig('object')
133
- setParentConfig(newConfig)
134
- }
135
-
136
- // eslint-disable-next-line react-hooks/exhaustive-deps
137
- }, [config])
138
-
139
- useEffect(() => {
140
- const runSetColumns = async () => {
141
- if (!config) return
142
- if (config.filterBehavior === FilterBehavior.Apply) return
143
- let columns = {}
144
- let dataKeys = Object.keys(config.datasets)
145
-
146
- for (let i = 0; i < dataKeys.length; i++) {
147
- let _dataSet = config.datasets[dataKeys[i]]
148
- if (!_dataSet.data && _dataSet.dataUrl) {
149
- config.datasets[dataKeys[i]].data = await fetchRemoteData(config.datasets[dataKeys[i]].dataUrl)
150
- _dataSet = config.datasets[dataKeys[i]]
151
- if (_dataSet.dataDescription) {
152
- try {
153
- config.datasets[dataKeys[i]].data = transform.autoStandardize(_dataSet.data)
154
- _dataSet = config.datasets[dataKeys[i]]
155
- config.datasets[dataKeys[i]].data = transform.developerStandardize(_dataSet.data, _dataSet.dataDescription)
156
- _dataSet = config.datasets[dataKeys[i]]
157
- } catch (e) {
158
- //Data not able to be standardized, leave as is
159
- }
160
- }
161
- }
162
-
163
- if (_dataSet.data) {
164
- config.datasets[dataKeys[i]].data.forEach(row => {
165
- Object.keys(row).forEach(columnName => (columns[columnName] = true))
166
- })
167
- }
168
- }
169
-
170
- setColumns(Object.keys(columns))
171
- }
172
-
173
- runSetColumns()
174
- }, [config.datasets])
175
-
176
- const filterModal = (filter: SharedFilter, index) => {
177
- const saveChanges = () => {
178
- let tempConfig = { ...config.dashboard }
179
- tempConfig.sharedFilters[index] = filter
180
-
181
- dispatch({ type: 'UPDATE_CONFIG', payload: [{ ...config, dashboard: tempConfig }] })
182
- overlay?.actions.toggleOverlay()
183
- }
184
-
185
- const updateFilterProp = (name, index, value) => {
186
- // @TODO this should be refactored into a reducer function.
187
- // it's unsafe to directly set objects w/o guardrails
188
- let newFilter = { ...filter }
189
-
190
- newFilter[name] = value
191
-
192
- overlay?.actions.openOverlay(filterModal(newFilter, index))
193
- }
194
-
195
- const addFilterUsedBy = (filter, index, value) => {
196
- if (!filter.usedBy) filter.usedBy = []
197
- filter.usedBy.push(value)
198
- updateFilterProp('usedBy', index, filter.usedBy)
199
- }
200
-
201
- const removeFilterUsedBy = (filter, index, value) => {
202
- let usedByIndex = filter.usedBy.indexOf(value)
203
- if (usedByIndex !== -1) {
204
- filter.usedBy.splice(usedByIndex, 1)
205
- updateFilterProp('usedBy', index, filter.usedBy)
206
- }
207
- }
208
-
209
- const updateAPIFilter = (key: keyof APIFilter, value: string | boolean) => {
210
- const _filter = filter.apiFilter || { apiEndpoint: '', valueSelector: '', textSelector: '' }
211
- const newAPIFilter: APIFilter = { ..._filter, [key]: value }
212
- overlay?.actions.openOverlay(filterModal({ ...filter, apiFilter: newAPIFilter }, index))
213
- }
214
-
215
- return (
216
- <Modal>
217
- <Modal.Content>
218
- <h2 className='shared-filter-modal__title'>Dashboard Filter Settings</h2>
219
- <fieldset className='shared-filter-modal shared-filter-modal__fieldset' key={filter.columnName + index}>
220
- <label>
221
- <span className='edit-label column-heading'>Filter Type: </span>
222
- <select defaultValue={filter.type || ''} onChange={e => updateFilterProp('type', index, e.target.value)}>
223
- <option value=''>- Select Option -</option>
224
- <option value='urlfilter'>URL</option>
225
- <option value='datafilter'>Data</option>
226
- </select>
227
- </label>
228
- {filter.type === 'urlfilter' && (
229
- <>
230
- <label>
231
- <span className='edit-label column-heading'>Label: </span>
232
- <input
233
- type='text'
234
- value={filter.key}
235
- onChange={e => {
236
- updateFilterProp('key', index, e.target.value)
237
- }}
238
- />
239
- </label>
240
- {config.filterBehavior !== FilterBehavior.Apply && (
241
- <>
242
- <label>
243
- <span className='edit-label column-heading'>URL to Filter: </span>
244
- <select defaultValue={filter.datasetKey || ''} onChange={e => updateFilterProp('datasetKey', index, e.target.value)}>
245
- <option value=''>- Select Option -</option>
246
- {Object.keys(config.datasets).map(datasetKey => {
247
- if (config.datasets[datasetKey].dataUrl) {
248
- return (
249
- <option key={datasetKey} value={datasetKey}>
250
- {config.datasets[datasetKey].dataUrl}
251
- </option>
252
- )
253
- }
254
- return null
255
- })}
256
- </select>
257
- </label>
258
- <label>
259
- <span className='edit-label column-heading'>Filter By: </span>
260
- <select defaultValue={filter.filterBy || ''} onChange={e => updateFilterProp('filterBy', index, e.target.value)}>
261
- <option value=''>- Select Option -</option>
262
- <option key={'query-string'} value={'Query String'}>
263
- Query String
264
- </option>
265
- <option key={'file-name'} value={'File Name'}>
266
- File Name
267
- </option>
268
- </select>
269
- </label>
270
- {filter.filterBy === 'File Name' && (
271
- <>
272
- <label>
273
- <span className='edit-label column-heading'>
274
- File Name:
275
- <Tooltip style={{ textTransform: 'none' }}>
276
- <Tooltip.Target>
277
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
278
- </Tooltip.Target>
279
- <Tooltip.Content>
280
- <p>{`Add \${query}\ to replace the filename with the active dropdown value.`}</p>
281
- </Tooltip.Content>
282
- </Tooltip>
283
- </span>
284
-
285
- <input type='text' defaultValue={filter.fileName || ''} onChange={e => updateFilterProp('fileName', index, e.target.value)} />
286
- </label>
287
-
288
- <label>
289
- <span className='edit-label column-heading'>
290
- White Space Replacments
291
- <Tooltip style={{ textTransform: 'none' }}>
292
- <Tooltip.Target>
293
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
294
- </Tooltip.Target>
295
- <Tooltip.Content>
296
- <p>{`Set how whitespace characters will be handled in the file request`}</p>
297
- </Tooltip.Content>
298
- </Tooltip>
299
- </span>
300
- <select defaultValue={filter.whitespaceReplacement || 'Keep Spaces'} onChange={e => updateFilterProp('whitespaceReplacement', index, e.target.value)}>
301
- <option key={'remove-spaces'} value={'Remove Spaces'}>
302
- Remove Spaces
303
- </option>
304
- <option key={'replace-with-underscore'} value={'Replace With Underscore'}>
305
- Replace With Underscore
306
- </option>
307
- <option key={'keep-spaces'} value={'Keep Spaces'}>
308
- Keep Spaces
309
- </option>
310
- </select>
311
- </label>
312
- </>
313
- )}
314
- </>
315
- )}
316
- {filter.filterBy === 'Query String' && (
317
- <label>
318
- <span className='edit-label column-heading'>Query string parameter</span> <input type='text' defaultValue={filter.queryParameter} onChange={e => updateFilterProp('queryParameter', index, e.target.value)} />
319
- </label>
320
- )}
321
- <label>
322
- <span className='edit-label column-heading'>Filter API Endpoint: </span>
323
- <input
324
- type='text'
325
- value={filter.apiFilter?.apiEndpoint}
326
- onChange={e => {
327
- updateAPIFilter('apiEndpoint', e.target.value)
328
- }}
329
- />
330
- </label>
331
- <label>
332
- <span className='edit-label column-heading'>
333
- Option Text Selector:
334
- <Tooltip style={{ textTransform: 'none' }}>
335
- <Tooltip.Target>
336
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
337
- </Tooltip.Target>
338
- <Tooltip.Content>
339
- <p>Text to use in the html option element</p>
340
- </Tooltip.Content>
341
- </Tooltip>
342
- </span>
343
- <input
344
- type='text'
345
- value={filter.apiFilter?.textSelector}
346
- onChange={e => {
347
- updateAPIFilter('textSelector', e.target.value)
348
- }}
349
- />
350
- </label>
351
- <label>
352
- <span className='edit-label column-heading'>
353
- Option Value Selector:
354
- <Tooltip style={{ textTransform: 'none' }}>
355
- <Tooltip.Target>
356
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
357
- </Tooltip.Target>
358
- <Tooltip.Content>
359
- <p>Value to use in the html option element</p>
360
- </Tooltip.Content>
361
- </Tooltip>
362
- </span>
363
- <input
364
- type='text'
365
- value={filter.apiFilter?.valueSelector}
366
- onChange={e => {
367
- updateAPIFilter('valueSelector', e.target.value)
368
- }}
369
- />
370
- </label>
371
- <label>
372
- <span className='edit-label column-heading'>Parent Filter: </span>
373
- <select
374
- value={filter.parents || []}
375
- onChange={e => {
376
- updateFilterProp('parent', index, e.target.value)
377
- }}
378
- >
379
- <option value=''>Select a filter</option>
380
- {config.dashboard.sharedFilters &&
381
- config.dashboard.sharedFilters.map(sharedFilter => {
382
- if (sharedFilter.key !== filter.key && sharedFilter.type !== 'datafilter') {
383
- return <option>{sharedFilter.key}</option>
384
- }
385
- })}
386
- </select>
387
- </label>
388
- <label>
389
- <span className='edit-label column-heading'>Auto Load: </span>
390
- <input
391
- type='checkbox'
392
- checked={filter.apiFilter?.autoLoad}
393
- onChange={e => {
394
- updateAPIFilter('autoLoad', !filter.apiFilter?.autoLoad)
395
- }}
396
- />
397
- </label>
398
- <label>
399
- <span className='edit-label column-heading'>Default Value: </span>
400
- <input
401
- type='text'
402
- value={filter.apiFilter?.defaultValue}
403
- onChange={e => {
404
- updateAPIFilter('defaultValue', e.target.value)
405
- }}
406
- />
407
- </label>
408
- </>
409
- )}
410
- {filter.type === 'datafilter' && (
411
- <>
412
- <label>
413
- <span className='edit-label column-heading'>Filter: </span>
414
- <select
415
- value={filter.columnName}
416
- onChange={e => {
417
- updateFilterProp('columnName', index, e.target.value)
418
- }}
419
- >
420
- <option value=''>- Select Option -</option>
421
- {columns.map(dataKey => (
422
- <option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
423
- {dataKey}
424
- </option>
425
- ))}
426
- </select>
427
- </label>
428
- <label>
429
- <span className='edit-label column-heading'>Label: </span>
430
- <input
431
- type='text'
432
- value={filter.key}
433
- onChange={e => {
434
- updateFilterProp('key', index, e.target.value)
435
- }}
436
- />
437
- </label>
438
- <label>
439
- <span className='edit-label column-heading'>Show Dropdown</span>
440
- <input
441
- type='checkbox'
442
- defaultChecked={filter.showDropdown === true}
443
- onChange={e => {
444
- updateFilterProp('showDropdown', index, !filter.showDropdown)
445
- }}
446
- />
447
- </label>
448
- <label>
449
- <span className='edit-label column-heading'>Set By: </span>
450
- <select value={filter.setBy} onChange={e => updateFilterProp('setBy', index, e.target.value)}>
451
- <option value=''>- Select Option -</option>
452
- {Object.keys(config.visualizations).map(vizKey => (
453
- <option value={vizKey} key={`set-by-select-item-${vizKey}`}>
454
- {config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
455
- </option>
456
- ))}
457
- </select>
458
- </label>
459
- <label>
460
- <span className='edit-label column-heading'>Used By: </span>
461
- <ul>
462
- {filter.usedBy &&
463
- filter.usedBy.map(vizKey => (
464
- <li key={`used-by-list-item-${vizKey}`}>
465
- <span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
466
- <button
467
- onClick={e => {
468
- e.preventDefault()
469
- removeFilterUsedBy(filter, index, vizKey)
470
- }}
471
- >
472
- X
473
- </button>
474
- </li>
475
- ))}
476
- </ul>
477
- <select onChange={e => addFilterUsedBy(filter, index, e.target.value)}>
478
- <option value=''>- Select Option -</option>
479
- {Object.keys(config.visualizations)
480
- .filter(vizKey => filter.setBy !== vizKey && (!filter.usedBy || filter.usedBy.indexOf(vizKey) === -1) && !config.visualizations[vizKey].usesSharedFilter)
481
- .map(vizKey => (
482
- <option value={vizKey} key={`used-by-select-item-${vizKey}`}>
483
- {config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
484
- </option>
485
- ))}
486
- </select>
487
- </label>
488
- <label>
489
- <span className='edit-label column-heading'>Reset Label: </span>
490
- <input
491
- type='text'
492
- value={filter.resetLabel || ''}
493
- onChange={e => {
494
- updateFilterProp('resetLabel', index, e.target.value)
495
- }}
496
- />
497
- </label>
498
- <label>
499
- <span className='edit-label column-heading'>Parent Filter: </span>
500
- <select
501
- value={filter.parents || []}
502
- onChange={e => {
503
- updateFilterProp('parent', index, e.target.value)
504
- }}
505
- >
506
- <option value=''>Select a filter</option>
507
- {config.dashboard.sharedFilters &&
508
- config.dashboard.sharedFilters.map(sharedFilter => {
509
- if (sharedFilter.key !== filter.key && sharedFilter.type !== 'urlfilter') {
510
- return <option>{sharedFilter.key}</option>
511
- }
512
- })}
513
- </select>
514
- </label>
515
- </>
516
- )}
517
- </fieldset>
518
-
519
- <Button
520
- className='btn--remove warn'
521
- onClick={() => {
522
- removeFilter(index)
523
- }}
524
- >
525
- Remove Filter
526
- </Button>
527
-
528
- <div className='shared-filter-modal__right-buttons'>
529
- <Button className='btn--cancel muted' style={{ display: 'inline-block', marginRight: '1em' }} onClick={overlay?.actions.toggleOverlay}>
530
- Cancel
531
- </Button>
532
-
533
- <Button type='button' className='btn--submit success' style={{ display: 'inline-block' }} onClick={saveChanges}>
534
- Save
535
- </Button>
536
- </div>
537
- </Modal.Content>
538
- </Modal>
539
- )
540
- }
541
-
542
- return (
543
- <div aria-level={2} role='heading' className={`editor-heading${subEditor ? ' sub-dashboard-viz' : ''}`}>
544
- {subEditor ? (
545
- <div className='heading-1 back-to' onClick={back} style={{ cursor: 'pointer' }}>
546
- <span>&#8592;</span> Back to Dashboard
547
- </div>
548
- ) : (
549
- <div className='heading-1'>
550
- Dashboard Editor
551
- <br />
552
- {<input type='text' placeholder='Enter Dashboard Name Here' defaultValue={config.dashboard?.title} onChange={e => changeConfigValue('dashboard', 'title', e.target.value)} />}
553
- </div>
554
- )}
555
- {!subEditor && (
556
- <div className='toggle-bar__wrapper'>
557
- <ul className='toggle-bar'>
558
- <li
559
- className={tabSelected === 0 ? 'active' : 'inactive'}
560
- onClick={() => {
561
- setTab(0)
562
- }}
563
- >
564
- Dashboard Description
565
- </li>
566
- <li
567
- className={tabSelected === 1 ? 'active' : 'inactive'}
568
- onClick={() => {
569
- setTab(1)
570
- }}
571
- >
572
- Dashboard Filters
573
- </li>
574
- <li
575
- className={tabSelected === 2 ? 'active' : 'inactive'}
576
- onClick={() => {
577
- setTab(2)
578
- }}
579
- >
580
- Data Table Settings
581
- </li>
582
- <li
583
- className={tabSelected === 3 ? 'active' : 'inactive'}
584
- onClick={() => {
585
- setTab(3)
586
- }}
587
- >
588
- Dashboard Preview
589
- </li>
590
- </ul>
591
- <div className='heading-body'>
592
- {tabSelected === 0 && <input type='text' className='description-input' placeholder='Type a dashboard description here.' defaultValue={config.dashboard?.description} onChange={e => changeConfigValue('dashboard', 'description', e.target.value)} />}
593
- {tabSelected === 1 && (
594
- <>
595
- {config.dashboard.sharedFilters &&
596
- config.dashboard.sharedFilters.map((sharedFilter, index) => (
597
- <span className='shared-filter-button' key={`shared-filter-${sharedFilter.key}`}>
598
- <a
599
- href='#'
600
- onClick={e => {
601
- e.preventDefault()
602
- overlay?.actions.openOverlay(filterModal(sharedFilter, index))
603
- }}
604
- >
605
- {sharedFilter.key}
606
- </a>
607
- <button onClick={() => removeFilter(index)}>X</button>
608
- </span>
609
- ))}
610
- <button onClick={addNewFilter}>Add New Filter</button>
611
-
612
- <Select
613
- value={config.filterBehavior}
614
- fieldName='filterBehavior'
615
- label='Filter Behavior'
616
- initial='- Select Option -'
617
- onchange={e => {
618
- const newConfig = { ...config, filterBehavior: e.target.value }
619
- dispatch({ type: 'UPDATE_CONFIG', payload: [newConfig] })
620
- }}
621
- options={Object.values(FilterBehavior)}
622
- tooltip={
623
- <Tooltip style={{ textTransform: 'none' }}>
624
- <Tooltip.Target>
625
- <Icon display='question' color='' style={{ marginLeft: '0.5rem' }} />
626
- </Tooltip.Target>
627
- <Tooltip.Content>
628
- <p>The Apply Button option changes the visualization when the user clicks "apply". The Filter Change option immediately changes the visualization when the selection is changed.</p>
629
- </Tooltip.Content>
630
- </Tooltip>
631
- }
632
- />
633
- </>
634
- )}
635
- {tabSelected === 2 && (
636
- <>
637
- <div className='wrap'>
638
- <label>
639
- <input type='checkbox' defaultChecked={config.table.show} onChange={e => changeConfigValue('table', 'show', e.target.checked)} />
640
- Show Data Table(s)
641
- </label>
642
- <br />
643
-
644
- <label>
645
- <input type='checkbox' defaultChecked={config.table.expanded} onChange={e => changeConfigValue('table', 'expanded', e.target.checked)} />
646
- Expanded by Default
647
- </label>
648
- <br />
649
- </div>
650
-
651
- <div className='wrap'>
652
- <label>
653
- <input type='checkbox' defaultChecked={config.table.limitHeight} onChange={e => changeConfigValue('table', 'limitHeight', e.target.checked)} />
654
- Limit Table Height
655
- </label>
656
- {config.table.limitHeight && <input className='table-height-input' type='text' placeholder='Height (px)' defaultValue={config.table.height} onChange={e => changeConfigValue('table', 'height', e.target.value)} />}
657
- </div>
658
-
659
- <div className='wrap'>
660
- <label>
661
- <input type='checkbox' defaultChecked={config.table.download} onChange={e => changeConfigValue('table', 'download', e.target.checked)} />
662
- Show Download CSV Link
663
- </label>
664
- <label>
665
- <input type='checkbox' defaultChecked={config.table.showDownloadUrl} onChange={e => changeConfigValue('table', 'showDownloadUrl', e.target.checked)} />
666
- Show URL to Automatically Updated Data
667
- </label>
668
- </div>
669
- </>
670
- )}
671
- </div>
672
- </div>
673
- )}
674
- </div>
675
- )
676
- }
1
+ import Header from './Header'
677
2
 
678
3
  export default Header