@cdc/dashboard 4.25.6 → 4.25.7-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.
@@ -1,287 +1,286 @@
1
- import {
2
- Accordion,
3
- AccordionItem,
4
- AccordionItemButton,
5
- AccordionItemHeading,
6
- AccordionItemPanel
7
- } from 'react-accessible-accordion'
8
- import { useContext, useMemo, useState } from 'react'
9
- import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
10
- import Tooltip from '@cdc/core/components/ui/Tooltip'
11
- import Icon from '@cdc/core/components/ui/Icon'
12
- import FieldSetWrapper from '@cdc/core/components/EditorPanel/FieldSetWrapper'
13
- import FilterEditor from './components/FilterEditor'
14
- import { AnyVisualization } from '@cdc/core/types/Visualization'
15
- import { DashboardContext, DashboardDispatchContext } from '../../../DashboardContext'
16
- import _ from 'lodash'
17
- import { DashboardFilters } from '../../../types/DashboardFilters'
18
- import { SharedFilter } from '../../../types/SharedFilter'
19
- import { useGlobalContext } from '@cdc/core/components/GlobalContext'
20
- import DeleteFilterModal from './components/DeleteFilterModal'
21
- import { addValuesToDashboardFilters } from '../../../helpers/addValuesToDashboardFilters'
22
- import { FILTER_STYLE } from '../../../types/FilterStyles'
23
- import { handleSorting } from '@cdc/core/components/Filters'
24
- import { removeDashboardFilter } from '../../../helpers/removeDashboardFilter'
25
-
26
- type DashboardFitlersEditorProps = {
27
- vizConfig: DashboardFilters
28
- updateConfig: Function
29
- }
30
-
31
- const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConfig, updateConfig }) => {
32
- const { config, loadAPIFilters, data } = useContext(DashboardContext)
33
- const { overlay } = useGlobalContext()
34
- const {
35
- dashboard: { sharedFilters },
36
- visualizations
37
- } = config
38
- const dispatch = useContext(DashboardDispatchContext)
39
-
40
- const existingOptions = useMemo(() => {
41
- const sharedFilterIndexes = (config.visualizations[vizConfig.uid] as DashboardFilters).sharedFilterIndexes.map(
42
- Number
43
- )
44
- return config.dashboard.sharedFilters
45
- ?.map<[number, string]>(({ key }, i) => [i, key])
46
- .filter(([filterIndex]) => !sharedFilterIndexes.includes(filterIndex)) // filter out already added filters
47
- .map(([filterIndex, filterName]) => (
48
- <option key={filterIndex} value={filterIndex}>{`${filterIndex} - ${filterName}`}</option>
49
- ))
50
- }, [config.visualizations, vizConfig.uid])
51
-
52
- const openControls = useState({})
53
- const [canAddExisting, setCanAddExisting] = useState(false)
54
-
55
- const updateFilterProp = (prop: string, index: number, value) => {
56
- const newSharedFilters = _.cloneDeep(sharedFilters)
57
- const {
58
- apiEndpoint: oldEndpoint,
59
- valueSelector: oldValueSelector,
60
- textSelector: oldTextSelector,
61
- subgroupValueSelector: oldSubgroupValueSelector,
62
- subgroupTextSelector: oldSubgroupTextSelector
63
- } = sharedFilters[index].apiFilter || {}
64
- const apiFilterChanged =
65
- value?.apiEndpoint !== oldEndpoint ||
66
- value?.valueSelector !== oldValueSelector ||
67
- value?.textSelector !== oldTextSelector ||
68
- value?.subgroupValueSelector !== oldSubgroupValueSelector ||
69
- value?.subgroupTextSelector !== oldSubgroupTextSelector
70
-
71
- newSharedFilters[index][prop] = value
72
- if (prop === 'columnName') {
73
- if (newSharedFilters[index].subGrouping) delete newSharedFilters[index].subGrouping
74
- // changing a data column and want to load the data into the preview options
75
- const sharedFiltersWithValues = addValuesToDashboardFilters(newSharedFilters, data)
76
- dispatch({ type: 'SET_SHARED_FILTERS', payload: sharedFiltersWithValues })
77
- } else if (prop === 'filterStyle') {
78
- newSharedFilters[index] = {
79
- ...newSharedFilters[index],
80
- active: '',
81
- apiFilter: {
82
- apiEndpoint: '',
83
- subgroupValueSelector: '',
84
- textSelector: '',
85
- valueSelector: ''
86
- },
87
- filterStyle: value
88
- }
89
- dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
90
- } else if (prop === 'apiFilter' && value.apiEndpoint && value.valueSelector && apiFilterChanged) {
91
- if (sharedFilters[index].filterStyle === FILTER_STYLE.nestedDropdown && value.subgroupValueSelector) {
92
- newSharedFilters[index].subGrouping = {
93
- active: '',
94
- columnName: '',
95
- setByQueryParameter: '',
96
- valuesLookup: {}
97
- }
98
- }
99
- // changing a api filter and want to load the api data into the preview.
100
- // automatically dispatches SET_SHARED_FILTERS
101
- loadAPIFilters(newSharedFilters, {})
102
- } else {
103
- handleSorting(newSharedFilters[index])
104
- dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
105
- }
106
- }
107
-
108
- const toggleNestedQueryParameters = (index, checked: boolean) => {
109
- const newSharedFilters = _.cloneDeep(sharedFilters)
110
- const filter = newSharedFilters[index]
111
- const isUrlFilter = filter.type === 'urlfilter'
112
- const groupColumnName = isUrlFilter ? filter.apiFilter.valueSelector : filter.columnName
113
- const subGroupColumnName = isUrlFilter ? filter.apiFilter.subgroupValueSelector : filter.subGrouping.columnName
114
- filter.setByQueryParameter = checked ? groupColumnName : undefined
115
- filter.subGrouping.setByQueryParameter = checked ? subGroupColumnName : undefined
116
- dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
117
- }
118
-
119
- const removeFilter = index => {
120
- const [newSharedFilters, newVisualizations] = removeDashboardFilter(index, sharedFilters, visualizations)
121
- const dashboard = {...config.dashboard, sharedFilters: newSharedFilters}
122
- dispatch({ type: 'SET_CONFIG', payload: { dashboard, visualizations: newVisualizations } })
123
- }
124
-
125
- const addNewFilter = () => {
126
- const _sharedFilters = _.cloneDeep(sharedFilters) || []
127
- const columnName = 'New Dashboard Filter ' + (_sharedFilters.length + 1)
128
- const newFilter = { key: columnName, showDropdown: true, values: [] } as SharedFilter
129
- dispatch({ type: 'SET_SHARED_FILTERS', payload: [..._sharedFilters, newFilter] })
130
- updateConfig({ ...vizConfig, sharedFilterIndexes: [...vizConfig.sharedFilterIndexes, _sharedFilters.length] })
131
- }
132
-
133
- return (
134
- <Accordion allowZeroExpanded={true}>
135
- <AccordionItem>
136
- <AccordionItemHeading>
137
- <AccordionItemButton>General</AccordionItemButton>
138
- </AccordionItemHeading>
139
- <AccordionItemPanel>
140
- <Select
141
- value={vizConfig.filterBehavior}
142
- label='Filter Behavior'
143
- updateField={(_section, _subsection, _key, value) => {
144
- updateConfig({ ...vizConfig, filterBehavior: value })
145
- }}
146
- options={['Apply Button', 'Filter Change']}
147
- tooltip={
148
- <Tooltip style={{ textTransform: 'none' }}>
149
- <Tooltip.Target>
150
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
151
- </Tooltip.Target>
152
- <Tooltip.Content>
153
- <p>
154
- The Apply Button option changes the visualization when the user clicks "apply". The Filter Change
155
- option immediately changes the visualization when the selection is changed.
156
- </p>
157
- </Tooltip.Content>
158
- </Tooltip>
159
- }
160
- />
161
- {vizConfig.filterBehavior === 'Apply Button' && (
162
- <TextField
163
- label='Apply Filter Button Text'
164
- maxLength={20}
165
- value={vizConfig.applyFiltersButtonText}
166
- updateField={(_section, _subsection, _key, value) => {
167
- updateConfig({ ...vizConfig, applyFiltersButtonText: value })
168
- }}
169
- />
170
- )}
171
- {vizConfig.filterBehavior === 'Filter Change' && (
172
- <CheckBox
173
- label='Auto Load'
174
- value={vizConfig.autoLoad}
175
- updateField={(_section, _subsection, _key, value) => {
176
- updateConfig({ ...vizConfig, autoLoad: value })
177
- }}
178
- tooltip={
179
- <Tooltip style={{ textTransform: 'none' }}>
180
- <Tooltip.Target>
181
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
182
- </Tooltip.Target>
183
- <Tooltip.Content>
184
- <p>
185
- Check if you would like for all URL filters to automatically select a value when a parent filter
186
- is changed.
187
- </p>
188
- </Tooltip.Content>
189
- </Tooltip>
190
- }
191
- />
192
- )}
193
- </AccordionItemPanel>
194
- </AccordionItem>
195
-
196
- <AccordionItem>
197
- <AccordionItemHeading>
198
- <AccordionItemButton>Filters</AccordionItemButton>
199
- </AccordionItemHeading>
200
- <AccordionItemPanel>
201
- {vizConfig.sharedFilterIndexes.map(index => {
202
- const filter = sharedFilters[index]
203
- return (
204
- <FieldSetWrapper
205
- key={filter.key + index}
206
- fieldName={filter.key}
207
- fieldKey={index}
208
- fieldType='Dashboard Filter'
209
- controls={openControls}
210
- deleteField={() => {
211
- overlay?.actions.openOverlay(
212
- <DeleteFilterModal
213
- removeFilterCompletely={removeFilter}
214
- removeFilterFromViz={index => {
215
- updateConfig({
216
- ...vizConfig,
217
- sharedFilterIndexes: vizConfig.sharedFilterIndexes.filter(i => i !== index)
218
- })
219
- }}
220
- filterIndex={index}
221
- />
222
- )
223
- }}
224
- >
225
- <FilterEditor
226
- filter={filter}
227
- filterIndex={index}
228
- updateFilterProp={(name, value) => {
229
- updateFilterProp(name, index, value)
230
- }}
231
- toggleNestedQueryParameters={checked => {
232
- toggleNestedQueryParameters(index, checked)
233
- }}
234
- config={config}
235
- />
236
- </FieldSetWrapper>
237
- )
238
- })}
239
- <button onClick={addNewFilter} className='btn btn-primary full-width'>
240
- Add Filter
241
- </button>
242
- {canAddExisting ? (
243
- <label>
244
- <span className='edit-label column-heading'>
245
- Select Existing Dashboard Filter
246
- <Tooltip style={{ textTransform: 'none' }}>
247
- <Tooltip.Target>
248
- <Icon display='question' style={{ marginLeft: '0.5rem' }} />
249
- </Tooltip.Target>
250
- <Tooltip.Content>
251
- <p>
252
- This feature is indentended to support legacy functionality. Be advised that any change to the
253
- filter in this editor will reflect on the whole dashboard.{' '}
254
- </p>
255
- </Tooltip.Content>
256
- </Tooltip>
257
- </span>
258
- <select
259
- value={''}
260
- onChange={e => {
261
- updateConfig({
262
- ...vizConfig,
263
- sharedFilterIndexes: [...vizConfig.sharedFilterIndexes, e.target.value]
264
- })
265
- setCanAddExisting(false)
266
- }}
267
- >
268
- {[
269
- <option key='select' value=''>
270
- Select
271
- </option>,
272
- ...existingOptions
273
- ]}
274
- </select>
275
- </label>
276
- ) : (
277
- <button onClick={() => setCanAddExisting(true)} className='btn btn-primary full-width mt-2'>
278
- Add Existing Dashboard Filter
279
- </button>
280
- )}
281
- </AccordionItemPanel>
282
- </AccordionItem>
283
- </Accordion>
284
- )
285
- }
286
-
287
- export default DashboardFiltersEditor
1
+ import {
2
+ Accordion,
3
+ AccordionItem,
4
+ AccordionItemButton,
5
+ AccordionItemHeading,
6
+ AccordionItemPanel
7
+ } from 'react-accessible-accordion'
8
+ import { useContext, useMemo, useState } from 'react'
9
+ import { CheckBox, Select, TextField } from '@cdc/core/components/EditorPanel/Inputs'
10
+ import Tooltip from '@cdc/core/components/ui/Tooltip'
11
+ import Icon from '@cdc/core/components/ui/Icon'
12
+ import FieldSetWrapper from '@cdc/core/components/EditorPanel/FieldSetWrapper'
13
+ import FilterEditor from './components/FilterEditor'
14
+ import { DashboardContext, DashboardDispatchContext } from '../../../DashboardContext'
15
+ import _ from 'lodash'
16
+ import { DashboardFilters } from '../../../types/DashboardFilters'
17
+ import { SharedFilter } from '../../../types/SharedFilter'
18
+ import { useGlobalContext } from '@cdc/core/components/GlobalContext'
19
+ import DeleteFilterModal from './components/DeleteFilterModal'
20
+ import { addValuesToDashboardFilters } from '../../../helpers/addValuesToDashboardFilters'
21
+ import { FILTER_STYLE } from '../../../types/FilterStyles'
22
+ import { handleSorting } from '@cdc/core/components/Filters'
23
+ import { removeDashboardFilter } from '../../../helpers/removeDashboardFilter'
24
+
25
+ type DashboardFitlersEditorProps = {
26
+ vizConfig: DashboardFilters
27
+ updateConfig: Function
28
+ }
29
+
30
+ const DashboardFiltersEditor: React.FC<DashboardFitlersEditorProps> = ({ vizConfig, updateConfig }) => {
31
+ const { config, loadAPIFilters, data } = useContext(DashboardContext)
32
+ const { overlay } = useGlobalContext()
33
+ const {
34
+ dashboard: { sharedFilters },
35
+ visualizations
36
+ } = config
37
+ const dispatch = useContext(DashboardDispatchContext)
38
+
39
+ const existingOptions = useMemo(() => {
40
+ const sharedFilterIndexes = (config.visualizations[vizConfig.uid] as DashboardFilters).sharedFilterIndexes.map(
41
+ Number
42
+ )
43
+ return config.dashboard.sharedFilters
44
+ ?.map<[number, string]>(({ key }, i) => [i, key])
45
+ .filter(([filterIndex]) => !sharedFilterIndexes.includes(filterIndex)) // filter out already added filters
46
+ .map(([filterIndex, filterName]) => (
47
+ <option key={filterIndex} value={filterIndex}>{`${filterIndex} - ${filterName}`}</option>
48
+ ))
49
+ }, [config.visualizations, vizConfig.uid])
50
+
51
+ const openControls = useState({})
52
+ const [canAddExisting, setCanAddExisting] = useState(false)
53
+
54
+ const updateFilterProp = (prop: string, index: number, value) => {
55
+ const newSharedFilters = _.cloneDeep(sharedFilters)
56
+ const {
57
+ apiEndpoint: oldEndpoint,
58
+ valueSelector: oldValueSelector,
59
+ textSelector: oldTextSelector,
60
+ subgroupValueSelector: oldSubgroupValueSelector,
61
+ subgroupTextSelector: oldSubgroupTextSelector
62
+ } = sharedFilters[index].apiFilter || {}
63
+ const apiFilterChanged =
64
+ value?.apiEndpoint !== oldEndpoint ||
65
+ value?.valueSelector !== oldValueSelector ||
66
+ value?.textSelector !== oldTextSelector ||
67
+ value?.subgroupValueSelector !== oldSubgroupValueSelector ||
68
+ value?.subgroupTextSelector !== oldSubgroupTextSelector
69
+
70
+ newSharedFilters[index][prop] = value
71
+ if (prop === 'columnName') {
72
+ if (newSharedFilters[index].subGrouping) delete newSharedFilters[index].subGrouping
73
+ // changing a data column and want to load the data into the preview options
74
+ const sharedFiltersWithValues = addValuesToDashboardFilters(newSharedFilters, data)
75
+ dispatch({ type: 'SET_SHARED_FILTERS', payload: sharedFiltersWithValues })
76
+ } else if (prop === 'filterStyle') {
77
+ newSharedFilters[index] = {
78
+ ...newSharedFilters[index],
79
+ active: '',
80
+ apiFilter: {
81
+ apiEndpoint: '',
82
+ subgroupValueSelector: '',
83
+ textSelector: '',
84
+ valueSelector: ''
85
+ },
86
+ filterStyle: value
87
+ }
88
+ dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
89
+ } else if (prop === 'apiFilter' && value.apiEndpoint && value.valueSelector && apiFilterChanged) {
90
+ if (sharedFilters[index].filterStyle === FILTER_STYLE.nestedDropdown && value.subgroupValueSelector) {
91
+ newSharedFilters[index].subGrouping = {
92
+ active: '',
93
+ columnName: '',
94
+ setByQueryParameter: '',
95
+ valuesLookup: {}
96
+ }
97
+ }
98
+ // changing a api filter and want to load the api data into the preview.
99
+ // automatically dispatches SET_SHARED_FILTERS
100
+ loadAPIFilters(newSharedFilters, {})
101
+ } else {
102
+ handleSorting(newSharedFilters[index])
103
+ dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
104
+ }
105
+ }
106
+
107
+ const toggleNestedQueryParameters = (index, checked: boolean) => {
108
+ const newSharedFilters = _.cloneDeep(sharedFilters)
109
+ const filter = newSharedFilters[index]
110
+ const isUrlFilter = filter.type === 'urlfilter'
111
+ const groupColumnName = isUrlFilter ? filter.apiFilter.valueSelector : filter.columnName
112
+ const subGroupColumnName = isUrlFilter ? filter.apiFilter.subgroupValueSelector : filter.subGrouping.columnName
113
+ filter.setByQueryParameter = checked ? groupColumnName : undefined
114
+ filter.subGrouping.setByQueryParameter = checked ? subGroupColumnName : undefined
115
+ dispatch({ type: 'SET_SHARED_FILTERS', payload: newSharedFilters })
116
+ }
117
+
118
+ const removeFilter = index => {
119
+ const [newSharedFilters, newVisualizations] = removeDashboardFilter(index, sharedFilters, visualizations)
120
+ const dashboard = { ...config.dashboard, sharedFilters: newSharedFilters }
121
+ dispatch({ type: 'SET_CONFIG', payload: { dashboard, visualizations: newVisualizations } })
122
+ }
123
+
124
+ const addNewFilter = () => {
125
+ const _sharedFilters = _.cloneDeep(sharedFilters) || []
126
+ const columnName = 'New Dashboard Filter ' + (_sharedFilters.length + 1)
127
+ const newFilter = { key: columnName, showDropdown: true, values: [] } as SharedFilter
128
+ dispatch({ type: 'SET_SHARED_FILTERS', payload: [..._sharedFilters, newFilter] })
129
+ updateConfig({ ...vizConfig, sharedFilterIndexes: [...vizConfig.sharedFilterIndexes, _sharedFilters.length] })
130
+ }
131
+
132
+ return (
133
+ <Accordion allowZeroExpanded={true}>
134
+ <AccordionItem>
135
+ <AccordionItemHeading>
136
+ <AccordionItemButton>General</AccordionItemButton>
137
+ </AccordionItemHeading>
138
+ <AccordionItemPanel>
139
+ <Select
140
+ value={vizConfig.filterBehavior}
141
+ label='Filter Behavior'
142
+ updateField={(_section, _subsection, _key, value) => {
143
+ updateConfig({ ...vizConfig, filterBehavior: value })
144
+ }}
145
+ options={['Apply Button', 'Filter Change']}
146
+ tooltip={
147
+ <Tooltip style={{ textTransform: 'none' }}>
148
+ <Tooltip.Target>
149
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
150
+ </Tooltip.Target>
151
+ <Tooltip.Content>
152
+ <p>
153
+ The Apply Button option changes the visualization when the user clicks "apply". The Filter Change
154
+ option immediately changes the visualization when the selection is changed.
155
+ </p>
156
+ </Tooltip.Content>
157
+ </Tooltip>
158
+ }
159
+ />
160
+ {vizConfig.filterBehavior === 'Apply Button' && (
161
+ <TextField
162
+ label='Apply Filter Button Text'
163
+ maxLength={20}
164
+ value={vizConfig.applyFiltersButtonText}
165
+ updateField={(_section, _subsection, _key, value) => {
166
+ updateConfig({ ...vizConfig, applyFiltersButtonText: value })
167
+ }}
168
+ />
169
+ )}
170
+ {vizConfig.filterBehavior === 'Filter Change' && (
171
+ <CheckBox
172
+ label='Auto Load'
173
+ value={vizConfig.autoLoad}
174
+ updateField={(_section, _subsection, _key, value) => {
175
+ updateConfig({ ...vizConfig, autoLoad: value })
176
+ }}
177
+ tooltip={
178
+ <Tooltip style={{ textTransform: 'none' }}>
179
+ <Tooltip.Target>
180
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
181
+ </Tooltip.Target>
182
+ <Tooltip.Content>
183
+ <p>
184
+ Check if you would like for all URL filters to automatically select a value when a parent filter
185
+ is changed.
186
+ </p>
187
+ </Tooltip.Content>
188
+ </Tooltip>
189
+ }
190
+ />
191
+ )}
192
+ </AccordionItemPanel>
193
+ </AccordionItem>
194
+
195
+ <AccordionItem>
196
+ <AccordionItemHeading>
197
+ <AccordionItemButton>Filters</AccordionItemButton>
198
+ </AccordionItemHeading>
199
+ <AccordionItemPanel>
200
+ {vizConfig.sharedFilterIndexes.map(index => {
201
+ const filter = sharedFilters[index]
202
+ return (
203
+ <FieldSetWrapper
204
+ key={filter.key + index}
205
+ fieldName={filter.key}
206
+ fieldKey={index}
207
+ fieldType='Dashboard Filter'
208
+ controls={openControls}
209
+ deleteField={() => {
210
+ overlay?.actions.openOverlay(
211
+ <DeleteFilterModal
212
+ removeFilterCompletely={removeFilter}
213
+ removeFilterFromViz={index => {
214
+ updateConfig({
215
+ ...vizConfig,
216
+ sharedFilterIndexes: vizConfig.sharedFilterIndexes.filter(i => i !== index)
217
+ })
218
+ }}
219
+ filterIndex={index}
220
+ />
221
+ )
222
+ }}
223
+ >
224
+ <FilterEditor
225
+ filter={filter}
226
+ filterIndex={index}
227
+ updateFilterProp={(name, value) => {
228
+ updateFilterProp(name, index, value)
229
+ }}
230
+ toggleNestedQueryParameters={checked => {
231
+ toggleNestedQueryParameters(index, checked)
232
+ }}
233
+ config={config}
234
+ />
235
+ </FieldSetWrapper>
236
+ )
237
+ })}
238
+ <button onClick={addNewFilter} className='btn btn-primary full-width'>
239
+ Add Filter
240
+ </button>
241
+ {canAddExisting ? (
242
+ <label>
243
+ <span className='edit-label column-heading'>
244
+ Select Existing Dashboard Filter
245
+ <Tooltip style={{ textTransform: 'none' }}>
246
+ <Tooltip.Target>
247
+ <Icon display='question' style={{ marginLeft: '0.5rem' }} />
248
+ </Tooltip.Target>
249
+ <Tooltip.Content>
250
+ <p>
251
+ This feature is indentended to support legacy functionality. Be advised that any change to the
252
+ filter in this editor will reflect on the whole dashboard.{' '}
253
+ </p>
254
+ </Tooltip.Content>
255
+ </Tooltip>
256
+ </span>
257
+ <select
258
+ value={''}
259
+ onChange={e => {
260
+ updateConfig({
261
+ ...vizConfig,
262
+ sharedFilterIndexes: [...vizConfig.sharedFilterIndexes, e.target.value]
263
+ })
264
+ setCanAddExisting(false)
265
+ }}
266
+ >
267
+ {[
268
+ <option key='select' value=''>
269
+ Select
270
+ </option>,
271
+ ...existingOptions
272
+ ]}
273
+ </select>
274
+ </label>
275
+ ) : (
276
+ <button onClick={() => setCanAddExisting(true)} className='btn btn-primary full-width mt-2'>
277
+ Add Existing Dashboard Filter
278
+ </button>
279
+ )}
280
+ </AccordionItemPanel>
281
+ </AccordionItem>
282
+ </Accordion>
283
+ )
284
+ }
285
+
286
+ export default DashboardFiltersEditor
@@ -17,7 +17,6 @@ const Header = (props: HeaderProps) => {
17
17
  const tabs: Tab[] = ['Dashboard Description', 'Data Table Settings', 'Dashboard Preview']
18
18
  const { visualizationKey, subEditor } = props
19
19
  const { config, setParentConfig, tabSelected, data } = useContext(DashboardContext)
20
- if (!config) return null
21
20
  const dispatch = useContext(DashboardDispatchContext)
22
21
  const back = () => {
23
22
  if (!visualizationKey) return
@@ -80,8 +79,9 @@ const Header = (props: HeaderProps) => {
80
79
  }
81
80
  }
82
81
 
83
- const multiInitialized = !!config.multiDashboards
82
+ if (!config) return null
84
83
 
84
+ const multiInitialized = !!config.multiDashboards
85
85
  return (
86
86
  <div aria-level={2} role='heading' className={`editor-heading${subEditor ? ' sub-dashboard-viz' : ''}`}>
87
87
  {subEditor ? (