@cdc/dashboard 4.24.5 → 4.24.9
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 +144406 -127510
- package/examples/custom/css/respiratory.css +236 -0
- package/examples/custom/js/respiratory.js +242 -0
- package/examples/default-multi-dataset-shared-filter.json +1729 -0
- package/examples/ed-visits-county-file.json +618 -0
- package/examples/filtered-dash.json +6 -21
- package/examples/single-state-dashboard-filters.json +421 -0
- package/examples/state-level.json +90136 -0
- package/examples/state-points.json +10474 -0
- package/examples/test-file.json +147 -0
- package/examples/testing.json +94456 -0
- package/index.html +25 -4
- package/package.json +12 -11
- package/src/CdcDashboard.tsx +5 -1
- package/src/CdcDashboardComponent.tsx +250 -327
- package/src/DashboardContext.tsx +15 -1
- package/src/_stories/Dashboard.stories.tsx +158 -40
- package/src/_stories/_mock/api-filter-chart.json +11 -35
- package/src/_stories/_mock/api-filter-map.json +17 -31
- package/src/_stories/_mock/bump-chart.json +3554 -0
- package/src/_stories/_mock/methodology.json +412 -0
- package/src/_stories/_mock/methodologyAPI.ts +90 -0
- package/src/_stories/_mock/multi-viz.json +3 -4
- package/src/_stories/_mock/pivot-filter.json +14 -12
- package/src/_stories/_mock/single-state-dashboard-filters.json +390 -0
- package/src/components/CollapsibleVisualizationRow.tsx +44 -0
- package/src/components/Column.tsx +1 -1
- package/src/components/DashboardFilters/DashboardFilters.tsx +102 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +218 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/DeleteFilterModal.tsx +48 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +477 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/index.ts +1 -0
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +191 -0
- package/src/components/DashboardFilters/index.ts +3 -0
- package/src/components/DataDesignerModal.tsx +9 -9
- package/src/components/ExpandCollapseButtons.tsx +20 -0
- package/src/components/Header/Header.tsx +1 -102
- package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +24 -12
- package/src/components/Row.tsx +52 -19
- package/src/components/Toggle/Toggle.tsx +2 -4
- package/src/components/VisualizationRow.tsx +169 -30
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +116 -0
- package/src/components/VisualizationsPanel/index.ts +1 -0
- package/src/components/VisualizationsPanel/visualizations-panel-styles.css +12 -0
- package/src/components/Widget.tsx +27 -90
- package/src/helpers/FilterBehavior.ts +4 -0
- package/src/helpers/addValuesToDashboardFilters.ts +49 -0
- package/src/helpers/apiFilterHelpers.ts +102 -0
- package/src/helpers/changeFilterActive.ts +39 -0
- package/src/helpers/filterData.ts +10 -48
- package/src/helpers/generateValuesForFilter.ts +1 -1
- package/src/helpers/getAutoLoadVisualization.ts +11 -0
- package/src/helpers/getFilteredData.ts +7 -5
- package/src/helpers/getVizConfig.ts +23 -2
- package/src/helpers/getVizRowColumnLocator.ts +2 -1
- package/src/helpers/hasDashboardApplyBehavior.ts +5 -0
- package/src/helpers/iconHash.tsx +5 -3
- package/src/helpers/loadAPIFilters.ts +74 -0
- package/src/helpers/mapDataToConfig.ts +29 -0
- package/src/helpers/processData.ts +2 -3
- package/src/helpers/reloadURLHelpers.ts +78 -0
- package/src/helpers/tests/addValuesToDashboardFilters.test.ts +44 -0
- package/src/helpers/tests/apiFilterHelpers.test.ts +156 -0
- package/src/helpers/tests/filterData.test.ts +1 -93
- package/src/helpers/tests/getFilteredData.test.ts +86 -0
- package/src/helpers/tests/loadAPIFiltersWrapper.test.ts +176 -0
- package/src/helpers/tests/reloadURLHelpers.test.ts +195 -0
- package/src/scss/editor-panel.scss +1 -1
- package/src/scss/grid.scss +34 -27
- package/src/scss/main.scss +41 -3
- package/src/scss/variables.scss +4 -0
- package/src/store/dashboard.actions.ts +12 -4
- package/src/store/dashboard.reducer.ts +30 -4
- package/src/types/APIFilter.ts +1 -5
- package/src/types/ConfigRow.ts +2 -0
- package/src/types/Dashboard.ts +1 -1
- package/src/types/DashboardConfig.ts +2 -4
- package/src/types/DashboardFilters.ts +7 -0
- package/src/types/InitialState.ts +1 -1
- package/src/types/MultiDashboard.ts +2 -2
- package/src/types/SharedFilter.ts +4 -6
- package/src/types/Tab.ts +1 -1
- package/LICENSE +0 -201
- package/src/components/Filters.tsx +0 -88
- package/src/components/Header/FilterModal.tsx +0 -510
- package/src/components/VisualizationsPanel.tsx +0 -95
- package/src/helpers/getApiFilterKey.ts +0 -5
|
@@ -1,510 +0,0 @@
|
|
|
1
|
-
import { useContext, useEffect, useMemo, useState } from 'react'
|
|
2
|
-
import { MultiDashboardConfig } from '../../types/MultiDashboard'
|
|
3
|
-
import { SharedFilter } from '../../types/SharedFilter'
|
|
4
|
-
import { DashboardDispatchContext } from '../../DashboardContext'
|
|
5
|
-
import { useGlobalContext } from '@cdc/core/components/GlobalContext'
|
|
6
|
-
import { APIFilter } from '../../types/APIFilter'
|
|
7
|
-
import Modal from '@cdc/core/components/ui/Modal'
|
|
8
|
-
import { FilterBehavior } from './Header'
|
|
9
|
-
import Tooltip from '@cdc/core/components/ui/Tooltip'
|
|
10
|
-
import Icon from '@cdc/core/components/ui/Icon'
|
|
11
|
-
import Button from '@cdc/core/components/elements/Button'
|
|
12
|
-
import fetchRemoteData from '@cdc/core/helpers/fetchRemoteData'
|
|
13
|
-
import DataTransform from '@cdc/core/helpers/DataTransform'
|
|
14
|
-
import { getVizRowColumnLocator } from '../../helpers/getVizRowColumnLocator'
|
|
15
|
-
import _ from 'lodash'
|
|
16
|
-
|
|
17
|
-
type ModalProps = {
|
|
18
|
-
config: MultiDashboardConfig
|
|
19
|
-
filterState: SharedFilter
|
|
20
|
-
index: number
|
|
21
|
-
removeFilter: Function
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const FilterModal: React.FC<ModalProps> = ({ config, filterState, index, removeFilter }) => {
|
|
25
|
-
const { overlay } = useGlobalContext()
|
|
26
|
-
const dispatch = useContext(DashboardDispatchContext)
|
|
27
|
-
const [filter, setFilter] = useState<SharedFilter>(filterState)
|
|
28
|
-
const [columns, setColumns] = useState<string[]>([])
|
|
29
|
-
const transform = new DataTransform()
|
|
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
|
-
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
const runSetColumns = async () => {
|
|
64
|
-
let columns = {}
|
|
65
|
-
let dataKeys = Object.keys(config.datasets)
|
|
66
|
-
|
|
67
|
-
for (let i = 0; i < dataKeys.length; i++) {
|
|
68
|
-
let _dataSet = config.datasets[dataKeys[i]]
|
|
69
|
-
if (!_dataSet.data && _dataSet.dataUrl) {
|
|
70
|
-
config.datasets[dataKeys[i]].data = await fetchRemoteData(config.datasets[dataKeys[i]].dataUrl)
|
|
71
|
-
_dataSet = config.datasets[dataKeys[i]]
|
|
72
|
-
if (_dataSet.dataDescription) {
|
|
73
|
-
try {
|
|
74
|
-
config.datasets[dataKeys[i]].data = transform.autoStandardize(_dataSet.data)
|
|
75
|
-
_dataSet = config.datasets[dataKeys[i]]
|
|
76
|
-
config.datasets[dataKeys[i]].data = transform.developerStandardize(_dataSet.data, _dataSet.dataDescription)
|
|
77
|
-
_dataSet = config.datasets[dataKeys[i]]
|
|
78
|
-
} catch (e) {
|
|
79
|
-
//Data not able to be standardized, leave as is
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (_dataSet.data) {
|
|
85
|
-
config.datasets[dataKeys[i]].data.forEach(row => {
|
|
86
|
-
Object.keys(row).forEach(columnName => (columns[columnName] = true))
|
|
87
|
-
})
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
setColumns(Object.keys(columns))
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
runSetColumns()
|
|
95
|
-
}, [config.datasets])
|
|
96
|
-
|
|
97
|
-
const saveChanges = () => {
|
|
98
|
-
let tempConfig = { ...config.dashboard }
|
|
99
|
-
tempConfig.sharedFilters[index] = filter
|
|
100
|
-
|
|
101
|
-
dispatch({ type: 'UPDATE_CONFIG', payload: [{ ...config, dashboard: tempConfig }] })
|
|
102
|
-
overlay?.actions.toggleOverlay()
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const updateFilterProp = (name, value) => {
|
|
106
|
-
const newFilter = { ..._.cloneDeep(filter), [name]: value }
|
|
107
|
-
|
|
108
|
-
setFilter(newFilter)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const addFilterUsedBy = (filter, value) => {
|
|
112
|
-
if (value === '') return
|
|
113
|
-
if (!filter.usedBy) filter.usedBy = []
|
|
114
|
-
filter.usedBy.push(value)
|
|
115
|
-
updateFilterProp('usedBy', filter.usedBy)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const removeFilterUsedBy = (filter, value) => {
|
|
119
|
-
let usedByIndex = filter.usedBy.indexOf(value)
|
|
120
|
-
if (usedByIndex !== -1) {
|
|
121
|
-
filter.usedBy.splice(usedByIndex, 1)
|
|
122
|
-
updateFilterProp('usedBy', filter.usedBy)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const updateAPIFilter = (key: keyof APIFilter, value: string | boolean) => {
|
|
127
|
-
const filterClone = _.cloneDeep(filter)
|
|
128
|
-
const _filter = filterClone.apiFilter || { apiEndpoint: '', valueSelector: '', textSelector: '' }
|
|
129
|
-
const newAPIFilter: APIFilter = { ..._filter, [key]: value }
|
|
130
|
-
setFilter({ ...filterClone, apiFilter: newAPIFilter })
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<Modal>
|
|
135
|
-
<Modal.Content>
|
|
136
|
-
<h2 className='shared-filter-modal__title'>Dashboard Filter Settings</h2>
|
|
137
|
-
<fieldset className='shared-filter-modal shared-filter-modal__fieldset' key={filter.columnName + index}>
|
|
138
|
-
<label>
|
|
139
|
-
<span className='edit-label column-heading'>Filter Type: </span>
|
|
140
|
-
<select defaultValue={filter.type || ''} onChange={e => updateFilterProp('type', e.target.value)}>
|
|
141
|
-
<option value=''>- Select Option -</option>
|
|
142
|
-
<option value='urlfilter'>URL</option>
|
|
143
|
-
<option value='datafilter'>Data</option>
|
|
144
|
-
</select>
|
|
145
|
-
</label>
|
|
146
|
-
{filter.type === 'urlfilter' && (
|
|
147
|
-
<>
|
|
148
|
-
<label>
|
|
149
|
-
<span className='edit-label column-heading'>Label: </span>
|
|
150
|
-
<input
|
|
151
|
-
type='text'
|
|
152
|
-
value={filter.key}
|
|
153
|
-
onChange={e => {
|
|
154
|
-
updateFilterProp('key', e.target.value)
|
|
155
|
-
}}
|
|
156
|
-
/>
|
|
157
|
-
</label>
|
|
158
|
-
{config.filterBehavior !== FilterBehavior.Apply && (
|
|
159
|
-
<>
|
|
160
|
-
<label>
|
|
161
|
-
<span className='edit-label column-heading'>URL to Filter: </span>
|
|
162
|
-
<select defaultValue={filter.datasetKey || ''} onChange={e => updateFilterProp('datasetKey', e.target.value)}>
|
|
163
|
-
<option value=''>- Select Option -</option>
|
|
164
|
-
{Object.keys(config.datasets).map(datasetKey => {
|
|
165
|
-
if (config.datasets[datasetKey].dataUrl) {
|
|
166
|
-
return (
|
|
167
|
-
<option key={datasetKey} value={datasetKey}>
|
|
168
|
-
{config.datasets[datasetKey].dataUrl}
|
|
169
|
-
</option>
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
return null
|
|
173
|
-
})}
|
|
174
|
-
</select>
|
|
175
|
-
</label>
|
|
176
|
-
<label>
|
|
177
|
-
<span className='edit-label column-heading'>Filter By: </span>
|
|
178
|
-
<select defaultValue={filter.filterBy || ''} onChange={e => updateFilterProp('filterBy', e.target.value)}>
|
|
179
|
-
<option value=''>- Select Option -</option>
|
|
180
|
-
<option key={'query-string'} value={'Query String'}>
|
|
181
|
-
Query String
|
|
182
|
-
</option>
|
|
183
|
-
<option key={'file-name'} value={'File Name'}>
|
|
184
|
-
File Name
|
|
185
|
-
</option>
|
|
186
|
-
</select>
|
|
187
|
-
</label>
|
|
188
|
-
{filter.filterBy === 'File Name' && (
|
|
189
|
-
<>
|
|
190
|
-
<label>
|
|
191
|
-
<span className='edit-label column-heading'>
|
|
192
|
-
File Name:
|
|
193
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
194
|
-
<Tooltip.Target>
|
|
195
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
196
|
-
</Tooltip.Target>
|
|
197
|
-
<Tooltip.Content>
|
|
198
|
-
<p>{`Add \${query}\ to replace the filename with the active dropdown value.`}</p>
|
|
199
|
-
</Tooltip.Content>
|
|
200
|
-
</Tooltip>
|
|
201
|
-
</span>
|
|
202
|
-
|
|
203
|
-
<input type='text' defaultValue={filter.fileName || ''} onChange={e => updateFilterProp('fileName', e.target.value)} />
|
|
204
|
-
</label>
|
|
205
|
-
|
|
206
|
-
<label>
|
|
207
|
-
<span className='edit-label column-heading'>
|
|
208
|
-
White Space Replacments
|
|
209
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
210
|
-
<Tooltip.Target>
|
|
211
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
212
|
-
</Tooltip.Target>
|
|
213
|
-
<Tooltip.Content>
|
|
214
|
-
<p>{`Set how whitespace characters will be handled in the file request`}</p>
|
|
215
|
-
</Tooltip.Content>
|
|
216
|
-
</Tooltip>
|
|
217
|
-
</span>
|
|
218
|
-
<select defaultValue={filter.whitespaceReplacement || 'Keep Spaces'} onChange={e => updateFilterProp('whitespaceReplacement', e.target.value)}>
|
|
219
|
-
<option key={'remove-spaces'} value={'Remove Spaces'}>
|
|
220
|
-
Remove Spaces
|
|
221
|
-
</option>
|
|
222
|
-
<option key={'replace-with-underscore'} value={'Replace With Underscore'}>
|
|
223
|
-
Replace With Underscore
|
|
224
|
-
</option>
|
|
225
|
-
<option key={'keep-spaces'} value={'Keep Spaces'}>
|
|
226
|
-
Keep Spaces
|
|
227
|
-
</option>
|
|
228
|
-
</select>
|
|
229
|
-
</label>
|
|
230
|
-
</>
|
|
231
|
-
)}
|
|
232
|
-
</>
|
|
233
|
-
)}
|
|
234
|
-
{filter.filterBy === 'Query String' && (
|
|
235
|
-
<label>
|
|
236
|
-
<span className='edit-label column-heading'>Query string parameter</span> <input type='text' defaultValue={filter.queryParameter} onChange={e => updateFilterProp('queryParameter', e.target.value)} />
|
|
237
|
-
</label>
|
|
238
|
-
)}
|
|
239
|
-
<label>
|
|
240
|
-
<span className='edit-label column-heading'>Filter API Endpoint: </span>
|
|
241
|
-
<input
|
|
242
|
-
type='text'
|
|
243
|
-
value={filter.apiFilter?.apiEndpoint}
|
|
244
|
-
onChange={e => {
|
|
245
|
-
updateAPIFilter('apiEndpoint', e.target.value)
|
|
246
|
-
}}
|
|
247
|
-
/>
|
|
248
|
-
</label>
|
|
249
|
-
<label>
|
|
250
|
-
<span className='edit-label column-heading'>
|
|
251
|
-
Option Text Selector:
|
|
252
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
253
|
-
<Tooltip.Target>
|
|
254
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
255
|
-
</Tooltip.Target>
|
|
256
|
-
<Tooltip.Content>
|
|
257
|
-
<p>Text to use in the html option element</p>
|
|
258
|
-
</Tooltip.Content>
|
|
259
|
-
</Tooltip>
|
|
260
|
-
</span>
|
|
261
|
-
<input
|
|
262
|
-
type='text'
|
|
263
|
-
value={filter.apiFilter?.textSelector}
|
|
264
|
-
onChange={e => {
|
|
265
|
-
updateAPIFilter('textSelector', e.target.value)
|
|
266
|
-
}}
|
|
267
|
-
/>
|
|
268
|
-
</label>
|
|
269
|
-
<label>
|
|
270
|
-
<span className='edit-label column-heading'>
|
|
271
|
-
Option Value Selector:
|
|
272
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
273
|
-
<Tooltip.Target>
|
|
274
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
275
|
-
</Tooltip.Target>
|
|
276
|
-
<Tooltip.Content>
|
|
277
|
-
<p>Value to use in the html option element</p>
|
|
278
|
-
</Tooltip.Content>
|
|
279
|
-
</Tooltip>
|
|
280
|
-
</span>
|
|
281
|
-
<input
|
|
282
|
-
type='text'
|
|
283
|
-
value={filter.apiFilter?.valueSelector}
|
|
284
|
-
onChange={e => {
|
|
285
|
-
updateAPIFilter('valueSelector', e.target.value)
|
|
286
|
-
}}
|
|
287
|
-
/>
|
|
288
|
-
</label>
|
|
289
|
-
<label>
|
|
290
|
-
<span className='edit-label column-heading'>Parent Filter: </span>
|
|
291
|
-
<select
|
|
292
|
-
value={filter.parents || []}
|
|
293
|
-
onChange={e => {
|
|
294
|
-
updateFilterProp('parents', e.target.value)
|
|
295
|
-
}}
|
|
296
|
-
>
|
|
297
|
-
<option value=''>Select a filter</option>
|
|
298
|
-
{config.dashboard.sharedFilters &&
|
|
299
|
-
config.dashboard.sharedFilters.map(sharedFilter => {
|
|
300
|
-
if (sharedFilter.key !== filter.key && sharedFilter.type !== 'datafilter') {
|
|
301
|
-
return <option value={sharedFilter.key}>{sharedFilter.key}</option>
|
|
302
|
-
}
|
|
303
|
-
})}
|
|
304
|
-
</select>
|
|
305
|
-
</label>
|
|
306
|
-
<label>
|
|
307
|
-
<span className='edit-label column-heading'>Auto Load: </span>
|
|
308
|
-
<input
|
|
309
|
-
type='checkbox'
|
|
310
|
-
checked={filter.apiFilter?.autoLoad}
|
|
311
|
-
onChange={e => {
|
|
312
|
-
updateAPIFilter('autoLoad', !filter.apiFilter?.autoLoad)
|
|
313
|
-
}}
|
|
314
|
-
/>
|
|
315
|
-
</label>
|
|
316
|
-
<label>
|
|
317
|
-
<span className='edit-label column-heading'>Default Value: </span>
|
|
318
|
-
<input
|
|
319
|
-
type='text'
|
|
320
|
-
value={filter.apiFilter?.defaultValue}
|
|
321
|
-
onChange={e => {
|
|
322
|
-
updateAPIFilter('defaultValue', e.target.value)
|
|
323
|
-
}}
|
|
324
|
-
/>
|
|
325
|
-
</label>
|
|
326
|
-
<label>
|
|
327
|
-
<span className='edit-label column-heading'>Default Value Set By Query String Parameter: </span>
|
|
328
|
-
<input
|
|
329
|
-
type='text'
|
|
330
|
-
value={filter.setByQueryParameter || ''}
|
|
331
|
-
onChange={e => {
|
|
332
|
-
updateFilterProp('setByQueryParameter', e.target.value)
|
|
333
|
-
}}
|
|
334
|
-
/>
|
|
335
|
-
</label>
|
|
336
|
-
</>
|
|
337
|
-
)}
|
|
338
|
-
{filter.type === 'datafilter' && (
|
|
339
|
-
<>
|
|
340
|
-
<label>
|
|
341
|
-
<span className='edit-label column-heading'>Filter: </span>
|
|
342
|
-
<select
|
|
343
|
-
value={filter.columnName}
|
|
344
|
-
onChange={e => {
|
|
345
|
-
updateFilterProp('columnName', e.target.value)
|
|
346
|
-
}}
|
|
347
|
-
>
|
|
348
|
-
<option value=''>- Select Option -</option>
|
|
349
|
-
{columns.map(dataKey => (
|
|
350
|
-
<option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
|
|
351
|
-
{dataKey}
|
|
352
|
-
</option>
|
|
353
|
-
))}
|
|
354
|
-
</select>
|
|
355
|
-
</label>
|
|
356
|
-
<label>
|
|
357
|
-
<span className='edit-label column-heading'>
|
|
358
|
-
Pivot:{' '}
|
|
359
|
-
<Tooltip style={{ textTransform: 'none' }}>
|
|
360
|
-
<Tooltip.Target>
|
|
361
|
-
<Icon display='question' style={{ marginLeft: '0.5rem' }} />
|
|
362
|
-
</Tooltip.Target>
|
|
363
|
-
<Tooltip.Content>
|
|
364
|
-
<p>The column whos values will be pivoted under the column selected as the Filter.</p>
|
|
365
|
-
</Tooltip.Content>
|
|
366
|
-
</Tooltip>
|
|
367
|
-
</span>
|
|
368
|
-
<select
|
|
369
|
-
value={filter.pivot}
|
|
370
|
-
onChange={e => {
|
|
371
|
-
updateFilterProp('pivot', e.target.value)
|
|
372
|
-
updateFilterProp('showDropdown', true)
|
|
373
|
-
}}
|
|
374
|
-
>
|
|
375
|
-
<option value=''>- Select Option -</option>
|
|
376
|
-
{columns
|
|
377
|
-
.filter(col => filter.columnName !== col)
|
|
378
|
-
.map(dataKey => (
|
|
379
|
-
<option value={dataKey} key={`filter-column-select-item-${dataKey}`}>
|
|
380
|
-
{dataKey}
|
|
381
|
-
</option>
|
|
382
|
-
))}
|
|
383
|
-
</select>
|
|
384
|
-
</label>
|
|
385
|
-
<label>
|
|
386
|
-
<span className='edit-label column-heading'>Label: </span>
|
|
387
|
-
<input
|
|
388
|
-
type='text'
|
|
389
|
-
value={filter.key}
|
|
390
|
-
onChange={e => {
|
|
391
|
-
updateFilterProp('key', e.target.value)
|
|
392
|
-
}}
|
|
393
|
-
/>
|
|
394
|
-
</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
|
-
|
|
408
|
-
<label>
|
|
409
|
-
<span className='edit-label column-heading'>Set By: </span>
|
|
410
|
-
<select value={filter.setBy} onChange={e => updateFilterProp('setBy', e.target.value)}>
|
|
411
|
-
<option value=''>- Select Option -</option>
|
|
412
|
-
{Object.keys(config.visualizations).map(vizKey => (
|
|
413
|
-
<option value={vizKey} key={`set-by-select-item-${vizKey}`}>
|
|
414
|
-
{config.visualizations[vizKey].general && config.visualizations[vizKey].general.title ? config.visualizations[vizKey].general.title : config.visualizations[vizKey].title || vizKey}
|
|
415
|
-
</option>
|
|
416
|
-
))}
|
|
417
|
-
</select>
|
|
418
|
-
</label>
|
|
419
|
-
<label>
|
|
420
|
-
<span className='edit-label column-heading'>Used By: </span>
|
|
421
|
-
<ul>
|
|
422
|
-
{filter.usedBy &&
|
|
423
|
-
filter.usedBy.map(opt => (
|
|
424
|
-
<li key={`used-by-list-item-${opt}`}>
|
|
425
|
-
<span>{usedByNameLookup[opt] || opt}</span>{' '}
|
|
426
|
-
<button
|
|
427
|
-
onClick={e => {
|
|
428
|
-
e.preventDefault()
|
|
429
|
-
removeFilterUsedBy(filter, opt)
|
|
430
|
-
}}
|
|
431
|
-
>
|
|
432
|
-
X
|
|
433
|
-
</button>
|
|
434
|
-
</li>
|
|
435
|
-
))}
|
|
436
|
-
</ul>
|
|
437
|
-
<select value='' onChange={e => addFilterUsedBy(filter, e.target.value)}>
|
|
438
|
-
<option value=''>- Select Option -</option>
|
|
439
|
-
{usedByOptions.map(opt => (
|
|
440
|
-
<option value={opt} key={`used-by-select-item-${opt}`}>
|
|
441
|
-
{usedByNameLookup[opt] || opt}
|
|
442
|
-
</option>
|
|
443
|
-
))}
|
|
444
|
-
</select>
|
|
445
|
-
</label>
|
|
446
|
-
<label>
|
|
447
|
-
<span className='edit-label column-heading'>Reset Label: </span>
|
|
448
|
-
<input
|
|
449
|
-
type='text'
|
|
450
|
-
value={filter.resetLabel || ''}
|
|
451
|
-
onChange={e => {
|
|
452
|
-
updateFilterProp('resetLabel', e.target.value)
|
|
453
|
-
}}
|
|
454
|
-
/>
|
|
455
|
-
</label>
|
|
456
|
-
<label>
|
|
457
|
-
<span className='edit-label column-heading'>Parent Filter: </span>
|
|
458
|
-
<select
|
|
459
|
-
value={filter.parents || []}
|
|
460
|
-
onChange={e => {
|
|
461
|
-
updateFilterProp('parents', e.target.value)
|
|
462
|
-
}}
|
|
463
|
-
>
|
|
464
|
-
<option value=''>Select a filter</option>
|
|
465
|
-
{config.dashboard.sharedFilters &&
|
|
466
|
-
config.dashboard.sharedFilters.map(sharedFilter => {
|
|
467
|
-
if (sharedFilter.key !== filter.key) {
|
|
468
|
-
return <option>{sharedFilter.key}</option>
|
|
469
|
-
}
|
|
470
|
-
})}
|
|
471
|
-
</select>
|
|
472
|
-
</label>
|
|
473
|
-
<label>
|
|
474
|
-
<span className='edit-label column-heading'>Default Value Set By Query String Parameter: </span>
|
|
475
|
-
<input
|
|
476
|
-
type='text'
|
|
477
|
-
value={filter.setByQueryParameter || ''}
|
|
478
|
-
onChange={e => {
|
|
479
|
-
updateFilterProp('setByQueryParameter', e.target.value)
|
|
480
|
-
}}
|
|
481
|
-
/>
|
|
482
|
-
</label>
|
|
483
|
-
</>
|
|
484
|
-
)}
|
|
485
|
-
</fieldset>
|
|
486
|
-
|
|
487
|
-
<Button
|
|
488
|
-
className='btn--remove warn'
|
|
489
|
-
onClick={() => {
|
|
490
|
-
removeFilter()
|
|
491
|
-
}}
|
|
492
|
-
>
|
|
493
|
-
Remove Filter
|
|
494
|
-
</Button>
|
|
495
|
-
|
|
496
|
-
<div className='shared-filter-modal__right-buttons'>
|
|
497
|
-
<Button className='btn--cancel muted' style={{ display: 'inline-block', marginRight: '1em' }} onClick={overlay?.actions.toggleOverlay}>
|
|
498
|
-
Cancel
|
|
499
|
-
</Button>
|
|
500
|
-
|
|
501
|
-
<Button type='button' className='btn--submit success' style={{ display: 'inline-block' }} onClick={saveChanges}>
|
|
502
|
-
Save
|
|
503
|
-
</Button>
|
|
504
|
-
</div>
|
|
505
|
-
</Modal.Content>
|
|
506
|
-
</Modal>
|
|
507
|
-
)
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
export default FilterModal
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { Visualization } from '@cdc/core/types/Visualization'
|
|
3
|
-
import Widget from './Widget'
|
|
4
|
-
import AdvancedEditor from '@cdc/core/components/AdvancedEditor'
|
|
5
|
-
import { Table } from '@cdc/core/types/Table'
|
|
6
|
-
|
|
7
|
-
const addVisualization = (type, subType) => {
|
|
8
|
-
const modalWillOpen = type !== 'markup-include'
|
|
9
|
-
const newVisualizationConfig: Partial<Visualization> = {
|
|
10
|
-
filters: [],
|
|
11
|
-
filterBehavior: 'Filter Change',
|
|
12
|
-
newViz: type !== 'table',
|
|
13
|
-
openModal: modalWillOpen,
|
|
14
|
-
uid: type + Date.now(),
|
|
15
|
-
type
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
switch (type) {
|
|
19
|
-
case 'chart':
|
|
20
|
-
newVisualizationConfig.visualizationType = subType
|
|
21
|
-
break
|
|
22
|
-
case 'map':
|
|
23
|
-
newVisualizationConfig.general = {}
|
|
24
|
-
newVisualizationConfig.general.geoType = subType
|
|
25
|
-
break
|
|
26
|
-
case 'data-bite' || 'waffle-chart' || 'filtered-text':
|
|
27
|
-
newVisualizationConfig.visualizationType = type
|
|
28
|
-
break
|
|
29
|
-
case 'table':
|
|
30
|
-
const tableConfig: Table = { label: 'Data Table', show: true, showDownloadUrl: false, showVertical: true, expanded: true, collapsible: true }
|
|
31
|
-
newVisualizationConfig.table = tableConfig
|
|
32
|
-
newVisualizationConfig.columns = {}
|
|
33
|
-
newVisualizationConfig.dataFormat = {}
|
|
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
|
-
|
|
56
|
-
break
|
|
57
|
-
default:
|
|
58
|
-
newVisualizationConfig.visualizationType = type
|
|
59
|
-
break
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return newVisualizationConfig
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const VisualizationsPanel = ({ loadConfig, config }) => (
|
|
66
|
-
<div className='visualizations-panel'>
|
|
67
|
-
<p style={{ fontSize: '14px' }}>Click and drag an item onto the grid to add it to your dashboard.</p>
|
|
68
|
-
<span className='subheading-3'>Chart</span>
|
|
69
|
-
<div className='drag-grid'>
|
|
70
|
-
<Widget addVisualization={() => addVisualization('chart', 'Bar')} type='Bar' />
|
|
71
|
-
<Widget addVisualization={() => addVisualization('chart', 'Line')} type='Line' />
|
|
72
|
-
<Widget addVisualization={() => addVisualization('chart', 'Pie')} type='Pie' />
|
|
73
|
-
<Widget addVisualization={() => addVisualization('chart', 'Sankey')} type='Sankey' />
|
|
74
|
-
</div>
|
|
75
|
-
<span className='subheading-3'>Map</span>
|
|
76
|
-
<div className='drag-grid'>
|
|
77
|
-
<Widget addVisualization={() => addVisualization('map', 'us')} type='us' />
|
|
78
|
-
<Widget addVisualization={() => addVisualization('map', 'world')} type='world' />
|
|
79
|
-
<Widget addVisualization={() => addVisualization('map', 'single-state')} type='single-state' />
|
|
80
|
-
</div>
|
|
81
|
-
<span className='subheading-3'>Misc.</span>
|
|
82
|
-
<div className='drag-grid'>
|
|
83
|
-
<Widget addVisualization={() => addVisualization('data-bite', '')} type='data-bite' />
|
|
84
|
-
<Widget addVisualization={() => addVisualization('waffle-chart', '')} type='waffle-chart' />
|
|
85
|
-
<Widget addVisualization={() => addVisualization('markup-include', '')} type='markup-include' />
|
|
86
|
-
<Widget addVisualization={() => addVisualization('filtered-text', '')} type='filtered-text' />
|
|
87
|
-
<Widget addVisualization={() => addVisualization('filter-dropdowns', '')} type='filter-dropdowns' />
|
|
88
|
-
<Widget addVisualization={() => addVisualization('table', '')} type='table' />
|
|
89
|
-
</div>
|
|
90
|
-
<span className='subheading-3'>Advanced</span>
|
|
91
|
-
<AdvancedEditor loadConfig={loadConfig} state={config} convertStateToConfig={() => undefined} />
|
|
92
|
-
</div>
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
export default VisualizationsPanel
|