@cdc/dashboard 4.24.4 → 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.
- package/dist/cdcdashboard.js +131937 -104935
- package/index.html +3 -3
- package/package.json +9 -9
- package/src/CdcDashboardComponent.tsx +73 -110
- package/src/_stories/Dashboard.stories.tsx +39 -15
- package/src/_stories/_mock/api-filter-chart.json +35 -11
- package/src/_stories/_mock/api-filter-map.json +31 -17
- package/src/_stories/_mock/dashboard-gallery.json +534 -523
- package/src/_stories/_mock/markup-include.json +78 -0
- package/src/_stories/_mock/multi-dashboards.json +914 -0
- package/src/_stories/_mock/pivot-filter.json +2 -0
- package/src/_stories/_mock/standalone-table.json +2 -0
- package/src/components/Header/FilterModal.tsx +14 -10
- package/src/components/MultiConfigTabs/MultiTabs.tsx +3 -2
- package/src/components/VisualizationRow.tsx +14 -5
- package/src/components/VisualizationsPanel.tsx +26 -3
- package/src/components/Widget.tsx +2 -2
- package/src/helpers/filterData.ts +15 -17
- package/src/store/dashboard.actions.ts +1 -10
- package/src/store/dashboard.reducer.ts +15 -13
- package/src/types/APIFilter.ts +5 -4
- package/src/components/EditorWrapper/EditorWrapper.tsx +0 -52
- package/src/components/EditorWrapper/editor-wrapper.style.css +0 -13
package/index.html
CHANGED
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
</head>
|
|
22
22
|
|
|
23
23
|
<body>
|
|
24
|
-
|
|
24
|
+
<div class="react-container" data-config="/examples/full-dashboard.json"></div>
|
|
25
25
|
<!-- <div class="react-container" data-config="/examples/dashboard-gallery.json"></div> -->
|
|
26
|
-
<div class="react-container" data-config="/examples/filtered-dash.json"></div>
|
|
26
|
+
<!-- <div class="react-container" data-config="/examples/filtered-dash.json"></div> -->
|
|
27
27
|
<!-- <div class="react-container" data-config="/examples/all-components.json"></div> -->
|
|
28
28
|
<!-- <div class="react-container" data-config="/examples/sankey.json"></div> -->
|
|
29
|
-
<div class="react-container" data-config="/examples/DEV-6574.json"></div>
|
|
29
|
+
<!-- <div class="react-container" data-config="/examples/DEV-6574.json"></div> -->
|
|
30
30
|
<!-- <div class="react-container" data-config="/examples/default-multi-dataset-2.json"></div> -->
|
|
31
31
|
<!-- <div class="react-container" data-config="/examples/default-multi-dataset.json"></div> -->
|
|
32
32
|
<!-- <div class="react-container" data-config="/examples/default-filter-control.json"></div> -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cdc/dashboard",
|
|
3
|
-
"version": "4.24.
|
|
3
|
+
"version": "4.24.5",
|
|
4
4
|
"description": "React component for combining multiple visualizations into a single dashboard",
|
|
5
5
|
"moduleName": "CdcDashboard",
|
|
6
6
|
"main": "dist/cdcdashboard",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
},
|
|
27
27
|
"license": "Apache-2.0",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@cdc/chart": "^4.24.
|
|
30
|
-
"@cdc/core": "^4.24.
|
|
31
|
-
"@cdc/data-bite": "^4.24.
|
|
32
|
-
"@cdc/filtered-text": "^4.24.
|
|
33
|
-
"@cdc/map": "^4.24.
|
|
34
|
-
"@cdc/markup-include": "^4.24.
|
|
35
|
-
"@cdc/waffle-chart": "^4.24.
|
|
29
|
+
"@cdc/chart": "^4.24.5",
|
|
30
|
+
"@cdc/core": "^4.24.5",
|
|
31
|
+
"@cdc/data-bite": "^4.24.5",
|
|
32
|
+
"@cdc/filtered-text": "^4.24.5",
|
|
33
|
+
"@cdc/map": "^4.24.5",
|
|
34
|
+
"@cdc/markup-include": "^4.24.5",
|
|
35
|
+
"@cdc/waffle-chart": "^4.24.5",
|
|
36
36
|
"html-react-parser": "^3.0.8",
|
|
37
37
|
"js-base64": "^2.5.2",
|
|
38
38
|
"papaparse": "^5.3.0",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"react": "^18.2.0",
|
|
49
49
|
"react-dom": "^18.2.0"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "def85aaf4cd9dc1983e80f2900199f35de82af95"
|
|
52
52
|
}
|
|
@@ -40,7 +40,6 @@ import { capitalizeSplitAndJoin } from '@cdc/core/helpers/cove/string'
|
|
|
40
40
|
import VisualizationsPanel from './components/VisualizationsPanel'
|
|
41
41
|
import dashboardReducer from './store/dashboard.reducer'
|
|
42
42
|
import { filterData } from './helpers/filterData'
|
|
43
|
-
import { getFormattedData } from './helpers/getFormattedData'
|
|
44
43
|
import { getVizKeys } from './helpers/getVizKeys'
|
|
45
44
|
import Title from '@cdc/core/components/ui/Title'
|
|
46
45
|
import { type TableConfig } from '@cdc/core/components/DataTable/types/TableConfig'
|
|
@@ -56,13 +55,11 @@ import _ from 'lodash'
|
|
|
56
55
|
import EditorContext from '../../editor/src/ConfigContext'
|
|
57
56
|
import { getApiFilterKey } from './helpers/getApiFilterKey'
|
|
58
57
|
import Filters, { APIFilterDropdowns, DropdownOptions } from './components/Filters'
|
|
59
|
-
import EditorWrapper from './components/EditorWrapper/EditorWrapper'
|
|
60
|
-
import DataTableEditorPanel from '@cdc/core/components/DataTable/components/DataTableEditorPanel'
|
|
61
58
|
import DataTableStandAlone from '@cdc/core/components/DataTable/DataTableStandAlone'
|
|
62
59
|
import { ViewPort } from '@cdc/core/types/ViewPort'
|
|
63
60
|
import VisualizationRow from './components/VisualizationRow'
|
|
64
61
|
import { getVizConfig } from './helpers/getVizConfig'
|
|
65
|
-
import {
|
|
62
|
+
import { getFilteredData } from './helpers/getFilteredData'
|
|
66
63
|
import { getVizRowColumnLocator } from './helpers/getVizRowColumnLocator'
|
|
67
64
|
import Layout from '@cdc/core/components/Layout'
|
|
68
65
|
|
|
@@ -103,7 +100,7 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
103
100
|
|
|
104
101
|
const transform = new DataTransform()
|
|
105
102
|
|
|
106
|
-
const setAutoLoadDefaultValue = (sharedFilterIndex: number, filterDropdowns: DropdownOptions
|
|
103
|
+
const setAutoLoadDefaultValue = (sharedFilterIndex: number, filterDropdowns: DropdownOptions) => {
|
|
107
104
|
const autoLoadViz = getAutoLoadVisualization()
|
|
108
105
|
if (!autoLoadViz) return // no autoLoading happening
|
|
109
106
|
const notIncludedInAutoLoad = autoLoadViz.hide
|
|
@@ -111,22 +108,20 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
111
108
|
// we don't want to auto load it
|
|
112
109
|
return
|
|
113
110
|
} else {
|
|
114
|
-
const
|
|
115
|
-
const sharedFilter = sharedFilters[sharedFilterIndex]
|
|
111
|
+
const sharedFilter = state.config.dashboard.sharedFilters[sharedFilterIndex]
|
|
116
112
|
if (sharedFilter.active) return // a value has already been selected.
|
|
117
|
-
const filterParents = sharedFilters.filter(f => sharedFilter.parents?.includes(f.key))
|
|
118
|
-
const notAllParentFiltersSelected = filterParents.some(p => !p.active
|
|
113
|
+
const filterParents = state.config.dashboard.sharedFilters.filter(f => sharedFilter.parents?.includes(f.key))
|
|
114
|
+
const notAllParentFiltersSelected = filterParents.some(p => !p.active)
|
|
119
115
|
if (filterParents && notAllParentFiltersSelected) return
|
|
120
116
|
const defaultFilterDropdown = filterDropdowns.find(({ value }) => value === sharedFilter.apiFilter!.defaultValue)
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
let defaultValue = defaultFilterDropdown?.value || filterDropdowns[0].value
|
|
118
|
+
changeFilterActive(sharedFilterIndex, defaultValue)
|
|
123
119
|
}
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
const loadAPIFilters = (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const sharedAPIFilters = sharedFilters.filter(f => f.apiFilter)
|
|
122
|
+
const loadAPIFilters = async () => {
|
|
123
|
+
if (state.config.dashboard.sharedFilters) {
|
|
124
|
+
const sharedAPIFilters = state.config.dashboard.sharedFilters.filter(f => f.apiFilter)
|
|
130
125
|
const loadingFilterMemo: APIFilterDropdowns = sharedAPIFilters.reduce((acc, curr) => {
|
|
131
126
|
const _key = getApiFilterKey(curr.apiFilter!)
|
|
132
127
|
if (apiFilterDropdowns[_key] != null) return acc // don't overwrite fetched data.
|
|
@@ -138,69 +133,58 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
138
133
|
const getParentParams = (childFilter: SharedFilter): Record<'key' | 'value', string>[] | null => {
|
|
139
134
|
const _parents = sharedAPIFilters.filter(parentFilter => childFilter.parents?.includes(parentFilter.key))
|
|
140
135
|
if (!_parents.length) return null
|
|
141
|
-
return _parents.map(({ queryParameter,
|
|
136
|
+
return _parents.map(({ queryParameter, queuedActive }) => ({ key: queryParameter || '', value: queuedActive || '' }))
|
|
142
137
|
}
|
|
143
|
-
const getFilterValues = (data: Array<Object>, apiFilter: APIFilter): DropdownOptions => {
|
|
144
|
-
const { textSelector, valueSelector } = apiFilter
|
|
138
|
+
const getFilterValues = (data: Object | Array<Object>, apiFilter: APIFilter): DropdownOptions => {
|
|
139
|
+
const { textSelector, valueSelector, heirarchyLookup } = apiFilter
|
|
140
|
+
if (heirarchyLookup) {
|
|
141
|
+
const heirarchy = heirarchyLookup!.split('.')
|
|
142
|
+
const selector = heirarchy.shift() // pop first element
|
|
143
|
+
return getFilterValues(selector ? data[selector] : data, { ...apiFilter, heirarchyLookup: heirarchy.join('.') })
|
|
144
|
+
}
|
|
145
|
+
if (!Array.isArray(data)) throw new Error('the filter data has requires a heirarchy path to access the filter values, This should be in the format key.subkey.subsubkey')
|
|
145
146
|
return data.map(v => ({ text: v[textSelector], value: v[valueSelector] }))
|
|
146
147
|
}
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
state.config.dashboard.sharedFilters.forEach(async (filter, index) => {
|
|
149
|
+
if (!filter.apiFilter) return
|
|
149
150
|
const baseEndpoint = filter.apiFilter.apiEndpoint
|
|
150
151
|
const _key = getApiFilterKey(filter.apiFilter)
|
|
151
152
|
const params = getParentParams(filter)
|
|
152
153
|
const notAllParentsSelected = params?.some(({ value }) => value === '')
|
|
153
|
-
|
|
154
154
|
if (notAllParentsSelected) return // don't send request for dependent children filter options
|
|
155
|
-
if (apiFilterDropdowns[_key] && !params) return // don't reload filter unless it's a child
|
|
156
|
-
const topLevelDataAlreadyLoaded = apiFilterDropdowns[_key] && !filter.parents
|
|
157
|
-
if (topLevelDataAlreadyLoaded) return // don't reload top level filters
|
|
158
|
-
|
|
155
|
+
if (apiFilterDropdowns[_key] && !params && filter.filterBy === 'Query String') return // don't reload filter unless it's a child
|
|
159
156
|
const endpoint = baseEndpoint + (params ? gatherQueryParams(params) : '')
|
|
160
|
-
|
|
157
|
+
|
|
158
|
+
fetch(endpoint)
|
|
159
|
+
.then(resp => resp.json())
|
|
160
|
+
.then(data => {
|
|
161
|
+
const apiFilter = filterLookup.get(_key) as APIFilter
|
|
162
|
+
const _filterValues = getFilterValues(data, apiFilter)
|
|
163
|
+
setAPIFilterDropdowns(dropdowns => ({ ...dropdowns, [_key]: _filterValues }))
|
|
164
|
+
setAutoLoadDefaultValue(index, _filterValues)
|
|
165
|
+
})
|
|
161
166
|
})
|
|
162
|
-
return Promise.all(
|
|
163
|
-
Object.keys(toFetch).map(
|
|
164
|
-
endpoint =>
|
|
165
|
-
new Promise<void>(resolve => {
|
|
166
|
-
fetch(endpoint)
|
|
167
|
-
.then(resp => resp.json())
|
|
168
|
-
.then(data => {
|
|
169
|
-
const [_key, index] = toFetch[endpoint]
|
|
170
|
-
if (!Array.isArray(data)) throw new Error('COVE only supports response data in the shape Array<Object>')
|
|
171
|
-
const apiFilter = filterLookup.get(_key) as APIFilter
|
|
172
|
-
const _filterValues = getFilterValues(data, apiFilter)
|
|
173
|
-
setAPIFilterDropdowns(dropdowns => ({ ...dropdowns, [_key]: _filterValues }))
|
|
174
|
-
setAutoLoadDefaultValue(index, _filterValues, dashboardConfigOverride)
|
|
175
|
-
})
|
|
176
|
-
.catch(console.error)
|
|
177
|
-
.finally(() => {
|
|
178
|
-
resolve()
|
|
179
|
-
})
|
|
180
|
-
})
|
|
181
|
-
)
|
|
182
|
-
)
|
|
183
167
|
}
|
|
184
168
|
}
|
|
185
169
|
|
|
186
|
-
const reloadURLData = async (
|
|
187
|
-
const config =
|
|
170
|
+
const reloadURLData = async () => {
|
|
171
|
+
const { config } = state
|
|
188
172
|
if (!config.datasets) return
|
|
189
173
|
let newData = { ...state.data }
|
|
190
174
|
let newDatasets = { ...config.datasets }
|
|
191
175
|
let datasetsNeedsUpdate = false
|
|
192
176
|
let datasetKeys = Object.keys(config.datasets)
|
|
193
177
|
let newFileName = ''
|
|
194
|
-
|
|
178
|
+
|
|
195
179
|
for (let i = 0; i < datasetKeys.length; i++) {
|
|
196
180
|
const datasetKey = datasetKeys[i]
|
|
197
181
|
const dataset = config.datasets[datasetKey]
|
|
198
|
-
|
|
182
|
+
const filters = config.dashboard?.sharedFilters
|
|
199
183
|
if (dataset.dataUrl && filters) {
|
|
200
184
|
const dataUrl = new URL(dataset.runtimeDataUrl || dataset.dataUrl, window.location.origin)
|
|
201
185
|
let currentQSParams = Object.fromEntries(new URLSearchParams(dataUrl.search))
|
|
202
186
|
let updatedQSParams = {}
|
|
203
|
-
let isUpdateNeeded =
|
|
187
|
+
let isUpdateNeeded = false
|
|
204
188
|
|
|
205
189
|
filters.forEach(filter => {
|
|
206
190
|
// filter.active is always a string when filter.type is 'urlfilter'
|
|
@@ -272,10 +256,10 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
272
256
|
}
|
|
273
257
|
|
|
274
258
|
if (datasetsNeedsUpdate) {
|
|
275
|
-
const dashboardConfig = dashboardConfigOverride || config.dashboard
|
|
276
259
|
dispatch({ type: 'SET_DATA', payload: newData })
|
|
277
260
|
|
|
278
261
|
const newFilteredData = getFilteredData(state, {}, newData)
|
|
262
|
+
|
|
279
263
|
const visualizations = Object.keys(config.visualizations).reduce((acc, vizKey) => {
|
|
280
264
|
const dataKey = config.visualizations[vizKey].dataKey
|
|
281
265
|
if (newData[dataKey]) {
|
|
@@ -285,7 +269,7 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
285
269
|
}, _.cloneDeep(config.visualizations))
|
|
286
270
|
|
|
287
271
|
dispatch({ type: 'SET_FILTERED_DATA', payload: newFilteredData })
|
|
288
|
-
dispatch({ type: 'SET_CONFIG', payload: {
|
|
272
|
+
dispatch({ type: 'SET_CONFIG', payload: { ...config, datasets: newDatasets, visualizations } })
|
|
289
273
|
}
|
|
290
274
|
}
|
|
291
275
|
|
|
@@ -300,7 +284,8 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
300
284
|
}
|
|
301
285
|
|
|
302
286
|
const setSharedFilter = (key, datum) => {
|
|
303
|
-
const { config
|
|
287
|
+
const { config } = state
|
|
288
|
+
let newConfig = { ...config }
|
|
304
289
|
|
|
305
290
|
for (let i = 0; i < newConfig.dashboard.sharedFilters.length; i++) {
|
|
306
291
|
const filter = newConfig.dashboard.sharedFilters[i]
|
|
@@ -312,13 +297,14 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
312
297
|
}
|
|
313
298
|
}
|
|
314
299
|
|
|
315
|
-
const newFilteredData = getFilteredData(
|
|
300
|
+
const newFilteredData = getFilteredData(state, _.cloneDeep(state.filteredData))
|
|
316
301
|
|
|
317
302
|
dispatch({ type: 'SET_FILTERED_DATA', payload: newFilteredData })
|
|
318
|
-
dispatch({ type: '
|
|
303
|
+
dispatch({ type: 'SET_CONFIG', payload: newConfig })
|
|
319
304
|
}
|
|
320
305
|
|
|
321
306
|
useEffect(() => {
|
|
307
|
+
if (state.tabSelected && state.tabSelected !== 'Dashboard Preview') return
|
|
322
308
|
const { config } = state
|
|
323
309
|
if (config.filterBehavior !== FilterBehavior.Apply) {
|
|
324
310
|
reloadURLData()
|
|
@@ -327,16 +313,16 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
327
313
|
}, [])
|
|
328
314
|
|
|
329
315
|
const updateChildConfig = (visualizationKey, newConfig) => {
|
|
330
|
-
const config =
|
|
331
|
-
|
|
316
|
+
const { config } = state
|
|
317
|
+
let updatedConfig = { ...config }
|
|
332
318
|
updatedConfig.visualizations[visualizationKey] = newConfig
|
|
333
319
|
updatedConfig.visualizations[visualizationKey].formattedData = config.visualizations[visualizationKey].formattedData
|
|
334
320
|
if (config.multiDashboards) {
|
|
335
321
|
const activeDashboard = config.activeDashboard
|
|
336
322
|
const multiDashboards = [...config.multiDashboards]
|
|
337
323
|
const label = multiDashboards[activeDashboard].label
|
|
338
|
-
const toSave =
|
|
339
|
-
multiDashboards[activeDashboard] = toSave
|
|
324
|
+
const toSave = _.pick(updatedConfig, ['dashboard', 'visualizations', 'rows'])
|
|
325
|
+
multiDashboards[activeDashboard] = { ...toSave, label }
|
|
340
326
|
updatedConfig.multiDashboards = multiDashboards
|
|
341
327
|
}
|
|
342
328
|
|
|
@@ -348,22 +334,13 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
348
334
|
}
|
|
349
335
|
|
|
350
336
|
const applyFilters = () => {
|
|
351
|
-
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
const allRequiredFiltersSelected = !dashboardConfig.sharedFilters.some((filter, filterIndex) => {
|
|
355
|
-
if (nonAutoLoadFilterIndexes.includes(filterIndex)) {
|
|
356
|
-
!filter.active && !filter.queuedActive
|
|
357
|
-
} else {
|
|
358
|
-
// autoload filters don't need to be selected to apply filters
|
|
359
|
-
return false
|
|
360
|
-
}
|
|
361
|
-
})
|
|
362
|
-
if (allRequiredFiltersSelected) {
|
|
337
|
+
let dashboardConfig = state.config.dashboard
|
|
338
|
+
const allFiltersSelected = !state.config.dashboard.sharedFilters.some(filter => !filter.active && !filter.queuedActive)
|
|
339
|
+
if (allFiltersSelected) {
|
|
363
340
|
if (state.config.filterBehavior === FilterBehavior.Apply) {
|
|
364
341
|
const queryParams = getQueryParams()
|
|
365
342
|
let needsQueryUpdate = false
|
|
366
|
-
|
|
343
|
+
state.config.dashboard.sharedFilters.forEach((sharedFilter, index) => {
|
|
367
344
|
if (sharedFilter.queuedActive) {
|
|
368
345
|
dashboardConfig.sharedFilters[index].active = sharedFilter.queuedActive
|
|
369
346
|
delete dashboardConfig.sharedFilters[index].queuedActive
|
|
@@ -380,28 +357,21 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
380
357
|
}
|
|
381
358
|
}
|
|
382
359
|
|
|
383
|
-
dispatch({ type: '
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
.then(() => {
|
|
387
|
-
reloadURLData(dashboardConfig)
|
|
388
|
-
})
|
|
389
|
-
.catch(e => {
|
|
390
|
-
console.error(e)
|
|
391
|
-
})
|
|
360
|
+
dispatch({ type: 'SET_CONFIG', payload: { ...state.config, dashboard: dashboardConfig } })
|
|
361
|
+
dispatch({ type: 'SET_FILTERED_DATA', payload: getFilteredData(state) })
|
|
362
|
+
reloadURLData()
|
|
392
363
|
} else {
|
|
393
364
|
// TODO noftify of required fields
|
|
394
365
|
}
|
|
395
366
|
}
|
|
396
367
|
|
|
397
368
|
const changeFilterActive = (index: number, value: string | string[]) => {
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const isAutoLoad = nonAutoLoadFilterIndexes && !nonAutoLoadFilterIndexes.includes(index)
|
|
369
|
+
const { config } = state
|
|
370
|
+
let dashboardConfig = { ...config.dashboard }
|
|
371
|
+
let filterActive = dashboardConfig.sharedFilters[index]
|
|
402
372
|
|
|
403
|
-
if (
|
|
404
|
-
sharedFilters[index].active = value
|
|
373
|
+
if (config.filterBehavior !== FilterBehavior.Apply) {
|
|
374
|
+
dashboardConfig.sharedFilters[index].active = value
|
|
405
375
|
|
|
406
376
|
const queryParams = getQueryParams()
|
|
407
377
|
if (filterActive.setByQueryParameter && queryParams[filterActive.setByQueryParameter] !== filterActive.active) {
|
|
@@ -410,35 +380,28 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
410
380
|
}
|
|
411
381
|
} else {
|
|
412
382
|
if (Array.isArray(value)) throw Error(`Cannot set active values on urlfilters. expected: ${JSON.stringify(value)} to be a single value.`)
|
|
413
|
-
sharedFilters[index].queuedActive = value
|
|
383
|
+
dashboardConfig.sharedFilters[index].queuedActive = value
|
|
414
384
|
}
|
|
415
385
|
|
|
416
|
-
dispatch({ type: '
|
|
417
|
-
if (
|
|
418
|
-
|
|
386
|
+
dispatch({ type: 'SET_CONFIG', payload: { ...config, dashboard: dashboardConfig } })
|
|
387
|
+
if (config.filterBehavior !== FilterBehavior.Apply) {
|
|
388
|
+
dispatch({ type: 'SET_FILTERED_DATA', payload: getFilteredData(state) })
|
|
419
389
|
reloadURLData()
|
|
420
390
|
}
|
|
421
|
-
return sharedFilters
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
const updateDataFilters = (sharedFilters = undefined) => {
|
|
425
|
-
const clonedState = _.cloneDeep(state)
|
|
426
|
-
if (sharedFilters) clonedState.config.dashboard.sharedFilters = sharedFilters
|
|
427
|
-
const newFilteredData = getFilteredData(clonedState)
|
|
428
|
-
dispatch({ type: 'SET_FILTERED_DATA', payload: newFilteredData })
|
|
429
391
|
}
|
|
430
392
|
|
|
431
393
|
const handleOnChange = (index: number, value: string | string[]) => {
|
|
432
|
-
const config =
|
|
433
|
-
|
|
394
|
+
const { config } = state
|
|
395
|
+
changeFilterActive(index, value)
|
|
434
396
|
if (config.filterBehavior === FilterBehavior.Apply) {
|
|
435
397
|
const autoLoadViz = getAutoLoadVisualization()
|
|
436
|
-
|
|
398
|
+
if (!autoLoadViz) return // nothing left to do for regular filter behavior.
|
|
399
|
+
const isAutoSelectFilter = !autoLoadViz.hide.includes(index)
|
|
437
400
|
const missingFilterSelections = config.dashboard.sharedFilters.some(f => !f.active)
|
|
438
401
|
if (isAutoSelectFilter && !missingFilterSelections) {
|
|
439
402
|
// a dropdown has been selected that doesn't
|
|
440
403
|
// require the Go Button
|
|
441
|
-
reloadURLData(
|
|
404
|
+
reloadURLData()
|
|
442
405
|
} else {
|
|
443
406
|
// A parent filter was selected, reset filters by:
|
|
444
407
|
// set auto select filter dropdowns to null
|
|
@@ -460,12 +423,11 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
460
423
|
if (_isAutoSelectFilter) filter.active = ''
|
|
461
424
|
return filter
|
|
462
425
|
})
|
|
463
|
-
const _newConfig = { dashboard: { ...config.dashboard, sharedFilters: newSharedFilters } }
|
|
426
|
+
const _newConfig = { ...config, dashboard: { ...config.dashboard, sharedFilters: newSharedFilters } }
|
|
464
427
|
dispatch({ type: 'SET_CONFIG', payload: _newConfig })
|
|
465
428
|
// setData to empty object because we no longer have a data state.
|
|
466
429
|
dispatch({ type: 'SET_DATA', payload: {} })
|
|
467
430
|
dispatch({ type: 'SET_FILTERED_DATA', payload: {} })
|
|
468
|
-
loadAPIFilters(_newConfig.dashboard)
|
|
469
431
|
}
|
|
470
432
|
}
|
|
471
433
|
}
|
|
@@ -604,9 +566,10 @@ export default function CdcDashboard({ initialState, isEditor = false, isDebug =
|
|
|
604
566
|
break
|
|
605
567
|
case 'table':
|
|
606
568
|
body = (
|
|
607
|
-
|
|
608
|
-
<
|
|
609
|
-
|
|
569
|
+
<>
|
|
570
|
+
<Header visualizationKey={visualizationKey} subEditor='Table' />
|
|
571
|
+
<DataTableStandAlone visualizationKey={visualizationKey} config={visualizationConfig} isEditor={true} updateConfig={_updateConfig} />
|
|
572
|
+
</>
|
|
610
573
|
)
|
|
611
574
|
break
|
|
612
575
|
default:
|
|
@@ -7,6 +7,7 @@ import ExampleConfig_2 from './_mock/dashboard-2.json'
|
|
|
7
7
|
import ExampleConfig_3 from './_mock/dashboard_no_filter.json'
|
|
8
8
|
import Dashboard_Filter from './_mock/dashboard-filter.json'
|
|
9
9
|
import MultiVizConfig from './_mock/multi-viz.json'
|
|
10
|
+
import MultiDashboardConfig from './_mock/multi-dashboards.json'
|
|
10
11
|
import Dashboard from '../CdcDashboard'
|
|
11
12
|
import StandaloneTable from './_mock/standalone-table.json'
|
|
12
13
|
import PivotFitlerConfig from './_mock/pivot-filter.json'
|
|
@@ -99,6 +100,13 @@ export const MultiVisualization: Story = {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
export const MultiDashboard: Story = {
|
|
104
|
+
args: {
|
|
105
|
+
config: MultiDashboardConfig,
|
|
106
|
+
isEditor: false
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
102
110
|
const sleep = ms => {
|
|
103
111
|
return new Promise(r => setTimeout(r, ms))
|
|
104
112
|
}
|
|
@@ -125,18 +133,35 @@ const fetchMock = {
|
|
|
125
133
|
body: [{ IndicatorID: 'indicatorID', Indicator: 'Some Indicator' }]
|
|
126
134
|
}
|
|
127
135
|
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
{
|
|
137
|
+
matcher: {
|
|
138
|
+
name: 'filters',
|
|
139
|
+
url: 'path:/api/POC/Filters'
|
|
140
|
+
},
|
|
141
|
+
response: {
|
|
142
|
+
status: 200,
|
|
143
|
+
body: {
|
|
144
|
+
Years: [{ Year: 1999 }],
|
|
145
|
+
DataValueTypes: [{ DataValueType: 'Some Data Value Type', DataValueTypeId: 'dataValueTypeId' }],
|
|
146
|
+
StratificationCategories: [{ StratificationCategoryId: 'stratCategoryId', StratificationCategory: 'Some Strat Category' }]
|
|
137
147
|
}
|
|
138
148
|
}
|
|
139
|
-
}
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
matcher: {
|
|
152
|
+
name: 'stratifications',
|
|
153
|
+
url: 'path:/api/POC/stratifications'
|
|
154
|
+
},
|
|
155
|
+
response: {
|
|
156
|
+
status: 200,
|
|
157
|
+
body: [
|
|
158
|
+
{
|
|
159
|
+
StratificationId: 'stratId',
|
|
160
|
+
Stratification: 'Some Strat'
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
},
|
|
140
165
|
{
|
|
141
166
|
matcher: {
|
|
142
167
|
name: 'locations',
|
|
@@ -205,11 +230,11 @@ export const APIFiltersMap: Story = {
|
|
|
205
230
|
const indicatorsFilter = canvas.getByLabelText('Indicator', { selector: 'select' })
|
|
206
231
|
await user.selectOptions(indicatorsFilter, ['indicatorID'])
|
|
207
232
|
const yearsFilter = canvas.getByLabelText('Year', { selector: 'select' })
|
|
208
|
-
await user.selectOptions(yearsFilter, ['
|
|
233
|
+
await user.selectOptions(yearsFilter, ['1999'])
|
|
209
234
|
const stratCategoryFilter = canvas.getByLabelText('View By', { selector: 'select' })
|
|
210
|
-
await user.selectOptions(stratCategoryFilter, ['
|
|
235
|
+
await user.selectOptions(stratCategoryFilter, ['stratCategoryId'])
|
|
211
236
|
const stratFilter = canvas.getByLabelText('Stratification', { selector: 'select' })
|
|
212
|
-
await user.selectOptions(stratFilter, ['
|
|
237
|
+
await user.selectOptions(stratFilter, ['stratId'])
|
|
213
238
|
await user.click(canvas.getByText('GO!'))
|
|
214
239
|
}
|
|
215
240
|
}
|
|
@@ -235,9 +260,8 @@ export const APIFiltersChart: Story = {
|
|
|
235
260
|
const indicatorsFilter = canvas.getByLabelText('Indicator', { selector: 'select' })
|
|
236
261
|
await user.selectOptions(indicatorsFilter, ['indicatorID'])
|
|
237
262
|
await user.click(canvas.getByText('GO!'))
|
|
238
|
-
await sleep(1000)
|
|
239
263
|
const yearFilter = canvas.getByLabelText('Year', { selector: 'select' })
|
|
240
|
-
await user.selectOptions(yearFilter, ['
|
|
264
|
+
await user.selectOptions(yearFilter, ['1999'])
|
|
241
265
|
}
|
|
242
266
|
}
|
|
243
267
|
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
"sharedFilters": [
|
|
5
5
|
{
|
|
6
6
|
"key": "Location",
|
|
7
|
+
"usedBy": [
|
|
8
|
+
"chart1"
|
|
9
|
+
],
|
|
7
10
|
"type": "urlfilter",
|
|
8
11
|
"apiFilter": {
|
|
9
12
|
"apiEndpoint": "http://test.gov/api/poc/locations",
|
|
@@ -16,6 +19,9 @@
|
|
|
16
19
|
},
|
|
17
20
|
{
|
|
18
21
|
"key": "Category",
|
|
22
|
+
"usedBy": [
|
|
23
|
+
"chart1"
|
|
24
|
+
],
|
|
19
25
|
"type": "urlfilter",
|
|
20
26
|
"apiFilter": {
|
|
21
27
|
"apiEndpoint": "http://test.gov/api/poc/topics",
|
|
@@ -28,6 +34,9 @@
|
|
|
28
34
|
},
|
|
29
35
|
{
|
|
30
36
|
"key": "Indicator",
|
|
37
|
+
"usedBy": [
|
|
38
|
+
"chart1"
|
|
39
|
+
],
|
|
31
40
|
"type": "urlfilter",
|
|
32
41
|
"apiFilter": {
|
|
33
42
|
"apiEndpoint": "http://test.gov/api/poc/indicators",
|
|
@@ -41,11 +50,15 @@
|
|
|
41
50
|
},
|
|
42
51
|
{
|
|
43
52
|
"key": "Year",
|
|
53
|
+
"usedBy": [
|
|
54
|
+
"chart1"
|
|
55
|
+
],
|
|
44
56
|
"type": "urlfilter",
|
|
45
57
|
"apiFilter": {
|
|
46
|
-
"apiEndpoint": "http://test.gov/api/POC/Filters
|
|
58
|
+
"apiEndpoint": "http://test.gov/api/POC/Filters",
|
|
47
59
|
"valueSelector": "Year",
|
|
48
|
-
"textSelector": "Year"
|
|
60
|
+
"textSelector": "Year",
|
|
61
|
+
"heirarchyLookup": "Years"
|
|
49
62
|
},
|
|
50
63
|
"queryParameter": "year",
|
|
51
64
|
"showDropdown": true,
|
|
@@ -53,11 +66,15 @@
|
|
|
53
66
|
},
|
|
54
67
|
{
|
|
55
68
|
"key": "View By",
|
|
69
|
+
"usedBy": [
|
|
70
|
+
"chart1"
|
|
71
|
+
],
|
|
56
72
|
"type": "urlfilter",
|
|
57
73
|
"apiFilter": {
|
|
58
|
-
"apiEndpoint": "http://test.gov/api/POC/Filters
|
|
59
|
-
"valueSelector": "
|
|
74
|
+
"apiEndpoint": "http://test.gov/api/POC/Filters",
|
|
75
|
+
"valueSelector": "StratificationCategoryId",
|
|
60
76
|
"textSelector": "StratificationCategory",
|
|
77
|
+
"heirarchyLookup": "StratificationCategories",
|
|
61
78
|
"defaultValue": "AGE"
|
|
62
79
|
},
|
|
63
80
|
"queryParameter": "stratificationcategoryid",
|
|
@@ -66,11 +83,16 @@
|
|
|
66
83
|
},
|
|
67
84
|
{
|
|
68
85
|
"key": "Data Type",
|
|
86
|
+
"usedBy": [
|
|
87
|
+
"chart1"
|
|
88
|
+
],
|
|
69
89
|
"type": "urlfilter",
|
|
70
90
|
"apiFilter": {
|
|
71
|
-
"apiEndpoint": "http://test.gov/api/POC/Filters
|
|
72
|
-
"valueSelector": "
|
|
73
|
-
"textSelector": "DataValueType"
|
|
91
|
+
"apiEndpoint": "http://test.gov/api/POC/Filters",
|
|
92
|
+
"valueSelector": "DataValueTypeId",
|
|
93
|
+
"textSelector": "DataValueType",
|
|
94
|
+
"heirarchyLookup": "DataValueTypes"
|
|
95
|
+
|
|
74
96
|
},
|
|
75
97
|
"queryParameter": "datavaluetypeid",
|
|
76
98
|
"showDropdown": true,
|
|
@@ -108,7 +130,7 @@
|
|
|
108
130
|
"filters": {
|
|
109
131
|
"type": "filter-dropdowns",
|
|
110
132
|
"visualizationType": "filter-dropdowns",
|
|
111
|
-
"hide": [3, 4, 5]
|
|
133
|
+
"hide": [ 3, 4, 5 ]
|
|
112
134
|
},
|
|
113
135
|
"header": {
|
|
114
136
|
"type": "markup-include",
|
|
@@ -119,7 +141,7 @@
|
|
|
119
141
|
"subfilters": {
|
|
120
142
|
"type": "filter-dropdowns",
|
|
121
143
|
"visualizationType": "filter-dropdowns",
|
|
122
|
-
"hide": [0, 1, 2],
|
|
144
|
+
"hide": [ 0, 1, 2 ],
|
|
123
145
|
"autoLoad": true
|
|
124
146
|
},
|
|
125
147
|
"chart1": {
|
|
@@ -319,7 +341,9 @@
|
|
|
319
341
|
},
|
|
320
342
|
"validated": 4.23,
|
|
321
343
|
"dynamicMarginTop": 0,
|
|
322
|
-
"regions": [
|
|
344
|
+
"regions": [
|
|
345
|
+
{}
|
|
346
|
+
]
|
|
323
347
|
}
|
|
324
348
|
},
|
|
325
349
|
"type": "dashboard",
|
|
@@ -331,4 +355,4 @@
|
|
|
331
355
|
"filterBehavior": "Apply Button",
|
|
332
356
|
"runtime": {},
|
|
333
357
|
"uuid": 1684783370106
|
|
334
|
-
}
|
|
358
|
+
}
|