@cdc/dashboard 4.26.4 → 4.26.5
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/CONFIG.md +77 -30
- package/LICENSE +201 -0
- package/dist/cdcdashboard.js +49936 -49166
- package/examples/dashboard-conditions-filters-incomplete.json +221 -0
- package/examples/dashboard-missing-datasets-multi.json +174 -0
- package/examples/dashboard-missing-datasets-single.json +121 -0
- package/examples/dashboard-multi-dashboard-version-regression.json +146 -0
- package/examples/dashboard-shared-filter-row-delete-cleanup.json +186 -0
- package/examples/dashboard-stale-dataset-keys.json +181 -0
- package/examples/dashboard-tiered-filter-regression.json +190 -0
- package/examples/private/cfa-dashboard.json +651 -0
- package/examples/private/data-bite-wrap.json +6936 -0
- package/examples/private/multi-dash-fix.json +16963 -0
- package/examples/private/versions.json +41612 -0
- package/examples/us-map-filter-example.json +1074 -0
- package/package.json +9 -9
- package/src/CdcDashboard.tsx +6 -2
- package/src/CdcDashboardComponent.tsx +178 -87
- package/src/DashboardCopyPasteContext.test.tsx +33 -0
- package/src/DashboardCopyPasteContext.tsx +48 -0
- package/src/_stories/Dashboard.EditorRegression.stories.tsx +72 -0
- package/src/_stories/Dashboard.Regression.stories.tsx +196 -0
- package/src/_stories/Dashboard.Zoom.stories.tsx +88 -0
- package/src/_stories/Dashboard.stories.tsx +294 -0
- package/src/_stories/FilteredTextMigrationComparison.stories.tsx +87 -0
- package/src/components/Column.test.tsx +176 -0
- package/src/components/Column.tsx +214 -13
- package/src/components/DashboardConditionModal.test.tsx +420 -0
- package/src/components/DashboardConditionModal.tsx +367 -0
- package/src/components/DashboardConditionSummary.tsx +59 -0
- package/src/components/DashboardEditors.tsx +8 -0
- package/src/components/DashboardFilters/DashboardFilters.test.tsx +139 -1
- package/src/components/DashboardFilters/DashboardFilters.tsx +192 -174
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.test.tsx +164 -0
- package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +41 -2
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.test.tsx +180 -3
- package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +15 -32
- package/src/components/DashboardFilters/DashboardFiltersWrapper.test.tsx +142 -0
- package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +32 -27
- package/src/components/DashboardFilters/dashboardfilter.styles.css +42 -27
- package/src/components/DataDesignerModal.tsx +2 -1
- package/src/components/Grid.tsx +8 -4
- package/src/components/Header/Header.tsx +36 -17
- package/src/components/Row.test.tsx +228 -0
- package/src/components/Row.tsx +93 -18
- package/src/components/VisualizationRow.test.tsx +396 -0
- package/src/components/VisualizationRow.tsx +110 -35
- package/src/components/VisualizationsPanel/VisualizationsPanel.test.tsx +49 -0
- package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +14 -13
- package/src/components/Widget/Widget.test.tsx +218 -0
- package/src/components/Widget/Widget.tsx +119 -17
- package/src/components/Widget/widget.styles.css +31 -18
- package/src/components/dashboard-condition-modal.css +76 -0
- package/src/components/dashboard-condition-summary.css +87 -0
- package/src/helpers/addValuesToDashboardFilters.ts +3 -5
- package/src/helpers/addVisualization.ts +15 -4
- package/src/helpers/cloneDashboardWidget.ts +127 -0
- package/src/helpers/dashboardColumnWidgets.ts +99 -0
- package/src/helpers/dashboardConditionUi.ts +47 -0
- package/src/helpers/dashboardConditions.ts +200 -0
- package/src/helpers/dashboardFilterTargets.ts +156 -0
- package/src/helpers/filterData.ts +4 -9
- package/src/helpers/filterVisibility.ts +20 -0
- package/src/helpers/formatConfigBeforeSave.ts +2 -2
- package/src/helpers/getFilteredData.ts +18 -5
- package/src/helpers/getUpdateConfig.ts +43 -12
- package/src/helpers/getVizRowColumnLocator.ts +11 -1
- package/src/helpers/iconHash.tsx +9 -3
- package/src/helpers/mapDataToConfig.ts +31 -29
- package/src/helpers/reloadURLHelpers.ts +25 -5
- package/src/helpers/removeDashboardFilter.ts +33 -33
- package/src/helpers/tests/addVisualization.test.ts +53 -9
- package/src/helpers/tests/cloneDashboardWidget.test.ts +136 -0
- package/src/helpers/tests/dashboardColumnWidgets.test.ts +99 -0
- package/src/helpers/tests/dashboardConditionUi.test.ts +41 -0
- package/src/helpers/tests/dashboardConditions.test.ts +428 -0
- package/src/helpers/tests/formatConfigBeforeSave.test.ts +51 -0
- package/src/helpers/tests/getFilteredData.test.ts +265 -86
- package/src/helpers/tests/getUpdateConfig.test.ts +338 -0
- package/src/helpers/tests/reloadURLHelpers.test.ts +394 -238
- package/src/index.tsx +6 -3
- package/src/scss/grid.scss +249 -20
- package/src/scss/main.scss +108 -29
- package/src/store/dashboard.actions.ts +17 -4
- package/src/store/dashboard.reducer.test.ts +538 -0
- package/src/store/dashboard.reducer.ts +135 -22
- package/src/test/CdcDashboard.test.tsx +148 -0
- package/src/test/CdcDashboardComponent.test.tsx +935 -2
- package/src/types/ConfigRow.ts +15 -0
- package/src/types/DashboardFilters.ts +4 -0
- package/src/types/SharedFilter.ts +1 -0
|
@@ -1,86 +1,265 @@
|
|
|
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({
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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({
|
|
69
|
+
newData: [data.data1[1]],
|
|
70
|
+
'0': [data.data1[0]],
|
|
71
|
+
vizA: [data.data1[0]]
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should filter visualizations and rows', () => {
|
|
76
|
+
const sharedFilters: SharedFilter[] = [
|
|
77
|
+
{
|
|
78
|
+
usedBy: ['vizA', '0'],
|
|
79
|
+
active: 'Alice',
|
|
80
|
+
columnName: 'name',
|
|
81
|
+
...sharedFilterDefaults
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
const dataOverride = _.cloneDeep(data)
|
|
85
|
+
dataOverride.data1[0] = { id: 1, name: 'Alice', age: 30 }
|
|
86
|
+
const config = { ...state.config, dashboard: { sharedFilters } }
|
|
87
|
+
const filteredData = getFilteredData({ ...state, config }, undefined, dataOverride)
|
|
88
|
+
expect(filteredData.vizA[0].age).toEqual(30)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('should apply shared filters with missing usedBy to visualizations and rows', () => {
|
|
92
|
+
const sharedFilters: SharedFilter[] = [
|
|
93
|
+
{
|
|
94
|
+
active: 'Alice',
|
|
95
|
+
columnName: 'name',
|
|
96
|
+
...sharedFilterDefaults
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
const config = { ...state.config, dashboard: { sharedFilters } }
|
|
100
|
+
|
|
101
|
+
expect(getFilteredData({ ...state, config })).toEqual({ '0': [data.data1[0]], vizA: [data.data1[0]] })
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should apply shared filters with empty usedBy to visualizations and rows', () => {
|
|
105
|
+
const sharedFilters: SharedFilter[] = [
|
|
106
|
+
{
|
|
107
|
+
usedBy: [],
|
|
108
|
+
active: 'Alice',
|
|
109
|
+
columnName: 'name',
|
|
110
|
+
...sharedFilterDefaults
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
const config = { ...state.config, dashboard: { sharedFilters } }
|
|
114
|
+
|
|
115
|
+
expect(getFilteredData({ ...state, config })).toEqual({ '0': [data.data1[0]], vizA: [data.data1[0]] })
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should keep explicit usedBy filters scoped to matching visualization and row targets', () => {
|
|
119
|
+
const sharedFilters: SharedFilter[] = [
|
|
120
|
+
{
|
|
121
|
+
usedBy: ['vizA'],
|
|
122
|
+
active: 'Alice',
|
|
123
|
+
columnName: 'name',
|
|
124
|
+
...sharedFilterDefaults
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
usedBy: [0],
|
|
128
|
+
active: 'Bob',
|
|
129
|
+
columnName: 'name',
|
|
130
|
+
...sharedFilterDefaults
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
const config = { ...state.config, dashboard: { sharedFilters } }
|
|
134
|
+
|
|
135
|
+
expect(getFilteredData({ ...state, config })).toEqual({ '0': [data.data1[1]], vizA: [data.data1[0]] })
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('should precompute dashboard condition targets using row owner filters when data is configured on the row', () => {
|
|
139
|
+
const config = {
|
|
140
|
+
...state.config,
|
|
141
|
+
rows: [
|
|
142
|
+
{
|
|
143
|
+
dataKey: 'data1',
|
|
144
|
+
dashboardCondition: {
|
|
145
|
+
id: 'row-condition-1',
|
|
146
|
+
datasetKey: 'data1',
|
|
147
|
+
operator: 'hasData'
|
|
148
|
+
},
|
|
149
|
+
columns: [
|
|
150
|
+
{
|
|
151
|
+
width: 12,
|
|
152
|
+
conditionalWidgets: [
|
|
153
|
+
{
|
|
154
|
+
widget: 'vizA',
|
|
155
|
+
dashboardCondition: {
|
|
156
|
+
id: 'column-condition-1',
|
|
157
|
+
datasetKey: 'data1',
|
|
158
|
+
operator: 'columnHasAnyValue',
|
|
159
|
+
columnName: 'name',
|
|
160
|
+
values: ['Alice']
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
dashboard: {
|
|
169
|
+
sharedFilters: [
|
|
170
|
+
{
|
|
171
|
+
active: 'Alice',
|
|
172
|
+
columnName: 'name',
|
|
173
|
+
usedBy: [0],
|
|
174
|
+
...sharedFilterDefaults
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
expect(getFilteredData({ ...state, config } as any)).toEqual({
|
|
181
|
+
'0': [data.data1[0]],
|
|
182
|
+
'row-condition-1': [data.data1[0]],
|
|
183
|
+
'column-condition-1': [data.data1[0]]
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should precompute component condition targets using widget owner filters when data is configured on the widget', () => {
|
|
188
|
+
const config = {
|
|
189
|
+
...state.config,
|
|
190
|
+
rows: [
|
|
191
|
+
{
|
|
192
|
+
columns: [
|
|
193
|
+
{
|
|
194
|
+
width: 12,
|
|
195
|
+
conditionalWidgets: [
|
|
196
|
+
{
|
|
197
|
+
widget: 'vizA',
|
|
198
|
+
dashboardCondition: {
|
|
199
|
+
id: 'column-condition-1',
|
|
200
|
+
datasetKey: 'data1',
|
|
201
|
+
operator: 'columnHasAnyValue',
|
|
202
|
+
columnName: 'name',
|
|
203
|
+
values: ['Alice']
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
dashboard: {
|
|
212
|
+
sharedFilters: [
|
|
213
|
+
{
|
|
214
|
+
active: 'Alice',
|
|
215
|
+
columnName: 'name',
|
|
216
|
+
usedBy: ['vizA'],
|
|
217
|
+
...sharedFilterDefaults
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
expect(getFilteredData({ ...state, config } as any)).toEqual({
|
|
224
|
+
'column-condition-1': [data.data1[0]],
|
|
225
|
+
vizA: [data.data1[0]]
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('should clear stale dashboard condition data when a condition becomes unresolved', () => {
|
|
230
|
+
const config = {
|
|
231
|
+
...state.config,
|
|
232
|
+
rows: [
|
|
233
|
+
{
|
|
234
|
+
dataKey: 'data1',
|
|
235
|
+
dashboardCondition: {
|
|
236
|
+
id: 'row-condition-1',
|
|
237
|
+
datasetKey: 'data1',
|
|
238
|
+
operator: 'hasData'
|
|
239
|
+
},
|
|
240
|
+
columns: [{ width: 12, widget: 'vizA' }]
|
|
241
|
+
}
|
|
242
|
+
],
|
|
243
|
+
dashboard: {
|
|
244
|
+
sharedFilters: [
|
|
245
|
+
{
|
|
246
|
+
active: '',
|
|
247
|
+
columnName: 'name',
|
|
248
|
+
usedBy: [0],
|
|
249
|
+
...sharedFilterDefaults
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const initialFilteredData = {
|
|
256
|
+
unrelatedCacheEntry: [data.data1[1]],
|
|
257
|
+
'row-condition-1': [data.data1[0]]
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
expect(getFilteredData({ ...state, config } as any, initialFilteredData)).toEqual({
|
|
261
|
+
'0': [],
|
|
262
|
+
unrelatedCacheEntry: [data.data1[1]]
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
})
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { SharedFilter } from '../../types/SharedFilter'
|
|
2
|
+
import { getUpdateConfig } from '../getUpdateConfig'
|
|
3
|
+
|
|
4
|
+
describe('getUpdateConfig', () => {
|
|
5
|
+
const sharedFilterDefaults = { values: [], showDropdown: true, id: 123, parents: [], key: 'key' }
|
|
6
|
+
const data = {
|
|
7
|
+
data1: [
|
|
8
|
+
{ id: 1, name: 'Alice', age: 25 },
|
|
9
|
+
{ id: 2, name: 'Bob', age: 30 },
|
|
10
|
+
{ id: 3, name: 'Charlie', age: 35 }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const getConfig = (sharedFilters: SharedFilter[]) =>
|
|
15
|
+
({
|
|
16
|
+
dashboard: { sharedFilters },
|
|
17
|
+
datasets: {
|
|
18
|
+
data1: { data: data.data1 }
|
|
19
|
+
},
|
|
20
|
+
visualizations: {
|
|
21
|
+
vizA: { dataKey: 'data1' }
|
|
22
|
+
},
|
|
23
|
+
rows: [{ dataKey: 'data1', data: data.data1, columns: [] }, { columns: [{ width: 12, widget: 'vizA' }] }]
|
|
24
|
+
} as any)
|
|
25
|
+
|
|
26
|
+
const getFilteredData = (sharedFilters: SharedFilter[]) => {
|
|
27
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(getConfig(sharedFilters))
|
|
28
|
+
return filteredData
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
it('should precompute visualization and row data for shared filters with missing usedBy', () => {
|
|
32
|
+
const sharedFilters: SharedFilter[] = [
|
|
33
|
+
{
|
|
34
|
+
active: 'Alice',
|
|
35
|
+
columnName: 'name',
|
|
36
|
+
...sharedFilterDefaults
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
expect(getFilteredData(sharedFilters)).toEqual({ '0': [data.data1[0]], vizA: [data.data1[0]] })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should precompute visualization and row data for shared filters with empty usedBy', () => {
|
|
44
|
+
const sharedFilters: SharedFilter[] = [
|
|
45
|
+
{
|
|
46
|
+
usedBy: [],
|
|
47
|
+
active: 'Alice',
|
|
48
|
+
columnName: 'name',
|
|
49
|
+
...sharedFilterDefaults
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
expect(getFilteredData(sharedFilters)).toEqual({ '0': [data.data1[0]], vizA: [data.data1[0]] })
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should keep explicit usedBy filters scoped during precompute', () => {
|
|
57
|
+
const sharedFilters: SharedFilter[] = [
|
|
58
|
+
{
|
|
59
|
+
usedBy: ['vizA'],
|
|
60
|
+
active: 'Alice',
|
|
61
|
+
columnName: 'name',
|
|
62
|
+
...sharedFilterDefaults
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
usedBy: [0],
|
|
66
|
+
active: 'Bob',
|
|
67
|
+
columnName: 'name',
|
|
68
|
+
...sharedFilterDefaults
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
expect(getFilteredData(sharedFilters)).toEqual({ '0': [data.data1[1]], vizA: [data.data1[0]] })
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should not precompute visualization data when the visualization uses row-level data', () => {
|
|
76
|
+
const sharedFilters: SharedFilter[] = [
|
|
77
|
+
{
|
|
78
|
+
active: 'Alice',
|
|
79
|
+
columnName: 'name',
|
|
80
|
+
...sharedFilterDefaults
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
const config = {
|
|
84
|
+
...getConfig(sharedFilters),
|
|
85
|
+
rows: [{ dataKey: 'data1', data: data.data1, columns: [{ width: 12, widget: 'vizA' }] }]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(config)
|
|
89
|
+
|
|
90
|
+
expect(filteredData).toEqual({ '0': [data.data1[0]] })
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should not create row cache entries for unscoped filters on visualization-data rows', () => {
|
|
94
|
+
const sharedFilters: SharedFilter[] = [
|
|
95
|
+
{
|
|
96
|
+
active: 'Alice',
|
|
97
|
+
columnName: 'name',
|
|
98
|
+
...sharedFilterDefaults
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(getConfig(sharedFilters))
|
|
103
|
+
|
|
104
|
+
expect(filteredData).toHaveProperty('vizA', [data.data1[0]])
|
|
105
|
+
expect(filteredData).not.toHaveProperty('1')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should precompute row data from row.dataKey when row data is not stored by row index', () => {
|
|
109
|
+
const rowData = [
|
|
110
|
+
{ id: 1, measure: 'Coverage', location: 'Alabama' },
|
|
111
|
+
{ id: 2, measure: 'Barriers', location: 'Georgia' }
|
|
112
|
+
]
|
|
113
|
+
const dataKey = 'private-multiviz-row-data'
|
|
114
|
+
const config = {
|
|
115
|
+
dashboard: {
|
|
116
|
+
sharedFilters: [
|
|
117
|
+
{
|
|
118
|
+
active: 'Alabama',
|
|
119
|
+
columnName: 'location',
|
|
120
|
+
...sharedFilterDefaults
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
datasets: {
|
|
125
|
+
[dataKey]: {}
|
|
126
|
+
},
|
|
127
|
+
visualizations: {
|
|
128
|
+
vizA: { dataKey }
|
|
129
|
+
},
|
|
130
|
+
rows: [
|
|
131
|
+
{ columns: [] },
|
|
132
|
+
{ columns: [] },
|
|
133
|
+
{ columns: [] },
|
|
134
|
+
{
|
|
135
|
+
dataKey,
|
|
136
|
+
multiVizColumn: 'measure',
|
|
137
|
+
columns: [{ width: 12, widget: 'vizA' }]
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const [, filteredData] = getUpdateConfig({
|
|
143
|
+
data: { [dataKey]: rowData },
|
|
144
|
+
filteredData: {}
|
|
145
|
+
} as any)(config)
|
|
146
|
+
|
|
147
|
+
expect(filteredData).toEqual({ '3': [rowData[0]] })
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should precompute row dashboard conditions using row owner filters', () => {
|
|
151
|
+
const sharedFilters: SharedFilter[] = [
|
|
152
|
+
{
|
|
153
|
+
usedBy: [0],
|
|
154
|
+
active: 'Alice',
|
|
155
|
+
columnName: 'name',
|
|
156
|
+
...sharedFilterDefaults
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
const config = {
|
|
160
|
+
...getConfig(sharedFilters),
|
|
161
|
+
rows: [
|
|
162
|
+
{
|
|
163
|
+
dataKey: 'data1',
|
|
164
|
+
data: data.data1,
|
|
165
|
+
dashboardCondition: {
|
|
166
|
+
id: 'row-condition-1',
|
|
167
|
+
datasetKey: 'data1',
|
|
168
|
+
operator: 'hasData'
|
|
169
|
+
},
|
|
170
|
+
columns: []
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(config)
|
|
176
|
+
|
|
177
|
+
expect(filteredData).toEqual({
|
|
178
|
+
'0': [data.data1[0]],
|
|
179
|
+
'row-condition-1': [data.data1[0]]
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('should precompute component conditions using widget owner filters when data is configured on the widget', () => {
|
|
184
|
+
const sharedFilters: SharedFilter[] = [
|
|
185
|
+
{
|
|
186
|
+
usedBy: ['vizA'],
|
|
187
|
+
active: 'Alice',
|
|
188
|
+
columnName: 'name',
|
|
189
|
+
...sharedFilterDefaults
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
const config = {
|
|
193
|
+
...getConfig(sharedFilters),
|
|
194
|
+
rows: [
|
|
195
|
+
{
|
|
196
|
+
columns: [
|
|
197
|
+
{
|
|
198
|
+
width: 12,
|
|
199
|
+
conditionalWidgets: [
|
|
200
|
+
{
|
|
201
|
+
widget: 'vizA',
|
|
202
|
+
dashboardCondition: {
|
|
203
|
+
id: 'component-condition-1',
|
|
204
|
+
datasetKey: 'data1',
|
|
205
|
+
operator: 'columnHasAnyValue',
|
|
206
|
+
columnName: 'name',
|
|
207
|
+
values: ['Alice']
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(config)
|
|
218
|
+
|
|
219
|
+
expect(filteredData).toEqual({
|
|
220
|
+
vizA: [data.data1[0]],
|
|
221
|
+
'component-condition-1': [data.data1[0]]
|
|
222
|
+
})
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('should precompute component conditions using row owner filters when data is configured on the row', () => {
|
|
226
|
+
const sharedFilters: SharedFilter[] = [
|
|
227
|
+
{
|
|
228
|
+
usedBy: [0],
|
|
229
|
+
active: 'Bob',
|
|
230
|
+
columnName: 'name',
|
|
231
|
+
...sharedFilterDefaults
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
const config = {
|
|
235
|
+
...getConfig(sharedFilters),
|
|
236
|
+
rows: [
|
|
237
|
+
{
|
|
238
|
+
dataKey: 'data1',
|
|
239
|
+
data: data.data1,
|
|
240
|
+
columns: [
|
|
241
|
+
{
|
|
242
|
+
width: 12,
|
|
243
|
+
conditionalWidgets: [
|
|
244
|
+
{
|
|
245
|
+
widget: 'vizA',
|
|
246
|
+
dashboardCondition: {
|
|
247
|
+
id: 'component-condition-row-data',
|
|
248
|
+
datasetKey: 'data1',
|
|
249
|
+
operator: 'hasData'
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(config)
|
|
260
|
+
|
|
261
|
+
expect(filteredData).toEqual({
|
|
262
|
+
'0': [data.data1[1]],
|
|
263
|
+
'component-condition-row-data': [data.data1[1]]
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('should leave reset-state data conditions unresolved during precompute', () => {
|
|
268
|
+
const sharedFilters: SharedFilter[] = [
|
|
269
|
+
{
|
|
270
|
+
usedBy: [0],
|
|
271
|
+
active: '',
|
|
272
|
+
columnName: 'name',
|
|
273
|
+
...sharedFilterDefaults,
|
|
274
|
+
values: ['Alice', 'Bob', 'Charlie']
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
const config = {
|
|
278
|
+
...getConfig(sharedFilters),
|
|
279
|
+
rows: [
|
|
280
|
+
{
|
|
281
|
+
dataKey: 'data1',
|
|
282
|
+
data: data.data1,
|
|
283
|
+
dashboardCondition: {
|
|
284
|
+
id: 'row-condition-1',
|
|
285
|
+
datasetKey: 'data1',
|
|
286
|
+
operator: 'hasNoData'
|
|
287
|
+
},
|
|
288
|
+
columns: []
|
|
289
|
+
}
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(config)
|
|
294
|
+
|
|
295
|
+
expect(filteredData).toEqual({ '0': [] })
|
|
296
|
+
expect(filteredData).not.toHaveProperty('row-condition-1')
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
it('should precompute filtersIncomplete conditions from owner filter state', () => {
|
|
300
|
+
const sharedFilters: SharedFilter[] = [
|
|
301
|
+
{
|
|
302
|
+
usedBy: ['vizA'],
|
|
303
|
+
active: '',
|
|
304
|
+
columnName: 'name',
|
|
305
|
+
...sharedFilterDefaults,
|
|
306
|
+
values: ['Alice', 'Bob', 'Charlie']
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
const config = {
|
|
310
|
+
...getConfig(sharedFilters),
|
|
311
|
+
rows: [
|
|
312
|
+
{
|
|
313
|
+
columns: [
|
|
314
|
+
{
|
|
315
|
+
width: 12,
|
|
316
|
+
conditionalWidgets: [
|
|
317
|
+
{
|
|
318
|
+
widget: 'vizA',
|
|
319
|
+
dashboardCondition: {
|
|
320
|
+
id: 'filters-incomplete-condition',
|
|
321
|
+
operator: 'filtersIncomplete'
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const [, filteredData] = getUpdateConfig({ data, filteredData: {} } as any)(config)
|
|
332
|
+
|
|
333
|
+
expect(filteredData).toEqual({
|
|
334
|
+
vizA: [],
|
|
335
|
+
'filters-incomplete-condition': [{}]
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
})
|