@cdc/dashboard 4.25.11 → 4.26.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.
- package/Dynamic_Data.md +66 -0
- package/dist/cdcdashboard-8NmHlKRI.es.js +15 -0
- package/dist/cdcdashboard-BPoPzKPz.es.js +6 -0
- package/dist/cdcdashboard-Cf9_fbQf.es.js +6 -0
- package/dist/{cdcdashboard-dgT_1dIT.es.js → cdcdashboard-DQ00cQCm.es.js} +1 -20
- package/dist/cdcdashboard-jiQQPkty.es.js +6 -0
- package/dist/cdcdashboard.js +83537 -86913
- package/examples/api-dashboard-data.json +272 -0
- package/examples/api-dashboard-years.json +11 -0
- package/examples/api-geographies-data.json +11 -0
- package/examples/default.json +522 -133
- package/examples/nested-dropdown.json +6985 -0
- package/examples/private/abc.json +467 -0
- package/examples/private/cat-y.json +1235 -0
- package/examples/private/chronic-dash.json +1584 -0
- package/examples/private/dash.json +12696 -0
- package/examples/private/map-issue.json +2260 -0
- package/examples/private/mpinc-state-reports.json +2260 -0
- package/examples/private/npcr.json +1 -0
- package/examples/private/nwss/rsv.json +1240 -0
- package/examples/private/simple-dash.json +490 -0
- package/examples/private/test-dash.json +0 -0
- package/examples/private/test.json +125407 -0
- package/examples/private/test123.json +491 -0
- package/examples/private/timeline-data.json +4994 -0
- package/examples/private/timeline.json +1708 -0
- package/examples/test-api-filter-reset.json +8 -4
- package/examples/test-dashboard-simple.json +503 -0
- package/examples/tp5-gauges.json +196 -0
- package/examples/tp5-test.json +266 -0
- package/index.html +1 -30
- package/package.json +39 -40
- package/src/CdcDashboardComponent.tsx +18 -5
- package/src/_stories/Dashboard.DataSetup.stories.tsx +204 -0
- package/src/_stories/Dashboard.stories.tsx +407 -1
- package/src/_stories/_mock/dashboard-line-chart-angles.json +1030 -0
- package/src/_stories/_mock/filter-cascade.json +3350 -0
- package/src/_stories/_mock/gallery-data-bite-dashboard.json +3500 -0
- package/src/_stories/_mock/nested-parent-child-filters.json +392 -0
- package/src/_stories/_mock/parent-child-filters.json +233 -0
- package/src/_stories/_mock/tp5-test.json +267 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +20 -11
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +92 -38
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +56 -30
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +151 -10
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +11 -7
- package/src/components/DataDesignerModal.tsx +6 -1
- package/src/components/Header/Header.tsx +51 -20
- package/src/components/VisualizationRow.tsx +76 -5
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +2 -20
- package/src/components/Widget/Widget.tsx +1 -1
- package/src/data/initial-state.js +1 -0
- package/src/helpers/addValuesToDashboardFilters.ts +30 -31
- package/src/helpers/apiFilterHelpers.ts +28 -32
- package/src/helpers/changeFilterActive.ts +67 -65
- package/src/helpers/formatConfigBeforeSave.ts +6 -5
- package/src/helpers/getUpdateConfig.ts +91 -91
- package/src/helpers/tests/addValuesToDashboardFilters.test.ts +141 -44
- package/src/helpers/tests/apiFilterHelpers.test.ts +523 -420
- package/src/helpers/tests/updatesChildFilters.test.ts +53 -22
- package/src/helpers/updateChildFilters.ts +50 -27
- package/src/scss/main.scss +144 -1
- package/src/test/CdcDashboard.test.jsx +9 -4
- package/src/types/Dashboard.ts +1 -0
- package/src/types/FilterStyles.ts +8 -7
- package/src/types/SharedFilter.ts +13 -0
- package/vite.config.js +7 -1
- package/LICENSE +0 -201
- package/dist/cdcdashboard-BnB1QM5d.es.js +0 -361528
- package/dist/cdcdashboard-Ct2SB0vL.es.js +0 -231049
- package/dist/cdcdashboard-D6CG2-Hb.es.js +0 -39377
- package/dist/cdcdashboard-MXgURbdZ.es.js +0 -39194
- package/examples/private/DEV-10538.json +0 -407
- package/examples/private/DEV-11072.json +0 -7591
- package/examples/private/DEV-11405.json +0 -39112
- package/examples/private/delete.json +0 -32919
- package/examples/private/pedro.json +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _ from 'lodash'
|
|
2
|
-
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
2
|
+
import { getQueryStringFilterValue, isFilterHiddenByQuery } from '@cdc/core/helpers/queryStringUtils'
|
|
3
3
|
import { SharedFilter } from '../types/SharedFilter'
|
|
4
4
|
import { handleSorting } from '@cdc/core/components/Filters'
|
|
5
5
|
import { mergeCustomOrderValues } from '@cdc/core/helpers/mergeCustomOrderValues'
|
|
@@ -39,7 +39,11 @@ export const addValuesToDashboardFilters = (
|
|
|
39
39
|
if (filtersToSkip.includes(index)) return filter
|
|
40
40
|
if (filter.type === 'urlfilter') return filter
|
|
41
41
|
const filterCopy = _.cloneDeep(filter)
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
// Only generate values from data if not pre-configured
|
|
44
|
+
const hasPreConfiguredValues = filter.values && filter.values.length > 0
|
|
45
|
+
const filterValues = hasPreConfiguredValues ? filter.values : generateValuesForFilter(getSelector(filter), data)
|
|
46
|
+
|
|
43
47
|
filterCopy.values = filterValues
|
|
44
48
|
|
|
45
49
|
// Merge new values with existing custom order (fixes DEV-11740 & DEV-11376)
|
|
@@ -54,32 +58,20 @@ export const addValuesToDashboardFilters = (
|
|
|
54
58
|
const active: string[] = Array.isArray(filterCopy.active) ? filterCopy.active : [filterCopy.active]
|
|
55
59
|
filterCopy.active = active.filter(val => defaultValues.includes(val))
|
|
56
60
|
} else {
|
|
57
|
-
//
|
|
58
|
-
const currentActive = filterCopy.active as string
|
|
59
|
-
const isResetLabelValue = currentActive && currentActive === filterCopy.resetLabel
|
|
60
|
-
const isCurrentActiveValid = currentActive && (filterValues.includes(currentActive) || isResetLabelValue)
|
|
61
|
-
|
|
62
|
-
// Check if this is an intentional clear (empty string, but not undefined during initial load)
|
|
63
|
-
const isIntentionalClear = currentActive === ''
|
|
64
|
-
|
|
65
|
-
// Priority: defaultValue > valid current active > reset label > first value
|
|
61
|
+
// Use defaultValue if set, otherwise keep existing active or use first value
|
|
66
62
|
if (filterCopy.defaultValue) {
|
|
67
|
-
// If defaultValue is explicitly set, always use it
|
|
68
63
|
filterCopy.active = filterCopy.defaultValue
|
|
69
|
-
} else if (
|
|
70
|
-
|
|
71
|
-
filterCopy.active = currentActive
|
|
72
|
-
} else if (isIntentionalClear) {
|
|
73
|
-
// Don't override intentional clears
|
|
74
|
-
filterCopy.active = currentActive
|
|
75
|
-
} else {
|
|
76
|
-
// Set to reset label or first value
|
|
77
|
-
const defaultValue = filterCopy.resetLabel || filterCopy.values[0]
|
|
78
|
-
filterCopy.active = defaultValue
|
|
64
|
+
} else if (!filterCopy.active) {
|
|
65
|
+
filterCopy.active = filterCopy.resetLabel || filterCopy.values[0]
|
|
79
66
|
}
|
|
80
67
|
}
|
|
81
68
|
}
|
|
82
69
|
|
|
70
|
+
// Check if filter should be hidden by query parameter
|
|
71
|
+
if (isFilterHiddenByQuery(filterCopy)) {
|
|
72
|
+
filterCopy.showDropdown = false
|
|
73
|
+
}
|
|
74
|
+
|
|
83
75
|
// Handle nested dropdown subGrouping.active property
|
|
84
76
|
if (filterCopy.subGrouping && filterCopy.subGrouping.valuesLookup) {
|
|
85
77
|
const groupName = filterCopy.active as string
|
|
@@ -89,17 +81,24 @@ export const addValuesToDashboardFilters = (
|
|
|
89
81
|
}
|
|
90
82
|
const queryStringFilterValue = getQueryStringFilterValue(subGroupingFilter)
|
|
91
83
|
const groupActive = groupName || filterCopy.values[0]
|
|
92
|
-
const
|
|
84
|
+
const currentGroupValues = filterCopy.subGrouping.valuesLookup[groupActive as string]?.values || []
|
|
85
|
+
const defaultSubValue = currentGroupValues[0]
|
|
93
86
|
|
|
94
|
-
// Priority order: query string >
|
|
95
|
-
let activeValue
|
|
87
|
+
// Priority order: query string > configured default > existing active > first available value
|
|
88
|
+
let activeValue: string | undefined
|
|
96
89
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
90
|
+
if (queryStringFilterValue && currentGroupValues.includes(queryStringFilterValue)) {
|
|
91
|
+
// 1. Query string parameter takes highest priority (only if valid for the current group)
|
|
92
|
+
activeValue = queryStringFilterValue
|
|
93
|
+
} else if (
|
|
94
|
+
filterCopy.subGrouping.defaultValue &&
|
|
95
|
+
currentGroupValues.includes(filterCopy.subGrouping.defaultValue)
|
|
96
|
+
) {
|
|
97
|
+
// 2. Use configured defaultValue if it exists and is valid for the current group
|
|
98
|
+
activeValue = filterCopy.subGrouping.defaultValue
|
|
99
|
+
} else if (filterCopy.subGrouping.active && currentGroupValues.includes(filterCopy.subGrouping.active)) {
|
|
100
|
+
// 3. Keep existing active value if it's valid for the current group
|
|
101
|
+
activeValue = filterCopy.subGrouping.active
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
filterCopy.subGrouping.active = activeValue || defaultSubValue
|
|
@@ -119,35 +119,31 @@ export const getToFetch = (
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
export const setActiveNestedDropdown = (dropdownOptions, sharedFilter) => {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
}
|
|
122
|
+
const queryValue = getQueryParam(sharedFilter?.setByQueryParameter)
|
|
123
|
+
const subQueryValue = getQueryParam(sharedFilter?.subGrouping?.setByQueryParameter)
|
|
124
|
+
|
|
125
|
+
// Priority: query string > configured defaultValue > existing active (if valid) > first option
|
|
126
|
+
// Note: use loose equality here to match values across possible string/number differences
|
|
127
|
+
const validActive = dropdownOptions.find(option => option.value == sharedFilter.active)
|
|
128
|
+
sharedFilter.active =
|
|
129
|
+
queryValue || sharedFilter.defaultValue || (validActive ? sharedFilter.active : dropdownOptions[0]?.value)
|
|
130
|
+
|
|
131
|
+
const options = dropdownOptions.find(option => option.value == sharedFilter.active)?.subOptions || []
|
|
132
|
+
const validSubActive = options.find(o => o.value == sharedFilter.subGrouping?.active)
|
|
133
|
+
sharedFilter.subGrouping.active =
|
|
134
|
+
subQueryValue ||
|
|
135
|
+
sharedFilter.subGrouping?.defaultValue ||
|
|
136
|
+
(validSubActive ? sharedFilter.subGrouping.active : options[0]?.value)
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
export const setActiveMultiDropdown = (dropdownOptions, sharedFilter) => {
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
: defaultQueryParamValue?.split(',')
|
|
146
|
-
const multiDefaultValue = defaultQueryParamValue ? multiDefaultQueryParamValue : [dropdownOptions[0]?.value]
|
|
140
|
+
const queryValue = getQueryParam(sharedFilter?.setByQueryParameter)
|
|
141
|
+
const queryValues = Array.isArray(queryValue) ? queryValue : queryValue?.split(',')
|
|
142
|
+
const defaultValues = queryValue ? queryValues : [dropdownOptions[0]?.value]
|
|
147
143
|
const currentOption = (Array.isArray(sharedFilter.active) ? sharedFilter.active : []).filter(activeVal =>
|
|
148
144
|
dropdownOptions.find(option => option.value === activeVal)
|
|
149
145
|
)
|
|
150
|
-
sharedFilter.active = currentOption.length ? currentOption :
|
|
146
|
+
sharedFilter.active = currentOption.length ? currentOption : defaultValues
|
|
151
147
|
}
|
|
152
148
|
|
|
153
149
|
export const setAutoLoadDefaultValue = (
|
|
@@ -158,20 +154,20 @@ export const setAutoLoadDefaultValue = (
|
|
|
158
154
|
): SharedFilter => {
|
|
159
155
|
const sharedFiltersCopy = _.cloneDeep(sharedFilters)
|
|
160
156
|
const sharedFilter = _.cloneDeep(sharedFiltersCopy[sharedFilterIndex])
|
|
161
|
-
const
|
|
162
|
-
const
|
|
157
|
+
const queryValue = getQueryParam(sharedFilter?.setByQueryParameter)
|
|
158
|
+
const hasQuery = sharedFilter.setByQueryParameter ? queryValue !== undefined : false
|
|
163
159
|
if (!autoLoadFilterIndexes.length || !dropdownOptions?.length) {
|
|
164
|
-
if (
|
|
160
|
+
if (hasQuery && sharedFilter.apiFilter) {
|
|
165
161
|
const subQueryValue = getQueryParam(sharedFilter.subGrouping?.setByQueryParameter)
|
|
166
|
-
const
|
|
167
|
-
sharedFilter.queuedActive =
|
|
162
|
+
const isNested = subQueryValue !== undefined
|
|
163
|
+
sharedFilter.queuedActive = isNested ? [queryValue, subQueryValue] : queryValue
|
|
168
164
|
}
|
|
169
165
|
return sharedFilter // no autoLoading happening
|
|
170
166
|
}
|
|
171
|
-
if (autoLoadFilterIndexes.includes(sharedFilterIndex) ||
|
|
167
|
+
if (autoLoadFilterIndexes.includes(sharedFilterIndex) || hasQuery) {
|
|
172
168
|
const filterParents = sharedFiltersCopy.filter(f => sharedFilter.parents?.includes(f.key))
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
169
|
+
const missingParents = filterParents.some(p => !(p.active || p.queuedActive))
|
|
170
|
+
if (missingParents) return sharedFilter
|
|
175
171
|
if (sharedFilter.filterStyle === FILTER_STYLE.multiSelect) {
|
|
176
172
|
setActiveMultiDropdown(dropdownOptions, sharedFilter)
|
|
177
173
|
} else if (sharedFilter.filterStyle === FILTER_STYLE.nestedDropdown) {
|
|
@@ -179,7 +175,7 @@ export const setAutoLoadDefaultValue = (
|
|
|
179
175
|
} else {
|
|
180
176
|
const defaultValue = dropdownOptions[0]?.value
|
|
181
177
|
if (!sharedFilter.active) {
|
|
182
|
-
sharedFilter.active =
|
|
178
|
+
sharedFilter.active = queryValue ?? defaultValue
|
|
183
179
|
} else {
|
|
184
180
|
const currentOption = dropdownOptions.find(option => option.value == sharedFilter.active) // loose equality required: 2017 should equal '2017'
|
|
185
181
|
sharedFilter.active = currentOption ? currentOption.value : defaultValue
|
|
@@ -1,65 +1,67 @@
|
|
|
1
|
-
import _ from 'lodash'
|
|
2
|
-
import { FilterBehavior } from '../helpers/FilterBehavior'
|
|
3
|
-
import {
|
|
4
|
-
getQueryParams,
|
|
5
|
-
removeQueryParam,
|
|
6
|
-
updateQueryParam,
|
|
7
|
-
updateQueryString
|
|
8
|
-
} from '@cdc/core/helpers/queryStringUtils'
|
|
9
|
-
import { SharedFilter } from '../types/SharedFilter'
|
|
10
|
-
import { DashboardFilters } from '../types/DashboardFilters'
|
|
11
|
-
import { FILTER_STYLE } from '../types/FilterStyles'
|
|
12
|
-
|
|
13
|
-
const handleChildren = (sharedFilters: SharedFilter[], parentIndex: number) => {
|
|
14
|
-
const parentKey = sharedFilters[parentIndex].key
|
|
15
|
-
const childFilterIndexes = sharedFilters
|
|
16
|
-
.map((filter, index) => (filter.parents?.includes(parentKey) ? index : null))
|
|
17
|
-
.filter(i => i !== null)
|
|
18
|
-
if (childFilterIndexes.length) {
|
|
19
|
-
childFilterIndexes.forEach(filterIndex => {
|
|
20
|
-
const cur = sharedFilters[filterIndex]
|
|
21
|
-
if (cur.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
cur.subGrouping
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
sharedFiltersCopy[filterIndex].active = value
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
queryParams[currentFilter.setByQueryParameter]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
import { FilterBehavior } from '../helpers/FilterBehavior'
|
|
3
|
+
import {
|
|
4
|
+
getQueryParams,
|
|
5
|
+
removeQueryParam,
|
|
6
|
+
updateQueryParam,
|
|
7
|
+
updateQueryString
|
|
8
|
+
} from '@cdc/core/helpers/queryStringUtils'
|
|
9
|
+
import { SharedFilter } from '../types/SharedFilter'
|
|
10
|
+
import { DashboardFilters } from '../types/DashboardFilters'
|
|
11
|
+
import { FILTER_STYLE } from '../types/FilterStyles'
|
|
12
|
+
|
|
13
|
+
const handleChildren = (sharedFilters: SharedFilter[], parentIndex: number) => {
|
|
14
|
+
const parentKey = sharedFilters[parentIndex].key
|
|
15
|
+
const childFilterIndexes = sharedFilters
|
|
16
|
+
.map((filter, index) => (filter.parents?.includes(parentKey) ? index : null))
|
|
17
|
+
.filter(i => i !== null)
|
|
18
|
+
if (childFilterIndexes.length) {
|
|
19
|
+
childFilterIndexes.forEach(filterIndex => {
|
|
20
|
+
const cur = sharedFilters[filterIndex]
|
|
21
|
+
if (cur.type === 'urlfilter') {
|
|
22
|
+
if (cur.setByQueryParameter) removeQueryParam(cur.setByQueryParameter)
|
|
23
|
+
cur.active = ''
|
|
24
|
+
if (cur.subGrouping) {
|
|
25
|
+
cur.subGrouping.active = ''
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
return childFilterIndexes
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const changeFilterActive = (
|
|
34
|
+
filterIndex: number,
|
|
35
|
+
value: string | string[],
|
|
36
|
+
sharedFilters: SharedFilter[],
|
|
37
|
+
vizConfig: DashboardFilters
|
|
38
|
+
): [SharedFilter[], number[]] => {
|
|
39
|
+
const sharedFiltersCopy = _.cloneDeep(sharedFilters)
|
|
40
|
+
const currentFilter = sharedFiltersCopy[filterIndex]
|
|
41
|
+
if (vizConfig.filterBehavior !== FilterBehavior.Apply || vizConfig.autoLoad) {
|
|
42
|
+
if (currentFilter?.filterStyle === FILTER_STYLE.nestedDropdown) {
|
|
43
|
+
sharedFiltersCopy[filterIndex].active = value[0]
|
|
44
|
+
sharedFiltersCopy[filterIndex].subGrouping.active = value[1]
|
|
45
|
+
} else {
|
|
46
|
+
sharedFiltersCopy[filterIndex].active = value
|
|
47
|
+
handleChildren(sharedFiltersCopy, filterIndex)
|
|
48
|
+
const queryParams = getQueryParams()
|
|
49
|
+
if (
|
|
50
|
+
currentFilter.setByQueryParameter &&
|
|
51
|
+
queryParams[currentFilter.setByQueryParameter] !== currentFilter.active
|
|
52
|
+
) {
|
|
53
|
+
queryParams[currentFilter.setByQueryParameter] = currentFilter.active
|
|
54
|
+
updateQueryString(queryParams)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} else if (currentFilter.subGrouping) {
|
|
58
|
+
updateQueryParam(currentFilter.setByQueryParameter, value[0])
|
|
59
|
+
updateQueryParam(currentFilter.subGrouping.setByQueryParameter, value[1])
|
|
60
|
+
sharedFiltersCopy[filterIndex].queuedActive = value
|
|
61
|
+
} else {
|
|
62
|
+
const paramVal = Array.isArray(value) ? value.join(',') : value
|
|
63
|
+
if (currentFilter.setByQueryParameter) updateQueryParam(currentFilter.setByQueryParameter, paramVal)
|
|
64
|
+
sharedFiltersCopy[filterIndex].queuedActive = value
|
|
65
|
+
}
|
|
66
|
+
return [sharedFiltersCopy, handleChildren(sharedFiltersCopy, filterIndex)]
|
|
67
|
+
}
|
|
@@ -17,11 +17,12 @@ const cleanDashboardFootnotes = (config: DashboardConfig) => {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const cleanDashboardData = (config: DashboardConfig) => {
|
|
20
|
+
const cleanDashboardData = (config: DashboardConfig, isEditor = false) => {
|
|
21
21
|
if (config.datasets) {
|
|
22
22
|
Object.keys(config.datasets).forEach(datasetKey => {
|
|
23
23
|
delete config.datasets[datasetKey].formattedData
|
|
24
|
-
|
|
24
|
+
// Only delete data when not in editor mode
|
|
25
|
+
if (config.datasets[datasetKey].dataUrl && !isEditor) {
|
|
25
26
|
delete config.datasets[datasetKey].data
|
|
26
27
|
}
|
|
27
28
|
})
|
|
@@ -104,12 +105,12 @@ const removeRuntimeDataURLs = (config: DashboardConfig) => {
|
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
export const stripConfig = configToStrip => {
|
|
108
|
+
export const stripConfig = (configToStrip, isEditor = false) => {
|
|
108
109
|
const strippedConfig = cloneConfig(configToStrip)
|
|
109
110
|
if (strippedConfig.type === 'dashboard') {
|
|
110
111
|
if (strippedConfig.multiDashboards) {
|
|
111
112
|
strippedConfig.multiDashboards.forEach((multiDashboard, i) => {
|
|
112
|
-
cleanDashboardData(strippedConfig.multiDashboards[i])
|
|
113
|
+
cleanDashboardData(strippedConfig.multiDashboards[i], isEditor)
|
|
113
114
|
cleanSharedFilters(strippedConfig.multiDashboards[i])
|
|
114
115
|
cleanDashboardFootnotes(strippedConfig.multiDashboards[i])
|
|
115
116
|
cleanVisualizationFilters(strippedConfig.multiDashboards[i])
|
|
@@ -120,7 +121,7 @@ export const stripConfig = configToStrip => {
|
|
|
120
121
|
delete strippedConfig.label
|
|
121
122
|
}
|
|
122
123
|
delete strippedConfig.activeDashboard
|
|
123
|
-
cleanDashboardData(strippedConfig)
|
|
124
|
+
cleanDashboardData(strippedConfig, isEditor)
|
|
124
125
|
cleanSharedFilters(strippedConfig)
|
|
125
126
|
cleanDashboardFootnotes(strippedConfig)
|
|
126
127
|
cleanVisualizationFilters(strippedConfig)
|
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
import { DashboardState } from '../store/dashboard.reducer'
|
|
2
|
-
import { DashboardConfig as Config, DashboardConfig } from '../types/DashboardConfig'
|
|
3
|
-
import { filterData } from './filterData'
|
|
4
|
-
import { generateValuesForFilter } from './generateValuesForFilter'
|
|
5
|
-
import { getFormattedData } from './getFormattedData'
|
|
6
|
-
import { getVizKeys } from './getVizKeys'
|
|
7
|
-
import { getVizRowColumnLocator } from './getVizRowColumnLocator'
|
|
8
|
-
|
|
9
|
-
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
10
|
-
|
|
11
|
-
type UpdateState = Omit<DashboardState, 'config'> & {
|
|
12
|
-
config?: DashboardConfig
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const getUpdateConfig =
|
|
16
|
-
(state: UpdateState) =>
|
|
17
|
-
(newConfig, dataOverride?: Object): [Config, Object] => {
|
|
18
|
-
let newFilteredData = {}
|
|
19
|
-
let visualizationKeys = getVizKeys(newConfig)
|
|
20
|
-
|
|
21
|
-
const vizRowColumnLocator = getVizRowColumnLocator(newConfig.rows)
|
|
22
|
-
|
|
23
|
-
if (newConfig.dashboard.sharedFilters) {
|
|
24
|
-
newConfig.dashboard.sharedFilters.forEach((filter, i) => {
|
|
25
|
-
const filterIsSetByVizData = !!visualizationKeys.find(key => key === filter.setBy)
|
|
26
|
-
const _filter = newConfig.dashboard.sharedFilters[i]
|
|
27
|
-
|
|
28
|
-
const setValuesAndActive = filterValues => {
|
|
29
|
-
_filter.values = filterValues
|
|
30
|
-
if (filterValues.length > 0) {
|
|
31
|
-
const defaultValues = _filter.pivot ? _filter.values : _filter.values[0]
|
|
32
|
-
|
|
33
|
-
const queryStringFilterValue = getQueryStringFilterValue(_filter)
|
|
34
|
-
if (queryStringFilterValue) {
|
|
35
|
-
_filter.active = queryStringFilterValue
|
|
36
|
-
} else {
|
|
37
|
-
_filter.active = _filter.active || defaultValues
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const filterValues = generateValuesForFilter(filter.columnName, dataOverride || state.data)
|
|
43
|
-
if (filterIsSetByVizData) {
|
|
44
|
-
if (_filter.order === 'asc') {
|
|
45
|
-
filterValues.sort()
|
|
46
|
-
}
|
|
47
|
-
if (_filter.order === 'desc') {
|
|
48
|
-
filterValues.sort().reverse()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
setValuesAndActive(filterValues)
|
|
52
|
-
} else if ((!filter.values || filter.values.length === 0) && filter.showDropdown) {
|
|
53
|
-
setValuesAndActive(filterValues)
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
visualizationKeys.forEach(visualizationKey => {
|
|
58
|
-
const row = vizRowColumnLocator[visualizationKey]
|
|
59
|
-
if (newConfig.rows[row]?.datakey) return // data configured on the row level
|
|
60
|
-
const applicableFilters = newConfig.dashboard.sharedFilters.filter(
|
|
61
|
-
sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
if (applicableFilters.length > 0) {
|
|
65
|
-
const visualization = newConfig.visualizations[visualizationKey]
|
|
66
|
-
const _newConfigDataSet = newConfig.datasets[visualization.dataKey]
|
|
67
|
-
const formattedData = getFormattedData(
|
|
68
|
-
_newConfigDataSet?.data || visualization.data,
|
|
69
|
-
visualization.dataDescription
|
|
70
|
-
)
|
|
71
|
-
const _data = formattedData || (dataOverride || state.data)[visualization.dataKey]
|
|
72
|
-
newFilteredData[visualizationKey] = filterData(applicableFilters, _data)
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
newConfig.rows.forEach((row, rowIndex) => {
|
|
77
|
-
const applicableFilters = newConfig.dashboard.sharedFilters.filter(
|
|
78
|
-
sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(rowIndex) !== -1
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
if (applicableFilters.length > 0) {
|
|
82
|
-
const formattedData = getFormattedData(row.data, row.dataDescription)
|
|
83
|
-
const _data = formattedData || (dataOverride || state.data)[rowIndex]
|
|
84
|
-
newFilteredData[rowIndex] = filterData(applicableFilters, _data)
|
|
85
|
-
}
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
//Enforce default values that need to be calculated at runtime
|
|
89
|
-
newConfig.runtime = {}
|
|
90
|
-
return [newConfig, newFilteredData]
|
|
91
|
-
}
|
|
1
|
+
import { DashboardState } from '../store/dashboard.reducer'
|
|
2
|
+
import { DashboardConfig as Config, DashboardConfig } from '../types/DashboardConfig'
|
|
3
|
+
import { filterData } from './filterData'
|
|
4
|
+
import { generateValuesForFilter } from './generateValuesForFilter'
|
|
5
|
+
import { getFormattedData } from './getFormattedData'
|
|
6
|
+
import { getVizKeys } from './getVizKeys'
|
|
7
|
+
import { getVizRowColumnLocator } from './getVizRowColumnLocator'
|
|
8
|
+
|
|
9
|
+
import { getQueryStringFilterValue } from '@cdc/core/helpers/queryStringUtils'
|
|
10
|
+
|
|
11
|
+
type UpdateState = Omit<DashboardState, 'config'> & {
|
|
12
|
+
config?: DashboardConfig
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getUpdateConfig =
|
|
16
|
+
(state: UpdateState) =>
|
|
17
|
+
(newConfig, dataOverride?: Object): [Config, Object] => {
|
|
18
|
+
let newFilteredData = {}
|
|
19
|
+
let visualizationKeys = getVizKeys(newConfig)
|
|
20
|
+
|
|
21
|
+
const vizRowColumnLocator = getVizRowColumnLocator(newConfig.rows)
|
|
22
|
+
|
|
23
|
+
if (newConfig.dashboard.sharedFilters) {
|
|
24
|
+
newConfig.dashboard.sharedFilters.forEach((filter, i) => {
|
|
25
|
+
const filterIsSetByVizData = !!visualizationKeys.find(key => key === filter.setBy)
|
|
26
|
+
const _filter = newConfig.dashboard.sharedFilters[i]
|
|
27
|
+
|
|
28
|
+
const setValuesAndActive = filterValues => {
|
|
29
|
+
_filter.values = filterValues
|
|
30
|
+
if (filterValues.length > 0) {
|
|
31
|
+
const defaultValues = _filter.pivot ? _filter.values : _filter.values[0]
|
|
32
|
+
|
|
33
|
+
const queryStringFilterValue = getQueryStringFilterValue(_filter)
|
|
34
|
+
if (queryStringFilterValue) {
|
|
35
|
+
_filter.active = queryStringFilterValue
|
|
36
|
+
} else {
|
|
37
|
+
_filter.active = _filter.active || defaultValues
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const filterValues = generateValuesForFilter(filter.columnName, dataOverride || state.data)
|
|
43
|
+
if (filterIsSetByVizData) {
|
|
44
|
+
if (_filter.order === 'asc') {
|
|
45
|
+
filterValues.sort()
|
|
46
|
+
}
|
|
47
|
+
if (_filter.order === 'desc') {
|
|
48
|
+
filterValues.sort().reverse()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setValuesAndActive(filterValues)
|
|
52
|
+
} else if ((!filter.values || filter.values.length === 0) && filter.showDropdown) {
|
|
53
|
+
setValuesAndActive(filterValues)
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
visualizationKeys.forEach(visualizationKey => {
|
|
58
|
+
const row = vizRowColumnLocator[visualizationKey]
|
|
59
|
+
if (newConfig.rows[row]?.datakey) return // data configured on the row level
|
|
60
|
+
const applicableFilters = newConfig.dashboard.sharedFilters.filter(
|
|
61
|
+
sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(visualizationKey) !== -1
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if (applicableFilters.length > 0) {
|
|
65
|
+
const visualization = newConfig.visualizations[visualizationKey]
|
|
66
|
+
const _newConfigDataSet = newConfig.datasets[visualization.dataKey]
|
|
67
|
+
const formattedData = getFormattedData(
|
|
68
|
+
_newConfigDataSet?.data || visualization.data,
|
|
69
|
+
visualization.dataDescription
|
|
70
|
+
)
|
|
71
|
+
const _data = formattedData || (dataOverride || state.data)[visualization.dataKey]
|
|
72
|
+
newFilteredData[visualizationKey] = filterData(applicableFilters, _data)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
newConfig.rows.forEach((row, rowIndex) => {
|
|
77
|
+
const applicableFilters = newConfig.dashboard.sharedFilters.filter(
|
|
78
|
+
sharedFilter => sharedFilter.usedBy && sharedFilter.usedBy.indexOf(rowIndex) !== -1
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if (applicableFilters.length > 0) {
|
|
82
|
+
const formattedData = getFormattedData(row.data, row.dataDescription)
|
|
83
|
+
const _data = formattedData || (dataOverride || state.data)[rowIndex]
|
|
84
|
+
newFilteredData[rowIndex] = filterData(applicableFilters, _data)
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
//Enforce default values that need to be calculated at runtime
|
|
89
|
+
newConfig.runtime = {}
|
|
90
|
+
return [newConfig, newFilteredData]
|
|
91
|
+
}
|