@cdc/dashboard 4.25.1 → 4.25.3-6

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 (38) hide show
  1. package/dist/cdcdashboard.js +53114 -52737
  2. package/examples/all-components.json +529 -4607
  3. package/examples/dashboard-gallery.json +397 -397
  4. package/examples/private/DEV-10527.json +845 -0
  5. package/examples/private/DEV-10586.json +54319 -0
  6. package/examples/private/DEV-10856.json +54319 -0
  7. package/examples/private/DEV-9932.json +95 -0
  8. package/examples/private/dashboard-map-filter.json +815 -0
  9. package/examples/private/dev-10856-2.json +1348 -0
  10. package/examples/private/feelings.json +1 -0
  11. package/examples/private/markup.json +115 -0
  12. package/examples/private/nhis.json +1792 -0
  13. package/index.html +4 -3
  14. package/package.json +9 -9
  15. package/src/CdcDashboard.tsx +5 -8
  16. package/src/CdcDashboardComponent.tsx +58 -56
  17. package/src/_stories/Dashboard.stories.tsx +31 -0
  18. package/src/_stories/_mock/dashboard-filter-asc.json +551 -0
  19. package/src/components/CollapsibleVisualizationRow.tsx +1 -1
  20. package/src/components/DashboardFilters/DashboardFilters.tsx +10 -5
  21. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +22 -5
  22. package/src/components/DashboardFilters/DashboardFiltersEditor/components/DeleteFilterModal.tsx +13 -3
  23. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +129 -40
  24. package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +10 -7
  25. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +11 -12
  26. package/src/components/DashboardFilters/dashboardfilter.styles.css +0 -2
  27. package/src/components/VisualizationRow.tsx +29 -15
  28. package/src/helpers/addValuesToDashboardFilters.ts +6 -5
  29. package/src/helpers/apiFilterHelpers.ts +10 -5
  30. package/src/helpers/changeFilterActive.ts +17 -4
  31. package/src/helpers/getFilteredData.ts +13 -4
  32. package/src/helpers/getUpdateConfig.ts +11 -4
  33. package/src/helpers/loadAPIFilters.ts +6 -4
  34. package/src/helpers/tests/updatesChildFilters.test.ts +56 -0
  35. package/src/helpers/updateChildFilters.ts +50 -0
  36. package/src/scss/main.scss +0 -3
  37. package/src/store/dashboard.reducer.ts +46 -24
  38. package/src/types/SharedFilter.ts +1 -1
@@ -0,0 +1,56 @@
1
+ import { SharedFilter } from '../../types/SharedFilter'
2
+ import { updateChildFilters } from '../updateChildFilters'
3
+
4
+ describe('updateChildFilters', () => {
5
+ it('should filter data based on the provided filters', () => {
6
+ const filters = [
7
+ {
8
+ tier: 1,
9
+ columnName: 'name',
10
+ active: 'John',
11
+ key: 'Parent Filter',
12
+ values: ['John', 'Kelly', 'Norman', 'Jane']
13
+ },
14
+ {
15
+ tier: 2,
16
+ columnName: 'lastName',
17
+ active: '',
18
+ key: 'Child Filter',
19
+ parents: 'Parent Filter',
20
+ values: ['Deer', 'Roberts']
21
+ }
22
+ ] as SharedFilter[]
23
+ const data = {
24
+ vizKey: [
25
+ [
26
+ { name: 'John', lastName: 'Deer' },
27
+ { name: 'John', lastName: 'Roberts' },
28
+ { name: 'Kelly', lastName: 'Adams' },
29
+ { name: 'Norman', lastName: 'Sally' },
30
+ { name: 'Jane', lastName: 'Gorman' }
31
+ ]
32
+ ]
33
+ }
34
+
35
+ let exprectedResult = [
36
+ {
37
+ tier: 1,
38
+ columnName: 'name',
39
+ active: 'John',
40
+ key: 'Parent Filter',
41
+ values: ['John', 'Kelly', 'Norman', 'Jane']
42
+ },
43
+ {
44
+ tier: 2,
45
+ columnName: 'lastName',
46
+ active: '',
47
+ key: 'Child Filter',
48
+ parents: 'Parent Filter',
49
+ values: ['Deer', 'Roberts'] // updated values only
50
+ }
51
+ ]
52
+ const result = updateChildFilters(filters, data)
53
+
54
+ expect(result).toEqual(exprectedResult)
55
+ })
56
+ })
@@ -0,0 +1,50 @@
1
+ import { SharedFilter } from '../types/SharedFilter'
2
+ import _ from 'lodash'
3
+
4
+ export const updateChildFilters = (newSharedFilters: SharedFilter[], data: Record<string, any>): SharedFilter[] => {
5
+ const dataSet = Object.values(data).flat()
6
+
7
+ // Find indexes of all child filters
8
+ const childFilterIndexes: number[] = newSharedFilters
9
+ .map((filter, index) => (filter.type === 'datafilter' && filter.parents ? index : -1))
10
+ .filter(index => index !== -1)
11
+ if (childFilterIndexes.length === 0) return newSharedFilters
12
+
13
+ // deep copy of the shared filters
14
+ const updatedFilters = _.cloneDeep(newSharedFilters)
15
+
16
+ // Update each child filter
17
+ childFilterIndexes.forEach(childIndex => {
18
+ const childFilter: SharedFilter = newSharedFilters[childIndex]
19
+ const parentFilter: SharedFilter = newSharedFilters.find(
20
+ filter => String(childFilter.parents) === String(filter.key)
21
+ )
22
+
23
+ if (parentFilter) {
24
+ // Filter dataset based on parent's active value
25
+ const parentsActiveValues: string[] = dataSet.filter((d: Record<string, any>) => {
26
+ return parentFilter.active?.includes(d[parentFilter.columnName])
27
+ })
28
+ // Get unique active values for the child filter
29
+ const childFilterValues = _.uniq(parentsActiveValues.map(d => d[childFilter.columnName]).filter(Boolean))
30
+
31
+ // Update the child filter if unique values exist
32
+ if (childFilterValues.length > 0) {
33
+ const isChildMultiSelect = childFilter.filterStyle === 'multi-select'
34
+ const activeValue = isChildMultiSelect
35
+ ? childFilterValues
36
+ : childFilter.active
37
+ ? childFilter.active
38
+ : childFilter.defaultValue
39
+ ? childFilter.defaultValue
40
+ : childFilterValues[0]
41
+ updatedFilters[childIndex] = {
42
+ ...childFilter,
43
+ values: childFilterValues,
44
+ active: activeValue
45
+ }
46
+ }
47
+ }
48
+ })
49
+ return updatedFilters
50
+ }
@@ -191,9 +191,6 @@
191
191
  .data-table-heading {
192
192
  display: none;
193
193
  }
194
- .table-container {
195
- margin: 0 1em;
196
- }
197
194
  }
198
195
 
199
196
  .dashboard-download-link {
@@ -1,6 +1,6 @@
1
1
  import _ from 'lodash'
2
2
  import { getUpdateConfig } from '../helpers/getUpdateConfig'
3
- import { MultiDashboardConfig } from '../types/MultiDashboard'
3
+ import { MultiDashboard, MultiDashboardConfig } from '../types/MultiDashboard'
4
4
  import DashboardActions from './dashboard.actions'
5
5
  import { devToolsWrapper } from '@cdc/core/helpers/withDevTools'
6
6
  import { Tab } from '../types/Tab'
@@ -44,7 +44,10 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
44
44
  const newRows = state.config.rows.map((row, i) => (i === rowIndex ? { ...row, footnotesId: id } : row))
45
45
  return {
46
46
  ...state,
47
- config: { ...state.config, rows: newRows, visualizations: { ...state.config.visualizations, [id]: config } }
47
+ config: saveMultiChanges(
48
+ { ...state.config, rows: newRows, visualizations: { ...state.config.visualizations, [id]: config } },
49
+ state.config.activeDashboard
50
+ )
48
51
  }
49
52
  }
50
53
  case 'ADD_NEW_DASHBOARD': {
@@ -55,7 +58,7 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
55
58
  }
56
59
  case 'UPDATE_CONFIG': {
57
60
  const [config, filteredData] = getUpdateConfig(state)(...action.payload)
58
- return { ...state, config, filteredData }
61
+ return { ...state, config: saveMultiChanges(config, state.config.activeDashboard), filteredData }
59
62
  }
60
63
  case 'APPLY_CONFIG': {
61
64
  // using advanced editor. Wipe all existing data and apply new config
@@ -68,14 +71,17 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
68
71
  if (data) acc[key] = data
69
72
  return acc
70
73
  }, {})
71
- return { ...initialState, config, filteredData, data }
74
+ return { ...initialState, config: saveMultiChanges(config, state.config.activeDashboard), filteredData, data }
72
75
  }
73
76
  case 'SET_CONFIG': {
74
77
  if (
75
78
  action.payload.activeDashboard === undefined ||
76
79
  state.config.activeDashboard === action.payload.activeDashboard
77
80
  ) {
78
- return { ...state, config: { ...state.config, ...action.payload } }
81
+ return {
82
+ ...state,
83
+ config: saveMultiChanges({ ...state.config, ...action.payload }, action.payload.activeDashboard)
84
+ }
79
85
  } else return state // ignore SET_CONFIG calls that have the wrong activeDashboard due to async api fetching
80
86
  }
81
87
  case 'SET_DATA': {
@@ -93,14 +99,10 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
93
99
  case 'SET_SHARED_FILTERS': {
94
100
  const newSharedFilters = action.payload
95
101
  const newDashboardConfig = { ...state.config.dashboard, sharedFilters: newSharedFilters }
96
- if (state.config.multiDashboards) {
97
- const saveSlot = state.config.activeDashboard
98
- const newMultiDashboards = _.cloneDeep(state.config.multiDashboards)
99
- newMultiDashboards[saveSlot].dashboard = newDashboardConfig
100
- const newState = applyMultiDashboards(state, newMultiDashboards)
101
- return { ...newState, config: { ...newState.config, dashboard: newDashboardConfig } }
102
+ return {
103
+ ...state,
104
+ config: saveMultiChanges({ ...state.config, dashboard: newDashboardConfig }, state.config.activeDashboard)
102
105
  }
103
- return { ...state, config: { ...state.config, dashboard: newDashboardConfig } }
104
106
  }
105
107
  case 'SET_TAB_SELECTED': {
106
108
  return { ...state, tabSelected: action.payload }
@@ -143,7 +145,8 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
143
145
  const label = newMultiDashboards[saveSlot].label
144
146
  const toSave = _.pick(state.config, ['dashboard', 'visualizations', 'rows'])
145
147
  newMultiDashboards[saveSlot] = { ...toSave, label }
146
- return applyMultiDashboards(state, newMultiDashboards)
148
+ const newConfig = saveMultiChanges(state.config, saveSlot)
149
+ return { ...state, config: newConfig }
147
150
  }
148
151
  case 'INITIALIZE_MULTIDASHBOARDS': {
149
152
  const label = 'New Dashboard 1'
@@ -176,7 +179,10 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
176
179
  newRows[rowIdx].columns[colIdx].widget = vizKey
177
180
  return {
178
181
  ...state,
179
- config: { ...state.config, visualizations: { ...state.config.visualizations, [vizKey]: newViz }, rows: newRows }
182
+ config: saveMultiChanges(
183
+ { ...state.config, visualizations: { ...state.config.visualizations, [vizKey]: newViz }, rows: newRows },
184
+ state.config.activeDashboard
185
+ )
180
186
  }
181
187
  }
182
188
  case 'MOVE_VISUALIZATION': {
@@ -186,7 +192,7 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
186
192
  newRows[rowIdx].columns[colIdx].widget = widget.uid
187
193
  return {
188
194
  ...state,
189
- config: { ...state.config, rows: newRows }
195
+ config: saveMultiChanges({ ...state.config, rows: newRows }, state.config.activeDashboard)
190
196
  }
191
197
  }
192
198
  case 'UPDATE_VISUALIZATION': {
@@ -194,7 +200,10 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
194
200
  const updatedViz = { ...state.config.visualizations[vizKey], ...configureData } as AnyVisualization
195
201
  return {
196
202
  ...state,
197
- config: { ...state.config, visualizations: { ...state.config.visualizations, [vizKey]: updatedViz } }
203
+ config: saveMultiChanges(
204
+ { ...state.config, visualizations: { ...state.config.visualizations, [vizKey]: updatedViz } },
205
+ state.config.activeDashboard
206
+ )
198
207
  }
199
208
  }
200
209
  case 'UPDATE_ROW': {
@@ -205,12 +214,13 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
205
214
  }
206
215
  return row
207
216
  })
208
- return { ...state, config: { ...state.config, rows: newRows } }
217
+ return { ...state, config: saveMultiChanges({ ...state.config, rows: newRows }, state.config.activeDashboard) }
209
218
  }
210
219
  case 'DELETE_WIDGET': {
211
220
  const { uid } = action.payload
212
221
  const newRows = _.cloneDeep(state.config.rows)
213
222
  const newVisualizations = _.cloneDeep(state.config.visualizations)
223
+ delete newVisualizations[uid]
214
224
  const newSharedFilters = _.cloneDeep(state.config.dashboard.sharedFilters)
215
225
  if (newSharedFilters && newSharedFilters.length > 0) {
216
226
  newSharedFilters.forEach(sharedFilter => {
@@ -227,12 +237,15 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
227
237
 
228
238
  return {
229
239
  ...state,
230
- config: {
231
- ...state.config,
232
- dashboard: { ...state.config.dashboard, sharedFilters: newSharedFilters },
233
- visualizations: newVisualizations,
234
- rows: filteredRows
235
- }
240
+ config: saveMultiChanges(
241
+ {
242
+ ...state.config,
243
+ dashboard: { ...state.config.dashboard, sharedFilters: newSharedFilters },
244
+ visualizations: newVisualizations,
245
+ rows: filteredRows
246
+ },
247
+ state.config.activeDashboard
248
+ )
236
249
  }
237
250
  }
238
251
  default:
@@ -240,7 +253,16 @@ const reducer = (state: DashboardState, action: DashboardActions): DashboardStat
240
253
  }
241
254
  }
242
255
 
243
- const applyMultiDashboards = (state, newMultiDashboards) => ({
256
+ const saveMultiChanges = (config: MultiDashboardConfig, saveSlot?: number): MultiDashboardConfig => {
257
+ if (saveSlot === undefined || !config.multiDashboards) return config
258
+ const newMultiDashboards = [...config.multiDashboards]
259
+ const label = newMultiDashboards[saveSlot].label
260
+ const toSave = _.pick(config, ['dashboard', 'visualizations', 'rows'])
261
+ newMultiDashboards[saveSlot] = { ...toSave, label }
262
+ return { ...config, multiDashboards: newMultiDashboards }
263
+ }
264
+
265
+ const applyMultiDashboards = (state: DashboardState, newMultiDashboards: MultiDashboard[]): DashboardState => ({
244
266
  ...state,
245
267
  config: { ...state.config, multiDashboards: newMultiDashboards }
246
268
  })
@@ -11,7 +11,7 @@ export type SharedFilter = FilterBase & {
11
11
  active?: string | string[]
12
12
  queuedActive?: string | string[]
13
13
  usedBy?: (string | number)[] // if number used by whole row, else used by specific viz
14
- order: 'cust' | 'desc' | 'asc'
14
+ order: 'cust' | 'desc' | 'asc' | 'column'
15
15
  parents?: string[]
16
16
  setBy?: string
17
17
  selectLimit?: number