@cdc/dashboard 4.24.3 → 4.24.5

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 (37) hide show
  1. package/dist/cdcdashboard.js +148123 -105018
  2. package/examples/chart-data.json +5409 -0
  3. package/examples/full-dash-test.json +14643 -0
  4. package/examples/full-dashboard.json +10036 -0
  5. package/index.html +2 -2
  6. package/package.json +9 -9
  7. package/src/CdcDashboard.tsx +8 -3
  8. package/src/CdcDashboardComponent.tsx +141 -290
  9. package/src/_stories/Dashboard.stories.tsx +52 -7
  10. package/src/_stories/_mock/markup-include.json +78 -0
  11. package/src/_stories/_mock/multi-dashboards.json +914 -0
  12. package/src/_stories/_mock/multi-viz.json +378 -0
  13. package/src/_stories/_mock/pivot-filter.json +2 -2
  14. package/src/_stories/_mock/standalone-table.json +2 -0
  15. package/src/components/DataDesignerModal.tsx +145 -0
  16. package/src/components/Grid.tsx +3 -1
  17. package/src/components/Header/FilterModal.tsx +63 -33
  18. package/src/components/MultiConfigTabs/MultiTabs.tsx +3 -2
  19. package/src/components/Row.tsx +50 -25
  20. package/src/components/Toggle/Toggle.tsx +6 -7
  21. package/src/components/VisualizationRow.tsx +183 -0
  22. package/src/components/VisualizationsPanel.tsx +26 -3
  23. package/src/components/Widget.tsx +21 -103
  24. package/src/helpers/filterData.ts +1 -1
  25. package/src/helpers/getFilteredData.ts +39 -0
  26. package/src/helpers/getUpdateConfig.ts +15 -0
  27. package/src/helpers/getVizConfig.ts +31 -0
  28. package/src/helpers/getVizRowColumnLocator.ts +9 -0
  29. package/src/scss/grid.scss +9 -2
  30. package/src/scss/main.scss +5 -0
  31. package/src/store/dashboard.actions.ts +6 -0
  32. package/src/store/dashboard.reducer.ts +33 -8
  33. package/src/types/ConfigRow.ts +12 -3
  34. package/src/types/DataSet.ts +11 -8
  35. package/src/types/SharedFilter.ts +1 -1
  36. package/src/components/EditorWrapper/EditorWrapper.tsx +0 -52
  37. package/src/components/EditorWrapper/editor-wrapper.style.css +0 -13
@@ -1,4 +1,4 @@
1
- import { useContext, useEffect, useState } from 'react'
1
+ import { useContext, useEffect, useMemo, useState } from 'react'
2
2
  import { MultiDashboardConfig } from '../../types/MultiDashboard'
3
3
  import { SharedFilter } from '../../types/SharedFilter'
4
4
  import { DashboardDispatchContext } from '../../DashboardContext'
@@ -11,6 +11,8 @@ import Icon from '@cdc/core/components/ui/Icon'
11
11
  import Button from '@cdc/core/components/elements/Button'
12
12
  import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
13
13
  import DataTransform from '@cdc/core/helpers/DataTransform'
14
+ import { getVizRowColumnLocator } from '../../helpers/getVizRowColumnLocator'
15
+ import _ from 'lodash'
14
16
 
15
17
  type ModalProps = {
16
18
  config: MultiDashboardConfig
@@ -26,9 +28,39 @@ const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeF
26
28
  const [columns, setColumns] = useState<string[]>([])
27
29
  const transform = new DataTransform()
28
30
 
31
+ const vizRowColumnLocator = getVizRowColumnLocator(config.rows)
32
+
33
+ const [usedByNameLookup, usedByOptions] = useMemo(() => {
34
+ const nameLookup = {}
35
+ const vizOptions = Object.keys(config.visualizations)
36
+ .filter(vizKey => {
37
+ const notAdded = !filter.usedBy || filter.usedBy.indexOf(vizKey) === -1
38
+ const usesSharedFilter = config.visualizations[vizKey].usesSharedFilter
39
+ const row = vizRowColumnLocator[vizKey].row
40
+ const dataConfiguredOnRow = config.rows[row].dataKey
41
+ return filter.setBy !== vizKey && notAdded && !usesSharedFilter && !dataConfiguredOnRow
42
+ })
43
+ .map(vizKey => {
44
+ const viz = config.visualizations[vizKey]
45
+ const vizName = viz.general?.title || viz.title || vizKey
46
+ nameLookup[vizKey] = vizName
47
+ return vizKey
48
+ })
49
+ const rowOptions: number[] = []
50
+
51
+ config.rows.forEach((row, rowIndex) => {
52
+ if (!!row.multiVizColumn) {
53
+ nameLookup[rowIndex] = `Row ${rowIndex + 1}`
54
+ rowOptions.push(rowIndex)
55
+ }
56
+ })
57
+
58
+ const rowsNotSelected = rowOptions.filter(row => !filter.usedBy || filter.usedBy.indexOf(row.toString()) === -1)
59
+ return [nameLookup, [...vizOptions, ...rowsNotSelected]]
60
+ }, [config.visualizations, filter.usedBy, filter.setBy, vizRowColumnLocator])
61
+
29
62
  useEffect(() => {
30
63
  const runSetColumns = async () => {
31
- if (config.filterBehavior === FilterBehavior.Apply) return
32
64
  let columns = {}
33
65
  let dataKeys = Object.keys(config.datasets)
34
66
 
@@ -71,18 +103,13 @@ const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeF
71
103
  }
72
104
 
73
105
  const updateFilterProp = (name, value) => {
74
- // @TODO this should be refactored into a reducer function.
75
- // it's unsafe to directly set objects w/o guardrails
76
- let newFilter = { ...filter }
77
-
78
- newFilter[name] = value
79
-
80
- console.log('newFilter', newFilter)
106
+ const newFilter = { ..._.cloneDeep(filter), [name]: value }
81
107
 
82
108
  setFilter(newFilter)
83
109
  }
84
110
 
85
111
  const addFilterUsedBy = (filter, value) => {
112
+ if (value === '') return
86
113
  if (!filter.usedBy) filter.usedBy = []
87
114
  filter.usedBy.push(value)
88
115
  updateFilterProp('usedBy', filter.usedBy)
@@ -97,9 +124,10 @@ const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeF
97
124
  }
98
125
 
99
126
  const updateAPIFilter = (key: keyof APIFilter, value: string | boolean) => {
100
- const _filter = filter.apiFilter || { apiEndpoint: '', valueSelector: '', textSelector: '' }
127
+ const filterClone = _.cloneDeep(filter)
128
+ const _filter = filterClone.apiFilter || { apiEndpoint: '', valueSelector: '', textSelector: '' }
101
129
  const newAPIFilter: APIFilter = { ..._filter, [key]: value }
102
- setFilter({ ...filter, apiFilter: newAPIFilter })
130
+ setFilter({ ...filterClone, apiFilter: newAPIFilter })
103
131
  }
104
132
 
105
133
  return (
@@ -341,6 +369,7 @@ const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeF
341
369
  value={filter.pivot}
342
370
  onChange={e => {
343
371
  updateFilterProp('pivot', e.target.value)
372
+ updateFilterProp('showDropdown', true)
344
373
  }}
345
374
  >
346
375
  <option value=''>- Select Option -</option>
@@ -363,16 +392,19 @@ const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeF
363
392
  }}
364
393
  />
365
394
  </label>
366
- <label>
367
- <span className='edit-label column-heading'>Show Dropdown</span>
368
- <input
369
- type='checkbox'
370
- defaultChecked={filter.showDropdown === true}
371
- onChange={e => {
372
- updateFilterProp('showDropdown', !filter.showDropdown)
373
- }}
374
- />
375
- </label>
395
+ {!filter.pivot && (
396
+ <label>
397
+ <span className='edit-label column-heading'>Show Dropdown</span>
398
+ <input
399
+ type='checkbox'
400
+ defaultChecked={filter.showDropdown === true}
401
+ onChange={e => {
402
+ updateFilterProp('showDropdown', !filter.showDropdown)
403
+ }}
404
+ />
405
+ </label>
406
+ )}
407
+
376
408
  <label>
377
409
  <span className='edit-label column-heading'>Set By: </span>
378
410
  <select value={filter.setBy} onChange={e => updateFilterProp('setBy', e.target.value)}>
@@ -388,13 +420,13 @@ const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeF
388
420
  <span className='edit-label column-heading'>Used By: </span>
389
421
  <ul>
390
422
  {filter.usedBy &&
391
- filter.usedBy.map(vizKey => (
392
- <li key={`used-by-list-item-${vizKey}`}>
393
- <span>{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}</span>{' '}
423
+ filter.usedBy.map(opt => (
424
+ <li key={`used-by-list-item-${opt}`}>
425
+ <span>{usedByNameLookup[opt] || opt}</span>{' '}
394
426
  <button
395
427
  onClick={e => {
396
428
  e.preventDefault()
397
- removeFilterUsedBy(filter, vizKey)
429
+ removeFilterUsedBy(filter, opt)
398
430
  }}
399
431
  >
400
432
  X
@@ -402,15 +434,13 @@ const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeF
402
434
  </li>
403
435
  ))}
404
436
  </ul>
405
- <select onChange={e => addFilterUsedBy(filter, e.target.value)}>
437
+ <select value='' onChange={e => addFilterUsedBy(filter, e.target.value)}>
406
438
  <option value=''>- Select Option -</option>
407
- {Object.keys(config.visualizations)
408
- .filter(vizKey => filter.setBy !== vizKey && (!filter.usedBy || filter.usedBy.indexOf(vizKey) === -1) && !config.visualizations[vizKey].usesSharedFilter)
409
- .map(vizKey => (
410
- <option value={vizKey} key={`used-by-select-item-${vizKey}`}>
411
- {config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
412
- </option>
413
- ))}
439
+ {usedByOptions.map(opt => (
440
+ <option value={opt} key={`used-by-select-item-${opt}`}>
441
+ {usedByNameLookup[opt] || opt}
442
+ </option>
443
+ ))}
414
444
  </select>
415
445
  </label>
416
446
  <label>
@@ -9,7 +9,8 @@ const MultiTabs = () => {
9
9
  const tabs = useMemo<string[]>(() => (config.multiDashboards || []).map(({ label }) => label), [config.multiDashboards])
10
10
  const activeTab = useMemo<number>(() => config.activeDashboard, [config.activeDashboard])
11
11
 
12
- const load = (indexToSwitchTo: number) => {
12
+ const load = (indexToSwitchTo: number, e) => {
13
+ e.preventDefault() // some form wrapper is causing this to act as a submit button
13
14
  dispatch({ type: 'SWITCH_CONFIG', payload: indexToSwitchTo })
14
15
  }
15
16
 
@@ -18,7 +19,7 @@ const MultiTabs = () => {
18
19
  <ul className='nav nav-tabs multi-config-tabs'>
19
20
  {tabs.map((tab, index) => (
20
21
  <li className='nav-item'>
21
- <a className={`nav-link${activeTab === index ? ' active' : ''}`} aria-current={activeTab === index ? 'page' : null} href='#' onClick={() => load(index)}>
22
+ <a className={`nav-link${activeTab === index ? ' active' : ''}`} aria-current={activeTab === index ? 'page' : null} href='#' onClick={e => load(index, e)}>
22
23
  {tab}
23
24
  </a>
24
25
  </li>
@@ -1,4 +1,4 @@
1
- import React, { useContext, useState } from 'react'
1
+ import React, { useContext, useMemo, useState } from 'react'
2
2
 
3
3
  import { DashboardContext, DashboardDispatchContext } from '../DashboardContext'
4
4
 
@@ -13,41 +13,53 @@ import FourEightColIcon from '../images/icon-col-4-8.svg'
13
13
  import EightFourColIcon from '../images/icon-col-8-4.svg'
14
14
  import ToggleIcon from '../images/icon-toggle.svg'
15
15
  import { ConfigRow } from '../types/ConfigRow'
16
+ import { DataDesignerModal } from './DataDesignerModal'
17
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
18
+ import { iconHash } from '../helpers/iconHash'
19
+ import _ from 'lodash'
16
20
 
17
21
  type RowMenuProps = {
18
22
  rowIdx: number
19
- row: ConfigRow
20
23
  }
21
24
 
22
- const RowMenu: React.FC<RowMenuProps> = ({ rowIdx, row }) => {
25
+ const RowMenu: React.FC<RowMenuProps> = ({ rowIdx }) => {
23
26
  const { config } = useContext(DashboardContext)
24
27
  const dispatch = useContext(DashboardDispatchContext)
25
- const { rows } = config
28
+ const rows = _.cloneDeep(config.rows)
29
+ const row = config.rows[rowIdx]
26
30
 
27
31
  const updateConfig = config => dispatch({ type: 'UPDATE_CONFIG', payload: [config] })
28
- const getCurr = () => {
29
- if (row[0].toggle) return 'toggle'
30
- return row.reduce((acc, curr) => {
32
+ const curr = useMemo(() => {
33
+ if (row.toggle) return 'toggle'
34
+ return row.columns.reduce((acc, curr) => {
31
35
  if (curr.width) {
32
36
  acc += curr.width
33
37
  }
34
38
  return acc
35
39
  }, '')
36
- }
37
-
38
- const [curr, setCurr] = useState(getCurr())
40
+ }, [row])
39
41
 
40
42
  const setRowLayout = (layout: number[], toggle = undefined) => {
41
- const newRows = [...rows]
42
- const row = newRows[rowIdx]
43
-
44
- row.forEach((col, i) => {
45
- col.width = layout[i] ?? null
46
- col.toggle = toggle
47
- if (!toggle) col.hide = undefined
48
- })
43
+ const newRows = _.cloneDeep(rows)
44
+ newRows[rowIdx].toggle = toggle
45
+ const rowColumns = newRows[rowIdx].columns
46
+ const columnsWithWidgets = rowColumns.filter(c => c.widget)
47
+
48
+ const totalWidgets = columnsWithWidgets.length
49
+ if (totalWidgets > layout.length) {
50
+ // don't let them change to a smaller layout and lose viz config work
51
+ return
52
+ } else {
53
+ // a 3 column becoming a 2 column with only a VizConfig in the second column will maintain order
54
+ // a 2 column becoming a 1 column with only a VizConfig in the second column will move the VizConfig to the first column
55
+ const mapRow = rowColumns.length > layout.length ? columnsWithWidgets : rowColumns
56
+ newRows[rowIdx].columns = layout.map((width, colIndex) => {
57
+ const col = mapRow[colIndex]
58
+ return col ? { ...col, width } : { width }
59
+ })
60
+ }
61
+
49
62
  updateConfig({ ...config, rows: newRows })
50
- setCurr(toggle ? 'toggle' : layout.join(''))
51
63
  }
52
64
 
53
65
  const moveRow = (dir = 'down') => {
@@ -138,15 +150,28 @@ const RowMenu: React.FC<RowMenuProps> = ({ rowIdx, row }) => {
138
150
  }
139
151
 
140
152
  const Row = ({ row, idx: rowIdx, uuid }) => {
153
+ const { overlay } = useGlobalContext()
141
154
  return (
142
155
  <div className='builder-row' data-row-id={rowIdx}>
143
- <RowMenu rowIdx={rowIdx} row={row} />
156
+ <RowMenu rowIdx={rowIdx} />
144
157
  <div className='column-container'>
145
- {row
146
- .filter(column => column.width)
147
- .map((column, colIdx) => (
148
- <Column data={column} key={`row-${uuid}-col-${colIdx}`} rowIdx={rowIdx} colIdx={colIdx} />
149
- ))}
158
+ <>
159
+ <button
160
+ title='Configure Data'
161
+ className='btn btn-configure-row'
162
+ onClick={() => {
163
+ overlay?.actions.openOverlay(<DataDesignerModal rowIndex={rowIdx} />)
164
+ }}
165
+ >
166
+ {iconHash['gear']}
167
+ </button>
168
+
169
+ {row.columns
170
+ .filter(column => column.width)
171
+ .map((column, colIdx) => (
172
+ <Column data={column} key={`row-${uuid}-col-${colIdx}`} rowIdx={rowIdx} colIdx={colIdx} />
173
+ ))}
174
+ </>
150
175
  </div>
151
176
  </div>
152
177
  )
@@ -7,23 +7,22 @@ import './toggle-style.css'
7
7
  import _ from 'lodash'
8
8
 
9
9
  type ToggleProps = {
10
+ active: number
10
11
  row: ConfigRow
11
- rowIndex: number
12
12
  visualizations: Record<string, Visualization>
13
+ setToggled: (colIndex: number) => void
13
14
  }
14
- const Toggle: React.FC<ToggleProps> = ({ row, rowIndex, visualizations }) => {
15
- const dispatch = useContext(DashboardDispatchContext)
16
-
15
+ const Toggle: React.FC<ToggleProps> = ({ active, row, visualizations, setToggled }) => {
17
16
  const selectItem = (colIndex, e = null) => {
18
17
  if (e?.key && e.key !== 'Enter') return
19
- dispatch({ type: 'TOGGLE_ROW', payload: { rowIndex, colIndex } })
18
+ setToggled(colIndex)
20
19
  }
21
20
  return (
22
21
  <div className='toggle-component'>
23
- {row.map((col, colIndex) => {
22
+ {row.columns.map((col, colIndex) => {
24
23
  if (!col.widget) return null
25
24
  const type = visualizations[col.widget].type
26
- const selected = col.hide !== undefined ? col.hide : colIndex === 0
25
+ const selected = colIndex === active
27
26
  return (
28
27
  <div role='radio' className={selected ? 'selected' : ''} key={colIndex} onClick={() => selectItem(colIndex)} onKeyUp={e => selectItem(colIndex, e)} aria-checked={selected} tabIndex={0} aria-label={`Toggle ${type}`}>
29
28
  {getIcon(visualizations[col.widget])} <span>{_.capitalize(type)}</span>
@@ -0,0 +1,183 @@
1
+ import DataTableStandAlone from '@cdc/core/components/DataTable/DataTableStandAlone'
2
+ import React, { MouseEventHandler, useContext, useMemo } from 'react'
3
+ import Toggle from './Toggle'
4
+ import _ from 'lodash'
5
+ import { ConfigRow } from '../types/ConfigRow'
6
+ import CdcMap from '@cdc/map'
7
+ import CdcChart from '@cdc/chart'
8
+ import CdcDataBite from '@cdc/data-bite'
9
+ import CdcWaffleChart from '@cdc/waffle-chart'
10
+ import CdcMarkupInclude from '@cdc/markup-include'
11
+ import CdcFilteredText from '@cdc/filtered-text'
12
+ import Filters, { APIFilterDropdowns } from './Filters'
13
+ import { FilterBehavior } from './Header/Header'
14
+ import { DashboardContext } from '../DashboardContext'
15
+ import { ViewPort } from '@cdc/core/types/ViewPort'
16
+ import { getVizConfig } from '../helpers/getVizConfig'
17
+ import { TableConfig } from '@cdc/core/components/DataTable/types/TableConfig'
18
+
19
+ type VizRowProps = {
20
+ filteredDataOverride?: Object[]
21
+ row: ConfigRow
22
+ rowIndex: number
23
+ setSharedFilter: Function
24
+ updateChildConfig: Function
25
+ applyFilters: MouseEventHandler<HTMLButtonElement>
26
+ apiFilterDropdowns: APIFilterDropdowns
27
+ handleOnChange: Function
28
+ currentViewport: ViewPort
29
+ }
30
+
31
+ const VisualizationRow: React.FC<VizRowProps> = ({ filteredDataOverride, row, rowIndex: index, setSharedFilter, updateChildConfig, applyFilters, apiFilterDropdowns, handleOnChange, currentViewport }) => {
32
+ const { config, filteredData: dashboardFilteredData, data: rawData } = useContext(DashboardContext)
33
+ const [show, setShow] = React.useState(row.columns.map((col, i) => i === 0))
34
+ const setToggled = (colIndex: number) => {
35
+ setShow(show.map((_, i) => i === colIndex))
36
+ }
37
+ const inNoDataState = useMemo(() => {
38
+ const vals = Object.values(rawData)
39
+ if (!vals.length) return true
40
+ return vals.some(val => val === undefined)
41
+ }, [rawData])
42
+ const GoButton = ({ autoLoad }: { autoLoad?: boolean }) => {
43
+ if (config.filterBehavior === FilterBehavior.Apply && !autoLoad) {
44
+ return <button onClick={applyFilters}>GO!</button>
45
+ }
46
+ return null
47
+ }
48
+ return (
49
+ <div className={`dashboard-row ${row.equalHeight ? 'equal-height' : ''} ${row.toggle ? 'toggle' : ''}`} key={`row__${index}`}>
50
+ {row.toggle && <Toggle row={row} visualizations={config.visualizations} active={show.indexOf(true)} setToggled={setToggled} />}
51
+ {row.columns.map((col, colIndex) => {
52
+ if (col.width) {
53
+ if (!col.widget) return <div key={`row__${index}__col__${colIndex}`} className={`dashboard-col dashboard-col-${col.width}`}></div>
54
+
55
+ const visualizationConfig = getVizConfig(col.widget, index, config, rawData, dashboardFilteredData)
56
+ if (filteredDataOverride) {
57
+ visualizationConfig.data = filteredDataOverride
58
+ if (visualizationConfig.formattedData) {
59
+ visualizationConfig.formattedData = filteredDataOverride
60
+ }
61
+ }
62
+
63
+ const setsSharedFilter = config.dashboard.sharedFilters && config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === col.widget).length > 0
64
+ const setSharedFilterValue = setsSharedFilter ? config.dashboard.sharedFilters.filter(sharedFilter => sharedFilter.setBy === col.widget)[0].active : undefined
65
+ const tableLink = (
66
+ <a href={`#data-table-${visualizationConfig.dataKey}`} className='margin-left-href'>
67
+ {visualizationConfig.dataKey} (Go to Table)
68
+ </a>
69
+ )
70
+ const hideFilter = visualizationConfig.autoLoad && inNoDataState
71
+
72
+ const shouldShow = row.toggle === undefined || (row.toggle && show[colIndex])
73
+ return (
74
+ <React.Fragment key={`vis__${index}__${colIndex}`}>
75
+ <div className={`dashboard-col dashboard-col-${col.width} ${!shouldShow ? 'hidden-toggle' : ''}`}>
76
+ {visualizationConfig.type === 'chart' && (
77
+ <CdcChart
78
+ key={col.widget}
79
+ config={visualizationConfig}
80
+ dashboardConfig={config}
81
+ isEditor={false}
82
+ setConfig={newConfig => {
83
+ updateChildConfig(col.widget, newConfig)
84
+ }}
85
+ setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
86
+ isDashboard={true}
87
+ link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
88
+ configUrl={undefined}
89
+ setEditing={undefined}
90
+ hostname={undefined}
91
+ setSharedFilterValue={undefined}
92
+ />
93
+ )}
94
+ {visualizationConfig.type === 'map' && (
95
+ <CdcMap
96
+ key={col.widget}
97
+ config={visualizationConfig}
98
+ isEditor={false}
99
+ setConfig={newConfig => {
100
+ updateChildConfig(col.widget, newConfig)
101
+ }}
102
+ showLoader={false}
103
+ setSharedFilter={setsSharedFilter ? setSharedFilter : undefined}
104
+ setSharedFilterValue={setSharedFilterValue}
105
+ isDashboard={true}
106
+ link={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
107
+ />
108
+ )}
109
+ {visualizationConfig.type === 'data-bite' && (
110
+ <CdcDataBite
111
+ key={col.widget}
112
+ config={visualizationConfig}
113
+ isEditor={false}
114
+ setConfig={newConfig => {
115
+ updateChildConfig(col.widget, newConfig)
116
+ }}
117
+ isDashboard={true}
118
+ />
119
+ )}
120
+ {visualizationConfig.type === 'waffle-chart' && (
121
+ <CdcWaffleChart
122
+ key={col.widget}
123
+ config={visualizationConfig}
124
+ isEditor={false}
125
+ setConfig={newConfig => {
126
+ updateChildConfig(col.widget, newConfig)
127
+ }}
128
+ isDashboard={true}
129
+ configUrl={config.table && config.table.show && config.datasets && visualizationConfig.table && visualizationConfig.table.showDataTableLink ? tableLink : undefined}
130
+ />
131
+ )}
132
+ {visualizationConfig.type === 'markup-include' && (
133
+ <CdcMarkupInclude
134
+ key={col.widget}
135
+ config={visualizationConfig}
136
+ configUrl={undefined}
137
+ isDashboard={true}
138
+ isEditor={false}
139
+ setConfig={newConfig => {
140
+ updateChildConfig(col.widget, newConfig)
141
+ }}
142
+ />
143
+ )}
144
+ {visualizationConfig.type === 'filtered-text' && (
145
+ <CdcFilteredText
146
+ key={col.widget}
147
+ config={visualizationConfig}
148
+ isEditor={false}
149
+ setConfig={newConfig => {
150
+ updateChildConfig(col.widget, newConfig)
151
+ }}
152
+ isDashboard={true}
153
+ configUrl={undefined}
154
+ />
155
+ )}
156
+ {visualizationConfig.type === 'filter-dropdowns' && !hideFilter && (
157
+ <React.Fragment key={col.widget}>
158
+ <Filters hide={visualizationConfig.hide} filters={config.dashboard.sharedFilters} apiFilterDropdowns={apiFilterDropdowns} handleOnChange={handleOnChange} />
159
+ <GoButton autoLoad={visualizationConfig.autoLoad} />
160
+ </React.Fragment>
161
+ )}
162
+ {visualizationConfig.type === 'table' && (
163
+ <DataTableStandAlone
164
+ key={col.widget}
165
+ updateConfig={newConfig => {
166
+ updateChildConfig(col.widget, newConfig)
167
+ }}
168
+ visualizationKey={col.widget}
169
+ config={visualizationConfig as TableConfig}
170
+ viewport={currentViewport}
171
+ />
172
+ )}
173
+ </div>
174
+ </React.Fragment>
175
+ )
176
+ }
177
+ return <React.Fragment key={`vis__${index}__${colIndex}`}></React.Fragment>
178
+ })}
179
+ </div>
180
+ )
181
+ }
182
+
183
+ export default VisualizationRow
@@ -7,6 +7,8 @@ import { Table } from '@cdc/core/types/Table'
7
7
  const addVisualization = (type, subType) => {
8
8
  const modalWillOpen = type !== 'markup-include'
9
9
  const newVisualizationConfig: Partial<Visualization> = {
10
+ filters: [],
11
+ filterBehavior: 'Filter Change',
10
12
  newViz: type !== 'table',
11
13
  openModal: modalWillOpen,
12
14
  uid: type + Date.now(),
@@ -21,15 +23,36 @@ const addVisualization = (type, subType) => {
21
23
  newVisualizationConfig.general = {}
22
24
  newVisualizationConfig.general.geoType = subType
23
25
  break
24
- case 'data-bite' || 'waffle-chart' || 'markup-include' || 'filtered-text':
26
+ case 'data-bite' || 'waffle-chart' || 'filtered-text':
25
27
  newVisualizationConfig.visualizationType = type
26
28
  break
27
29
  case 'table':
28
- const tableConfig: Table = { label: 'Data Table', show: true, showDownloadUrl: false, showVertical: true, expanded: true }
30
+ const tableConfig: Table = { label: 'Data Table', show: true, showDownloadUrl: false, showVertical: true, expanded: true, collapsible: true }
29
31
  newVisualizationConfig.table = tableConfig
30
32
  newVisualizationConfig.columns = {}
31
33
  newVisualizationConfig.dataFormat = {}
32
34
  newVisualizationConfig.visualizationType = type
35
+ break
36
+ case 'markup-include':
37
+ newVisualizationConfig.contentEditor = {
38
+ inlineHTML: '<h2>Inline HTML</h2>',
39
+ markupVariables: [],
40
+ showHeader: true,
41
+ srcUrl: '#example',
42
+ title: 'Markup Include',
43
+ useInlineHTML: true
44
+ }
45
+ newVisualizationConfig.theme = 'theme-blue'
46
+ newVisualizationConfig.visual = {
47
+ border: false,
48
+ accent: false,
49
+ background: false,
50
+ hideBackgroundColor: false,
51
+ borderColorTheme: false
52
+ }
53
+ newVisualizationConfig.showEditorPanel = true
54
+ newVisualizationConfig.visualizationType = type
55
+
33
56
  break
34
57
  default:
35
58
  newVisualizationConfig.visualizationType = type
@@ -65,7 +88,7 @@ const VisualizationsPanel = ({ loadConfig, config }) => (
65
88
  <Widget addVisualization={() => addVisualization('table', '')} type='table' />
66
89
  </div>
67
90
  <span className='subheading-3'>Advanced</span>
68
- <AdvancedEditor loadConfig={loadConfig} state={config} convertStateToConfig={undefined} />
91
+ <AdvancedEditor loadConfig={loadConfig} state={config} convertStateToConfig={() => undefined} />
69
92
  </div>
70
93
  )
71
94