@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.
- package/LICENSE +201 -0
- package/dist/cdcdashboard.js +131733 -123191
- package/examples/single-state-dashboard-filters.json +421 -0
- package/examples/state-level.json +90136 -0
- package/examples/state-points.json +10474 -0
- package/examples/test-file.json +147 -0
- package/examples/testing.json +94456 -0
- package/index.html +18 -6
- package/package.json +9 -9
- package/src/CdcDashboardComponent.tsx +154 -90
- package/src/DashboardContext.tsx +7 -1
- package/src/_stories/Dashboard.stories.tsx +124 -10
- package/src/_stories/_mock/api-filter-map.json +1 -1
- package/src/_stories/_mock/bump-chart.json +3554 -0
- package/src/_stories/_mock/methodology.json +412 -0
- package/src/_stories/_mock/methodologyAPI.ts +90 -0
- package/src/_stories/_mock/multi-viz.json +1 -1
- package/src/_stories/_mock/single-state-dashboard-filters.json +390 -0
- package/src/components/DashboardFilters/DashboardFilters.tsx +39 -17
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +2 -2
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +141 -31
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +66 -18
- package/src/components/Header/Header.tsx +0 -5
- package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +20 -8
- package/src/components/Row.tsx +1 -1
- package/src/components/VisualizationRow.tsx +98 -17
- package/src/components/Widget.tsx +1 -0
- package/src/helpers/FilterBehavior.ts +4 -0
- package/src/helpers/addValuesToDashboardFilters.ts +49 -0
- package/src/helpers/apiFilterHelpers.ts +70 -18
- package/src/helpers/changeFilterActive.ts +17 -8
- package/src/helpers/getFilteredData.ts +4 -4
- package/src/helpers/iconHash.tsx +2 -0
- package/src/helpers/loadAPIFilters.ts +74 -0
- package/src/helpers/reloadURLHelpers.ts +41 -7
- package/src/helpers/tests/addValuesToDashboardFilters.test.ts +44 -0
- package/src/helpers/tests/apiFilterHelpers.test.ts +155 -0
- package/src/helpers/tests/getFilteredData.test.ts +86 -0
- package/src/helpers/tests/loadAPIFiltersWrapper.test.ts +220 -0
- package/src/helpers/tests/reloadURLHelpers.test.ts +232 -0
- 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
|
+
})
|