@cdc/dashboard 4.25.11 → 4.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/Dynamic_Data.md +66 -0
  2. package/dist/cdcdashboard.js +78783 -76370
  3. package/examples/api-dashboard-data.json +272 -0
  4. package/examples/api-dashboard-years.json +11 -0
  5. package/examples/api-geographies-data.json +11 -0
  6. package/examples/private/cat-y.json +1235 -0
  7. package/examples/private/chronic-dash.json +1584 -0
  8. package/examples/private/map-issue.json +2260 -0
  9. package/examples/private/mpinc-state-reports.json +2260 -0
  10. package/examples/private/nwss/rsv.json +1240 -0
  11. package/examples/private/simple-dash.json +490 -0
  12. package/examples/private/test-dash.json +0 -0
  13. package/examples/private/test123.json +491 -0
  14. package/examples/test-dashboard-simple.json +503 -0
  15. package/index.html +24 -25
  16. package/package.json +12 -11
  17. package/src/CdcDashboardComponent.tsx +18 -2
  18. package/src/_stories/Dashboard.DataSetup.stories.tsx +203 -0
  19. package/src/_stories/Dashboard.stories.tsx +385 -1
  20. package/src/_stories/_mock/filter-cascade.json +3350 -0
  21. package/src/_stories/_mock/gallery-data-bite-dashboard.json +3500 -0
  22. package/src/_stories/_mock/nested-parent-child-filters.json +392 -0
  23. package/src/_stories/_mock/parent-child-filters.json +233 -0
  24. package/src/components/DashboardFilters/DashboardFilters.tsx +20 -11
  25. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +89 -38
  26. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +51 -29
  27. package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +146 -9
  28. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +11 -7
  29. package/src/components/DataDesignerModal.tsx +6 -1
  30. package/src/components/Header/Header.tsx +51 -20
  31. package/src/components/VisualizationRow.tsx +71 -5
  32. package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +2 -3
  33. package/src/components/Widget/Widget.tsx +1 -1
  34. package/src/data/initial-state.js +1 -0
  35. package/src/helpers/addValuesToDashboardFilters.ts +15 -22
  36. package/src/helpers/changeFilterActive.ts +67 -65
  37. package/src/helpers/formatConfigBeforeSave.ts +6 -5
  38. package/src/helpers/getUpdateConfig.ts +91 -91
  39. package/src/helpers/tests/updatesChildFilters.test.ts +53 -22
  40. package/src/helpers/updateChildFilters.ts +50 -27
  41. package/src/scss/main.scss +141 -1
  42. package/src/test/CdcDashboard.test.jsx +9 -4
  43. package/src/types/Dashboard.ts +1 -0
  44. package/src/types/FilterStyles.ts +8 -7
  45. package/src/types/SharedFilter.ts +13 -0
  46. package/LICENSE +0 -201
  47. package/examples/private/DEV-10538.json +0 -407
  48. package/examples/private/DEV-11072.json +0 -7591
  49. package/examples/private/DEV-11405.json +0 -39112
  50. package/examples/private/delete.json +0 -32919
  51. package/examples/private/pedro.json +0 -1
@@ -93,6 +93,63 @@ const VisualizationRow: React.FC<VizRowProps> = ({
93
93
  if (row.toggle) setToggled(0)
94
94
  }, [config.activeDashboard, index])
95
95
 
96
+ useEffect(() => {
97
+ // Trigger window resize event when tab changes to force chart re-render
98
+ if (row.toggle && toggledRow !== undefined) {
99
+ // Use setTimeout to ensure the d-none class has been removed first
100
+ setTimeout(() => {
101
+ window.dispatchEvent(new Event('resize'))
102
+ }, 50)
103
+ }
104
+ }, [toggledRow, row.toggle])
105
+
106
+ // Equalize TP5 data bite title heights in the same row
107
+ useEffect(() => {
108
+ const rowElement = document.querySelector(`[data-row-index="${index}"]`)
109
+ if (!rowElement) return
110
+
111
+ const tp5Titles = Array.from(rowElement.querySelectorAll('.bite__style--tp5 .cdc-callout__heading'))
112
+ if (tp5Titles.length <= 1) return // No need to equalize if there's only one or none
113
+
114
+ const equalizeTP5Titles = () => {
115
+ // Reset heights first
116
+ tp5Titles.forEach((title: HTMLElement) => {
117
+ title.style.minHeight = ''
118
+ })
119
+
120
+ // Calculate max height after reset
121
+ let maxHeight = 0
122
+ tp5Titles.forEach((title: HTMLElement) => {
123
+ const height = title.offsetHeight
124
+ if (height > maxHeight) maxHeight = height
125
+ })
126
+
127
+ // Apply max height to all titles
128
+ if (maxHeight > 0) {
129
+ tp5Titles.forEach((title: HTMLElement) => {
130
+ title.style.minHeight = `${maxHeight}px`
131
+ })
132
+ }
133
+ }
134
+
135
+ // Initial equalization
136
+ equalizeTP5Titles()
137
+
138
+ // Use ResizeObserver to watch for size changes in any of the titles
139
+ const resizeObserver = new ResizeObserver(() => {
140
+ equalizeTP5Titles()
141
+ })
142
+
143
+ // Observe all titles
144
+ tp5Titles.forEach(title => {
145
+ resizeObserver.observe(title as Element)
146
+ })
147
+
148
+ return () => {
149
+ resizeObserver.disconnect()
150
+ }
151
+ }, [index, row, config, filteredDataOverride])
152
+
96
153
  const show = useMemo(() => {
97
154
  if (row.toggle) {
98
155
  return row.columns.map((col, i) => i === toggledRow)
@@ -166,13 +223,18 @@ const VisualizationRow: React.FC<VizRowProps> = ({
166
223
  }
167
224
 
168
225
  return (
169
- <div className={`row${row.equalHeight ? ' equal-height' : ''}${row.toggle ? ' toggle' : ''}`} key={`row__${index}`}>
226
+ <div
227
+ className={`row${row.equalHeight ? ' equal-height' : ''}${row.toggle ? ' toggle' : ''}`}
228
+ key={`row__${index}`}
229
+ data-row-index={index}
230
+ >
170
231
  {row.toggle && !inNoDataState && (
171
232
  <Toggle row={row} visualizations={config.visualizations} active={toggledRow} setToggled={setToggled} />
172
233
  )}
173
234
  {row.columns.map((col, colIndex) => {
174
235
  if (col.width) {
175
- if (!col.widget) return <div key={`row__${index}__col__${colIndex}`} className={`col col-${col.width}`}></div>
236
+ if (!col.widget)
237
+ return <div key={`row__${index}__col__${colIndex}`} className={`col-12 col-md-${col.width}`}></div>
176
238
 
177
239
  const visualizationConfig = getVizConfig(
178
240
  col.widget,
@@ -212,9 +274,14 @@ const VisualizationRow: React.FC<VizRowProps> = ({
212
274
  </a>
213
275
  )
214
276
 
277
+ // Markup-includes with external URLs don't depend on dashboard data
278
+ const isMarkupIncludeWithoutDataDependency =
279
+ type === 'markup-include' && !visualizationConfig.dataKey && !visualizationConfig.data?.length
280
+
215
281
  const hideVisualization =
216
282
  inNoDataState &&
217
283
  filterBehavior !== 'Apply Button' &&
284
+ !isMarkupIncludeWithoutDataDependency &&
218
285
  (type !== 'dashboardFilters' || applyButtonNotClicked(visualizationConfig))
219
286
 
220
287
  const shouldShow = row.toggle === undefined || (row.toggle && show[colIndex])
@@ -224,11 +291,10 @@ const VisualizationRow: React.FC<VizRowProps> = ({
224
291
  sharedFilterIndexes &&
225
292
  sharedFilterIndexes.filter(idx => config.dashboard.sharedFilters?.[idx]?.showDropdown === false).length ===
226
293
  sharedFilterIndexes.length
227
- const hasMarginBottom = !isLastRow && !hiddenDashboardFilters
228
294
 
229
295
  const vizWrapperClass = `col-12 col-md-${col.width}${!shouldShow ? ' d-none' : ''}${
230
- hideVisualization ? ' hide-parent-visualization' : hasMarginBottom ? ' mb-4' : ''
231
- }`
296
+ hideVisualization ? ' hide-parent-visualization' : ''
297
+ }${hiddenDashboardFilters ? ' hidden-dashboard-filters' : ''}`
232
298
  const link =
233
299
  config.table && config.table.show && config.datasets && table && table.showDataTableLink
234
300
  ? tableLink
@@ -51,7 +51,6 @@ const addVisualization = (type, subType) => {
51
51
  markupVariables: [],
52
52
  showHeader: true,
53
53
  srcUrl: '#example',
54
- title: 'Markup Include',
55
54
  useInlineHTML: true
56
55
  }
57
56
  newVisualizationConfig.theme = 'theme-blue'
@@ -81,7 +80,7 @@ const addVisualization = (type, subType) => {
81
80
 
82
81
  const VisualizationsPanel = () => {
83
82
  const [advancedEditing, setAdvancedEditing] = useState(false)
84
- const { config } = useContext(DashboardContext)
83
+ const { config, isEditor } = useContext(DashboardContext)
85
84
  const dispatch = useContext(DashboardDispatchContext)
86
85
  const loadConfig = incomingConfig => {
87
86
  const newConfig = !incomingConfig.multiDashboards
@@ -124,7 +123,7 @@ const VisualizationsPanel = () => {
124
123
  loadConfig={loadConfig}
125
124
  config={config}
126
125
  convertStateToConfig={() => undefined}
127
- stripConfig={stripConfig}
126
+ stripConfig={cfg => stripConfig(cfg, isEditor)}
128
127
  onExpandCollapse={() => {
129
128
  setAdvancedEditing(!advancedEditing)
130
129
  }}
@@ -123,7 +123,7 @@ const Widget = ({
123
123
  if (!widgetConfig) return
124
124
  dispatch({
125
125
  type: 'UPDATE_VISUALIZATION',
126
- payload: { vizKey: widgetConfig.uid as string, configureData: { editing: true } }
126
+ payload: { vizKey: widgetConfig.uid as string, configureData: { editing: true, showEditorPanel: true } }
127
127
  })
128
128
  loadSampleData()
129
129
  }
@@ -1,6 +1,7 @@
1
1
  export default {
2
2
  dashboard: {
3
3
  theme: 'theme-blue',
4
+ titleStyle: 'small',
4
5
  sharedFilters: []
5
6
  },
6
7
  rows: [[{ width: 12 }, {}, {}]],
@@ -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
- const filterValues = generateValuesForFilter(getSelector(filter), data)
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,21 @@ 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
- // Preserve existing active value if it's valid in the new filter values
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
+ // Initialize active from defaultValue if not already set
62
+ // OR if defaultValue exists, always use it (overrides stale active from saved config)
66
63
  if (filterCopy.defaultValue) {
67
- // If defaultValue is explicitly set, always use it
68
64
  filterCopy.active = filterCopy.defaultValue
69
- } else if (isCurrentActiveValid) {
70
- // Keep the current active value if valid
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
65
+ } else if (!filterCopy.active) {
66
+ filterCopy.active = filterCopy.resetLabel || filterCopy.values[0]
79
67
  }
80
68
  }
81
69
  }
82
70
 
71
+ // Check if filter should be hidden by query parameter
72
+ if (isFilterHiddenByQuery(filterCopy)) {
73
+ filterCopy.showDropdown = false
74
+ }
75
+
83
76
  // Handle nested dropdown subGrouping.active property
84
77
  if (filterCopy.subGrouping && filterCopy.subGrouping.valuesLookup) {
85
78
  const groupName = filterCopy.active as string
@@ -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.setByQueryParameter) removeQueryParam(cur.setByQueryParameter)
22
- cur.active = ''
23
- if (cur.subGrouping) {
24
- cur.subGrouping.active = ''
25
- }
26
- })
27
- }
28
- return childFilterIndexes
29
- }
30
-
31
- export const changeFilterActive = (
32
- filterIndex: number,
33
- value: string | string[],
34
- sharedFilters: SharedFilter[],
35
- vizConfig: DashboardFilters
36
- ): [SharedFilter[], number[]] => {
37
- const sharedFiltersCopy = _.cloneDeep(sharedFilters)
38
- const currentFilter = sharedFiltersCopy[filterIndex]
39
- if (vizConfig.filterBehavior !== FilterBehavior.Apply || vizConfig.autoLoad) {
40
- if (currentFilter?.filterStyle === FILTER_STYLE.nestedDropdown) {
41
- sharedFiltersCopy[filterIndex].active = value[0]
42
- sharedFiltersCopy[filterIndex].subGrouping.active = value[1]
43
- } else {
44
- sharedFiltersCopy[filterIndex].active = value
45
- handleChildren(sharedFiltersCopy, filterIndex)
46
- const queryParams = getQueryParams()
47
- if (
48
- currentFilter.setByQueryParameter &&
49
- queryParams[currentFilter.setByQueryParameter] !== currentFilter.active
50
- ) {
51
- queryParams[currentFilter.setByQueryParameter] = currentFilter.active
52
- updateQueryString(queryParams)
53
- }
54
- }
55
- } else if (currentFilter.subGrouping) {
56
- updateQueryParam(currentFilter.setByQueryParameter, value[0])
57
- updateQueryParam(currentFilter.subGrouping.setByQueryParameter, value[1])
58
- sharedFiltersCopy[filterIndex].queuedActive = value
59
- } else {
60
- const paramVal = Array.isArray(value) ? value.join(',') : value
61
- if (currentFilter.setByQueryParameter) updateQueryParam(currentFilter.setByQueryParameter, paramVal)
62
- sharedFiltersCopy[filterIndex].queuedActive = value
63
- }
64
- return [sharedFiltersCopy, handleChildren(sharedFiltersCopy, filterIndex)]
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
- if (config.datasets[datasetKey].dataUrl) {
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
+ }