@cdc/dashboard 4.24.7 → 4.24.9-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 (41) hide show
  1. package/LICENSE +201 -0
  2. package/dist/cdcdashboard.js +131733 -123191
  3. package/examples/single-state-dashboard-filters.json +421 -0
  4. package/examples/state-level.json +90136 -0
  5. package/examples/state-points.json +10474 -0
  6. package/examples/test-file.json +147 -0
  7. package/examples/testing.json +94456 -0
  8. package/index.html +18 -6
  9. package/package.json +9 -9
  10. package/src/CdcDashboardComponent.tsx +154 -90
  11. package/src/DashboardContext.tsx +7 -1
  12. package/src/_stories/Dashboard.stories.tsx +124 -10
  13. package/src/_stories/_mock/api-filter-map.json +1 -1
  14. package/src/_stories/_mock/bump-chart.json +3554 -0
  15. package/src/_stories/_mock/methodology.json +412 -0
  16. package/src/_stories/_mock/methodologyAPI.ts +90 -0
  17. package/src/_stories/_mock/multi-viz.json +1 -1
  18. package/src/_stories/_mock/single-state-dashboard-filters.json +390 -0
  19. package/src/components/DashboardFilters/DashboardFilters.tsx +39 -17
  20. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +2 -2
  21. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +141 -31
  22. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +66 -18
  23. package/src/components/Header/Header.tsx +0 -5
  24. package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +20 -8
  25. package/src/components/Row.tsx +1 -1
  26. package/src/components/VisualizationRow.tsx +98 -17
  27. package/src/components/Widget.tsx +1 -0
  28. package/src/helpers/FilterBehavior.ts +4 -0
  29. package/src/helpers/addValuesToDashboardFilters.ts +49 -0
  30. package/src/helpers/apiFilterHelpers.ts +70 -18
  31. package/src/helpers/changeFilterActive.ts +17 -8
  32. package/src/helpers/getFilteredData.ts +4 -4
  33. package/src/helpers/iconHash.tsx +2 -0
  34. package/src/helpers/loadAPIFilters.ts +74 -0
  35. package/src/helpers/reloadURLHelpers.ts +41 -7
  36. package/src/helpers/tests/addValuesToDashboardFilters.test.ts +44 -0
  37. package/src/helpers/tests/apiFilterHelpers.test.ts +155 -0
  38. package/src/helpers/tests/getFilteredData.test.ts +86 -0
  39. package/src/helpers/tests/loadAPIFiltersWrapper.test.ts +220 -0
  40. package/src/helpers/tests/reloadURLHelpers.test.ts +232 -0
  41. package/src/types/SharedFilter.ts +2 -1
@@ -0,0 +1,44 @@
1
+ import { SharedFilter } from '../../types/SharedFilter'
2
+ import { addValuesToDashboardFilters } from '../addValuesToDashboardFilters'
3
+
4
+ describe('addValuesToDashboardFilters', () => {
5
+ const colA = { columnName: 'colA', id: 11, active: 'apple', values: [], type: 'datafilter' } as SharedFilter
6
+ const colB = { columnName: 'colB', id: 22, active: '1', values: [], type: 'datafilter' } as SharedFilter
7
+ const colC = { columnName: 'colC', id: 33, values: [], setByQueryParameter: 'colC', type: 'datafilter' } as SharedFilter
8
+
9
+ const data = {
10
+ key: [
11
+ { colA: 'apple', colB: 3, colC: 'abc' },
12
+ { colA: 'apple', colB: 1, colC: 'bcd' },
13
+ { colA: 'pear', colB: 4, colC: 'test' }
14
+ ]
15
+ }
16
+ const filters = [colA, colC, colB]
17
+ it('adds filter values', () => {
18
+ const newFilters = addValuesToDashboardFilters(filters, data)
19
+ expect(newFilters[0].values).toEqual(['apple', 'pear'])
20
+ })
21
+ it('converts to multiselect', () => {
22
+ colA.multiSelect = true
23
+ const newFilters = addValuesToDashboardFilters(filters, data)
24
+ expect(newFilters[0].active).toEqual(['apple'])
25
+ })
26
+
27
+ it('sets active value by query string', () => {
28
+ delete window.location
29
+ window.location = new URL('https://www.example.com?colC=test')
30
+ const newFilters = addValuesToDashboardFilters(filters, data)
31
+ expect(newFilters[1].active).toEqual('test')
32
+ })
33
+ const colA2 = { apiFilter: { valueSelector: 'colA' }, id: 11, active: 'apple', values: [], type: 'urlfilter' } as SharedFilter
34
+ const colB2 = { apiFilter: { valueSelector: 'colB' }, id: 22, active: '1', values: [], type: 'urlfilter' } as SharedFilter
35
+ const colC2 = { apiFilter: { valueSelector: 'colC' }, id: 33, values: [], setByQueryParameter: 'colC', type: 'urlfilter' } as SharedFilter
36
+ const filters2 = [colA2, colC2, colB2]
37
+ it('skips urlfilters', () => {
38
+ // urlfilter reloading happens in the dashboard in the loadAPIFilters function
39
+ delete window.location
40
+ window.location = new URL('https://www.example.com?colC=test')
41
+ const newFilters = addValuesToDashboardFilters(filters2, data)
42
+ expect(newFilters[1].active).toEqual(undefined)
43
+ })
44
+ })
@@ -0,0 +1,155 @@
1
+ import { setAutoLoadDefaultValue, getToFetch, getFilterValues, getLoadingFilterMemo } from '../apiFilterHelpers'
2
+ import _ from 'lodash'
3
+ import type { APIFilterDropdowns } from '../../components/DashboardFilters'
4
+
5
+ describe('getLoadingFilterMemo', () => {
6
+ it('should return correct APIFilterDropdowns for valid inputs', () => {
7
+ const sharedAPIFilters = ['endpoint1', 'endpoint2']
8
+ const apiFilterDropdowns: APIFilterDropdowns = {
9
+ endpoint1: { text: 'text1', value: 'value1' }
10
+ }
11
+ const expectedOutput: APIFilterDropdowns = {
12
+ endpoint1: { text: 'text1', value: 'value1' },
13
+ endpoint2: null
14
+ }
15
+ expect(getLoadingFilterMemo(sharedAPIFilters, apiFilterDropdowns)).toEqual(expectedOutput)
16
+ })
17
+
18
+ it('should return an empty object for empty sharedAPIFilters', () => {
19
+ const sharedAPIFilters: string[] = []
20
+ const apiFilterDropdowns: APIFilterDropdowns = {
21
+ endpoint1: { text: 'text1', value: 'value1' }
22
+ }
23
+ const expectedOutput: APIFilterDropdowns = {}
24
+ expect(getLoadingFilterMemo(sharedAPIFilters, apiFilterDropdowns)).toEqual(expectedOutput)
25
+ })
26
+
27
+ it('should return APIFilterDropdowns with null values for empty apiFilterDropdowns', () => {
28
+ const sharedAPIFilters = ['endpoint1', 'endpoint2']
29
+ const apiFilterDropdowns: APIFilterDropdowns = {}
30
+ const expectedOutput: APIFilterDropdowns = {
31
+ endpoint1: null,
32
+ endpoint2: null
33
+ }
34
+ expect(getLoadingFilterMemo(sharedAPIFilters, apiFilterDropdowns)).toEqual(expectedOutput)
35
+ })
36
+
37
+ it('should not overwrite existing data in apiFilterDropdowns, so return original dropdowns', () => {
38
+ const sharedAPIFilters = ['endpoint1', 'endpoint2']
39
+ const apiFilterDropdowns: APIFilterDropdowns = {
40
+ endpoint1: { text: 'text1', value: 'value1' },
41
+ endpoint2: { text: 'text2', value: 'value2' }
42
+ }
43
+ expect(getLoadingFilterMemo(sharedAPIFilters, apiFilterDropdowns)).toEqual(apiFilterDropdowns)
44
+ })
45
+ })
46
+
47
+ describe('getFilterValues', () => {
48
+ it('should return correct filter values for valid inputs', () => {
49
+ const data = [{ key1: 'value1', key2: 'value2' }]
50
+ const apiFilter = { textSelector: 'key1', valueSelector: 'key2' }
51
+ const expectedOutput = [{ text: 'value1', value: 'value2' }]
52
+ expect(getFilterValues(data, apiFilter)).toEqual(expectedOutput)
53
+ delete apiFilter.textSelector
54
+ const expectedOutput2 = [{ text: 'value2', value: 'value2' }]
55
+ expect(getFilterValues(data, apiFilter)).toEqual(expectedOutput2)
56
+ })
57
+ })
58
+
59
+ describe('getToFetch', () => {
60
+ it('should return an empty object when sharedAPIFilters is empty', () => {
61
+ const result = getToFetch([], {})
62
+ expect(result).toEqual({})
63
+ })
64
+
65
+ it('should return an object with endpoints when apiFilterDropdowns is empty', () => {
66
+ const sharedAPIFilters = [{ apiFilter: { apiEndpoint: '/endpoint1' }, parents: [] }]
67
+ const result = getToFetch(sharedAPIFilters, {})
68
+ expect(result).toEqual({ '/endpoint1': ['/endpoint1', 0] })
69
+ })
70
+
71
+ it('should return and empty object when sharedAPIFilters contains filters with no parents', () => {
72
+ const sharedAPIFilters = [{ apiFilter: { apiEndpoint: '/endpoint1' }, parents: [] }]
73
+ const apiFilterDropdowns = { '/endpoint1': true }
74
+ const result = getToFetch(sharedAPIFilters, apiFilterDropdowns)
75
+ expect(result).toEqual({})
76
+ })
77
+
78
+ it('should return an empty object when parentParams contains an empty value', () => {
79
+ const sharedAPIFilters = [
80
+ { key: 'parent1', apiFilter: { apiEndpoint: '/endpoint1' }, parents: [] },
81
+ { apiFilter: { apiEndpoint: '/endpoint1' }, parents: ['parent1'] }
82
+ ]
83
+ const apiFilterDropdowns = { '/endpoint1': true }
84
+ const result = getToFetch(sharedAPIFilters, apiFilterDropdowns)
85
+ expect(result).toEqual({})
86
+ })
87
+
88
+ it('should return an empty object when parentParams contains an empty value', () => {
89
+ const sharedAPIFilters = [
90
+ { key: 'parent1', value: '', apiFilter: { apiEndpoint: '/endpoint1' }, parents: [] },
91
+ { apiFilter: { apiEndpoint: '/endpoint1' }, parents: ['parent1'] }
92
+ ]
93
+ const apiFilterDropdowns = { '/endpoint1': true }
94
+ const result = getToFetch(sharedAPIFilters, apiFilterDropdowns)
95
+ expect(result).toEqual({})
96
+ })
97
+ })
98
+
99
+ describe('setAutoLoadDefaultValue', () => {
100
+ const dropdownOptions = [
101
+ { value: 'option1', label: 'Option 1' },
102
+ { value: 'option2', label: 'Option 2' }
103
+ ]
104
+
105
+ const sharedFilters = [
106
+ { key: 'filter1', active: null, queuedActive: null, parents: [] },
107
+ { key: 'filter2', active: null, queuedActive: null, parents: ['filter1'] }
108
+ ]
109
+
110
+ it('should return the original filter when autoLoadFilterIndexes is empty', () => {
111
+ const result = setAutoLoadDefaultValue(0, dropdownOptions, sharedFilters, [])
112
+ expect(result).toEqual(sharedFilters[0])
113
+ })
114
+
115
+ it('should return the original filter when dropdownOptions is empty', () => {
116
+ const result = setAutoLoadDefaultValue(0, [], sharedFilters, [0])
117
+ expect(result).toEqual(sharedFilters[0])
118
+ })
119
+
120
+ it('should return the original filter when dropdownOptions is undefined', () => {
121
+ const result = setAutoLoadDefaultValue(0, undefined, sharedFilters, [0])
122
+ expect(result).toEqual(sharedFilters[0])
123
+ })
124
+
125
+ it('should return the original filter when sharedFilterIndex is not in autoLoadFilterIndexes', () => {
126
+ const result = setAutoLoadDefaultValue(0, dropdownOptions, sharedFilters, [1])
127
+ expect(result).toEqual(sharedFilters[0])
128
+ })
129
+
130
+ it('should return the original filter when not all parent filters are selected', () => {
131
+ const result = setAutoLoadDefaultValue(1, dropdownOptions, sharedFilters, [1])
132
+ expect(result).toEqual(sharedFilters[1])
133
+ })
134
+
135
+ it('should assign the default value from dropdownOptions when no active value is set', () => {
136
+ const sharedFiltersCopy = _.cloneDeep(sharedFilters)
137
+ sharedFiltersCopy[0].active = null
138
+ const result = setAutoLoadDefaultValue(0, dropdownOptions, sharedFiltersCopy, [0])
139
+ expect(result.active).toEqual('option1')
140
+ })
141
+
142
+ it('should retain the current active value if it exists in dropdownOptions', () => {
143
+ const sharedFiltersCopy = _.cloneDeep(sharedFilters)
144
+ sharedFiltersCopy[0].active = 'option1'
145
+ const result = setAutoLoadDefaultValue(0, dropdownOptions, sharedFiltersCopy, [0])
146
+ expect(result.active).toEqual('option1')
147
+ })
148
+
149
+ it('should assign the default value if the current active value does not exist in dropdownOptions', () => {
150
+ const sharedFiltersCopy = _.cloneDeep(sharedFilters)
151
+ sharedFiltersCopy[0].active = 'nonexistent'
152
+ const result = setAutoLoadDefaultValue(0, dropdownOptions, sharedFiltersCopy, [0])
153
+ expect(result.active).toEqual('option1')
154
+ })
155
+ })
@@ -0,0 +1,86 @@
1
+ import _ from 'lodash'
2
+ import { SharedFilter } from '../../types/SharedFilter'
3
+ import { getFilteredData } from '../getFilteredData'
4
+
5
+ describe('getFilteredData', () => {
6
+ const sharedFilterDefaults = { values: [], showDropdown: true, id: 123, parents: [], key: 'key' }
7
+ const data = {
8
+ data1: [
9
+ { id: 1, name: 'Alice', age: 25 },
10
+ { id: 2, name: 'Bob', age: 30 },
11
+ { id: 3, name: 'Charlie', age: 35 }
12
+ ]
13
+ }
14
+ const state = {
15
+ data,
16
+ config: {
17
+ dashboard: {
18
+ sharedFilters: []
19
+ },
20
+ visualizations: {
21
+ vizA: { dataKey: 'data1' }
22
+ },
23
+ rows: [{ dataKey: 'data1' }]
24
+ }
25
+ }
26
+
27
+ it('should apply data to rows when there are no applicable filters', () => {
28
+ expect(getFilteredData(state)).toEqual({ '0': data.data1 })
29
+ })
30
+
31
+ it('should filter visualizations', () => {
32
+ const sharedFilters: SharedFilter[] = [
33
+ {
34
+ usedBy: ['vizA'],
35
+ active: 'Alice',
36
+ columnName: 'name',
37
+ ...sharedFilterDefaults
38
+ }
39
+ ]
40
+ const config = { ...state.config, dashboard: { sharedFilters } }
41
+ expect(getFilteredData({ ...state, config })).toEqual({ '0': data.data1, vizA: [data.data1[0]] })
42
+ })
43
+
44
+ it('should filter visualizations and rows', () => {
45
+ const sharedFilters: SharedFilter[] = [
46
+ {
47
+ usedBy: ['vizA', '0'],
48
+ active: 'Alice',
49
+ columnName: 'name',
50
+ ...sharedFilterDefaults
51
+ }
52
+ ]
53
+ const config = { ...state.config, dashboard: { sharedFilters } }
54
+ expect(getFilteredData({ ...state, config })).toEqual({ '0': [data.data1[0]], vizA: [data.data1[0]] })
55
+ })
56
+
57
+ it('should use initialFilteredData', () => {
58
+ const initialFilteredData = { newData: [data.data1[1]] }
59
+ const sharedFilters: SharedFilter[] = [
60
+ {
61
+ usedBy: ['vizA', '0'],
62
+ active: 'Alice',
63
+ columnName: 'name',
64
+ ...sharedFilterDefaults
65
+ }
66
+ ]
67
+ const config = { ...state.config, dashboard: { sharedFilters } }
68
+ expect(getFilteredData({ ...state, config }, initialFilteredData)).toEqual({ newData: [data.data1[1]], '0': [data.data1[0]], vizA: [data.data1[0]] })
69
+ })
70
+
71
+ it('should filter visualizations and rows', () => {
72
+ const sharedFilters: SharedFilter[] = [
73
+ {
74
+ usedBy: ['vizA', '0'],
75
+ active: 'Alice',
76
+ columnName: 'name',
77
+ ...sharedFilterDefaults
78
+ }
79
+ ]
80
+ const dataOverride = _.cloneDeep(data)
81
+ dataOverride.data1[0] = { id: 1, name: 'Alice', age: 30 }
82
+ const config = { ...state.config, dashboard: { sharedFilters } }
83
+ const filteredData = getFilteredData({ ...state, config }, undefined, dataOverride)
84
+ expect(filteredData.vizA[0].age).toEqual(30)
85
+ })
86
+ })
@@ -0,0 +1,220 @@
1
+ import { vi } from 'vitest'
2
+ import { loadAPIFiltersFactory } from '../loadAPIFilters'
3
+ import { faker } from '@faker-js/faker'
4
+ import _ from 'lodash'
5
+ import { SharedFilter } from '../../types/SharedFilter'
6
+
7
+ faker.seed(123)
8
+
9
+ const endpointMockData = {
10
+ 'cdc.gov/filters/Sex': [
11
+ { Sex: 'male', Abbreviation: 'M' },
12
+ { Sex: 'female', Abbreviation: 'F' }
13
+ ],
14
+ 'cdc.gov/filters/Quarter': [{ Quarter: 'Q1' }, { Quarter: 'Q2' }, { Quarter: 'Q3' }, { Quarter: 'Q4' }],
15
+ 'cdc.gov/filters/YearQuarter': [
16
+ { Year: 2020, YearQuarter: '2020Q1' },
17
+ { Year: 2021, YearQuarter: '2021Q1' }
18
+ ]
19
+ }
20
+
21
+ const sharedFilters = [
22
+ {
23
+ key: 'Sex',
24
+ active: 'F',
25
+ showDropdown: true,
26
+ type: 'urlfilter',
27
+ apiFilter: {
28
+ apiEndpoint: 'cdc.gov/filters/Sex',
29
+ valueSelector: 'Abbreviation',
30
+ textSelector: 'Sex'
31
+ }
32
+ },
33
+ {
34
+ key: 'Quarter',
35
+ showDropdown: true,
36
+ type: 'urlfilter',
37
+ apiFilter: {
38
+ apiEndpoint: 'cdc.gov/filters/Quarter',
39
+ valueSelector: 'Quarter',
40
+ textSelector: ''
41
+ },
42
+ parents: ['Sex']
43
+ },
44
+ {
45
+ key: 'YearQuarter',
46
+ showDropdown: true,
47
+ type: 'urlfilter',
48
+ apiFilter: {
49
+ apiEndpoint: 'cdc.gov/filters/YearQuarter',
50
+ valueSelector: 'Year',
51
+ textSelector: 'YearQuarter'
52
+ },
53
+ parents: ['Sex', 'Quarter'],
54
+ filterStyle: 'multi-select'
55
+ }
56
+ ] as SharedFilter[]
57
+
58
+ const fetch = vi.fn(endpoint => {
59
+ const baseEndpoint = endpoint.split('?')[0]
60
+ return Promise.resolve({
61
+ json: () => Promise.resolve(endpointMockData[baseEndpoint])
62
+ })
63
+ })
64
+
65
+ global.fetch = fetch
66
+
67
+ describe('loadAPIFiltersFactory', () => {
68
+ const dispatch = vi.fn()
69
+ const setAPIFilterDropdowns = vi.fn()
70
+ const apiFilterDropdowns = {
71
+ 'cdc.gov/filters/Sex': [
72
+ { text: 'male', value: 'M' },
73
+ { text: 'female', value: 'F' }
74
+ ]
75
+ }
76
+ afterEach(() => {
77
+ vi.restoreAllMocks()
78
+ })
79
+ const loadAPIFilters = loadAPIFiltersFactory(dispatch, setAPIFilterDropdowns, [2])
80
+ it('creates a function', () => {
81
+ expect(typeof loadAPIFilters).toEqual('function')
82
+ })
83
+
84
+ it('loadAPIFilters() should return a promise', async () => {
85
+ const result = loadAPIFilters([], apiFilterDropdowns)
86
+ expect(result).toBeInstanceOf(Promise)
87
+ await result
88
+ })
89
+
90
+ it('loadAPIFilters() load dropdowns for children when parent is selected', async () => {
91
+ const newSharedFilters = await loadAPIFilters(sharedFilters, apiFilterDropdowns)
92
+ expect(dispatch).toHaveBeenCalledTimes(1)
93
+ expect(setAPIFilterDropdowns).toHaveBeenCalledTimes(1)
94
+
95
+ const expectedDropdowns = {
96
+ 'cdc.gov/filters/Quarter': [
97
+ {
98
+ text: 'Q1',
99
+ value: 'Q1'
100
+ },
101
+ {
102
+ text: 'Q2',
103
+ value: 'Q2'
104
+ },
105
+ {
106
+ text: 'Q3',
107
+ value: 'Q3'
108
+ },
109
+ {
110
+ text: 'Q4',
111
+ value: 'Q4'
112
+ }
113
+ ],
114
+ 'cdc.gov/filters/Sex': [
115
+ {
116
+ text: 'male',
117
+ value: 'M'
118
+ },
119
+ {
120
+ text: 'female',
121
+ value: 'F'
122
+ }
123
+ ]
124
+ }
125
+
126
+ expect(setAPIFilterDropdowns).toHaveBeenCalledWith(expectedDropdowns)
127
+ expect(fetch).toHaveBeenCalledTimes(1)
128
+ expect(newSharedFilters).toEqual(sharedFilters)
129
+ })
130
+
131
+ it('loadAPIFilters() load dropdowns for children when parents are selected, and autoload any autoloading filters', async () => {
132
+ sharedFilters[1].active = 'Q1'
133
+ const newSharedFilters = await loadAPIFilters(sharedFilters, apiFilterDropdowns)
134
+ expect(dispatch).toHaveBeenCalledTimes(1)
135
+ expect(setAPIFilterDropdowns).toHaveBeenCalledTimes(1)
136
+
137
+ const expectedDropdowns = {
138
+ 'cdc.gov/filters/Quarter': [
139
+ {
140
+ text: 'Q1',
141
+ value: 'Q1'
142
+ },
143
+ {
144
+ text: 'Q2',
145
+ value: 'Q2'
146
+ },
147
+ {
148
+ text: 'Q3',
149
+ value: 'Q3'
150
+ },
151
+ {
152
+ text: 'Q4',
153
+ value: 'Q4'
154
+ }
155
+ ],
156
+ 'cdc.gov/filters/Sex': [
157
+ {
158
+ text: 'male',
159
+ value: 'M'
160
+ },
161
+ {
162
+ text: 'female',
163
+ value: 'F'
164
+ }
165
+ ],
166
+ 'cdc.gov/filters/YearQuarter': [
167
+ { value: 2020, text: '2020Q1' },
168
+ { value: 2021, text: '2021Q1' }
169
+ ]
170
+ }
171
+
172
+ expect(setAPIFilterDropdowns).toHaveBeenCalledWith(expectedDropdowns)
173
+ expect(fetch).toHaveBeenCalledTimes(2)
174
+ expect(newSharedFilters[2].active).toEqual([2020])
175
+ })
176
+ it('returns new active values for multiselect', async () => {
177
+ const _sharedFilters = _.cloneDeep(sharedFilters)
178
+ _sharedFilters[1].active = 'Q1'
179
+ _sharedFilters[2].active = ['2020', '2021']
180
+ const apiDropdownsLoaded = {
181
+ 'cdc.gov/filters/Quarter': [
182
+ {
183
+ text: 'Q1',
184
+ value: 'Q1'
185
+ },
186
+ {
187
+ text: 'Q2',
188
+ value: 'Q2'
189
+ },
190
+ {
191
+ text: 'Q3',
192
+ value: 'Q3'
193
+ },
194
+ {
195
+ text: 'Q4',
196
+ value: 'Q4'
197
+ }
198
+ ],
199
+ 'cdc.gov/filters/Sex': [
200
+ {
201
+ text: 'male',
202
+ value: 'M'
203
+ },
204
+ {
205
+ text: 'female',
206
+ value: 'F'
207
+ }
208
+ ],
209
+ 'cdc.gov/filters/YearQuarter': [
210
+ { value: '2020', text: '2020Q1' },
211
+ { value: '2021', text: '2021Q1' }
212
+ ]
213
+ }
214
+ const newSharedFilters = await loadAPIFilters(_sharedFilters, apiDropdownsLoaded)
215
+ expect(dispatch).toHaveBeenCalledTimes(1)
216
+ expect(setAPIFilterDropdowns).toHaveBeenCalledTimes(1)
217
+
218
+ expect(newSharedFilters[2].active).toEqual(['2020', '2021'])
219
+ })
220
+ })