@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.
Files changed (91) hide show
  1. package/CONFIG.md +77 -30
  2. package/LICENSE +201 -0
  3. package/dist/cdcdashboard.js +49936 -49166
  4. package/examples/dashboard-conditions-filters-incomplete.json +221 -0
  5. package/examples/dashboard-missing-datasets-multi.json +174 -0
  6. package/examples/dashboard-missing-datasets-single.json +121 -0
  7. package/examples/dashboard-multi-dashboard-version-regression.json +146 -0
  8. package/examples/dashboard-shared-filter-row-delete-cleanup.json +186 -0
  9. package/examples/dashboard-stale-dataset-keys.json +181 -0
  10. package/examples/dashboard-tiered-filter-regression.json +190 -0
  11. package/examples/private/cfa-dashboard.json +651 -0
  12. package/examples/private/data-bite-wrap.json +6936 -0
  13. package/examples/private/multi-dash-fix.json +16963 -0
  14. package/examples/private/versions.json +41612 -0
  15. package/examples/us-map-filter-example.json +1074 -0
  16. package/package.json +9 -9
  17. package/src/CdcDashboard.tsx +6 -2
  18. package/src/CdcDashboardComponent.tsx +178 -87
  19. package/src/DashboardCopyPasteContext.test.tsx +33 -0
  20. package/src/DashboardCopyPasteContext.tsx +48 -0
  21. package/src/_stories/Dashboard.EditorRegression.stories.tsx +72 -0
  22. package/src/_stories/Dashboard.Regression.stories.tsx +196 -0
  23. package/src/_stories/Dashboard.Zoom.stories.tsx +88 -0
  24. package/src/_stories/Dashboard.stories.tsx +294 -0
  25. package/src/_stories/FilteredTextMigrationComparison.stories.tsx +87 -0
  26. package/src/components/Column.test.tsx +176 -0
  27. package/src/components/Column.tsx +214 -13
  28. package/src/components/DashboardConditionModal.test.tsx +420 -0
  29. package/src/components/DashboardConditionModal.tsx +367 -0
  30. package/src/components/DashboardConditionSummary.tsx +59 -0
  31. package/src/components/DashboardEditors.tsx +8 -0
  32. package/src/components/DashboardFilters/DashboardFilters.test.tsx +139 -1
  33. package/src/components/DashboardFilters/DashboardFilters.tsx +192 -174
  34. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.test.tsx +164 -0
  35. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +41 -2
  36. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.test.tsx +180 -3
  37. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +15 -32
  38. package/src/components/DashboardFilters/DashboardFiltersWrapper.test.tsx +142 -0
  39. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +32 -27
  40. package/src/components/DashboardFilters/dashboardfilter.styles.css +42 -27
  41. package/src/components/DataDesignerModal.tsx +2 -1
  42. package/src/components/Grid.tsx +8 -4
  43. package/src/components/Header/Header.tsx +36 -17
  44. package/src/components/Row.test.tsx +228 -0
  45. package/src/components/Row.tsx +93 -18
  46. package/src/components/VisualizationRow.test.tsx +396 -0
  47. package/src/components/VisualizationRow.tsx +110 -35
  48. package/src/components/VisualizationsPanel/VisualizationsPanel.test.tsx +49 -0
  49. package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +14 -13
  50. package/src/components/Widget/Widget.test.tsx +218 -0
  51. package/src/components/Widget/Widget.tsx +119 -17
  52. package/src/components/Widget/widget.styles.css +31 -18
  53. package/src/components/dashboard-condition-modal.css +76 -0
  54. package/src/components/dashboard-condition-summary.css +87 -0
  55. package/src/helpers/addValuesToDashboardFilters.ts +3 -5
  56. package/src/helpers/addVisualization.ts +15 -4
  57. package/src/helpers/cloneDashboardWidget.ts +127 -0
  58. package/src/helpers/dashboardColumnWidgets.ts +99 -0
  59. package/src/helpers/dashboardConditionUi.ts +47 -0
  60. package/src/helpers/dashboardConditions.ts +200 -0
  61. package/src/helpers/dashboardFilterTargets.ts +156 -0
  62. package/src/helpers/filterData.ts +4 -9
  63. package/src/helpers/filterVisibility.ts +20 -0
  64. package/src/helpers/formatConfigBeforeSave.ts +2 -2
  65. package/src/helpers/getFilteredData.ts +18 -5
  66. package/src/helpers/getUpdateConfig.ts +43 -12
  67. package/src/helpers/getVizRowColumnLocator.ts +11 -1
  68. package/src/helpers/iconHash.tsx +9 -3
  69. package/src/helpers/mapDataToConfig.ts +31 -29
  70. package/src/helpers/reloadURLHelpers.ts +25 -5
  71. package/src/helpers/removeDashboardFilter.ts +33 -33
  72. package/src/helpers/tests/addVisualization.test.ts +53 -9
  73. package/src/helpers/tests/cloneDashboardWidget.test.ts +136 -0
  74. package/src/helpers/tests/dashboardColumnWidgets.test.ts +99 -0
  75. package/src/helpers/tests/dashboardConditionUi.test.ts +41 -0
  76. package/src/helpers/tests/dashboardConditions.test.ts +428 -0
  77. package/src/helpers/tests/formatConfigBeforeSave.test.ts +51 -0
  78. package/src/helpers/tests/getFilteredData.test.ts +265 -86
  79. package/src/helpers/tests/getUpdateConfig.test.ts +338 -0
  80. package/src/helpers/tests/reloadURLHelpers.test.ts +394 -238
  81. package/src/index.tsx +6 -3
  82. package/src/scss/grid.scss +249 -20
  83. package/src/scss/main.scss +108 -29
  84. package/src/store/dashboard.actions.ts +17 -4
  85. package/src/store/dashboard.reducer.test.ts +538 -0
  86. package/src/store/dashboard.reducer.ts +135 -22
  87. package/src/test/CdcDashboard.test.tsx +148 -0
  88. package/src/test/CdcDashboardComponent.test.tsx +935 -2
  89. package/src/types/ConfigRow.ts +15 -0
  90. package/src/types/DashboardFilters.ts +4 -0
  91. package/src/types/SharedFilter.ts +1 -0
@@ -0,0 +1,428 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+ import {
3
+ dashboardRowsUseFiltersIncomplete,
4
+ ensureRowConditionIds,
5
+ evaluateDashboardCondition,
6
+ getDashboardConditionFilteredData,
7
+ hasIncompleteFiltersForDashboardCondition
8
+ } from '../dashboardConditions'
9
+ import {
10
+ getDashboardConditionTargets,
11
+ getSharedFilterTargetOptions,
12
+ remapRowTargetsInSharedFilters
13
+ } from '../dashboardFilterTargets'
14
+
15
+ describe('dashboardConditions', () => {
16
+ afterEach(() => {
17
+ vi.restoreAllMocks()
18
+ })
19
+
20
+ it('assigns missing condition ids and preserves existing ones', () => {
21
+ vi.spyOn(Math, 'random').mockReturnValueOnce(0.123456789).mockReturnValueOnce(0.23456789)
22
+
23
+ const rows = ensureRowConditionIds([
24
+ {
25
+ columns: [
26
+ {
27
+ width: 6,
28
+ conditionalWidgets: [
29
+ { widget: 'viz-1', dashboardCondition: { datasetKey: 'dataset-1', operator: 'hasData' } }
30
+ ]
31
+ },
32
+ { width: 6, widget: 'viz-2' }
33
+ ],
34
+ dashboardCondition: { datasetKey: 'dataset-1', operator: 'hasData' },
35
+ expandCollapseAllButtons: false
36
+ }
37
+ ] as any)
38
+
39
+ expect(rows[0].dashboardCondition?.id).toMatch(/^condition-[a-z0-9]{8}$/)
40
+ expect(rows[0].columns[0].conditionalWidgets?.[0].dashboardCondition?.id).toMatch(/^condition-[a-z0-9]{8}$/)
41
+ expect(rows[0].dashboardCondition?.id).not.toBe(rows[0].columns[0].conditionalWidgets?.[0].dashboardCondition?.id)
42
+ expect(rows[0].columns[1]).toMatchObject({ widget: 'viz-2' })
43
+ })
44
+
45
+ it('assigns row condition ids without requiring normalized columns', () => {
46
+ vi.spyOn(Math, 'random').mockReturnValue(0.123456789)
47
+
48
+ const rows = ensureRowConditionIds([
49
+ {
50
+ dashboardCondition: { datasetKey: 'dataset-1', operator: 'hasData' },
51
+ expandCollapseAllButtons: false
52
+ }
53
+ ] as any)
54
+
55
+ expect(rows[0].dashboardCondition?.id).toMatch(/^condition-[a-z0-9]{8}$/)
56
+ expect(rows[0]).not.toHaveProperty('columns')
57
+ })
58
+
59
+ it('preserves legacy array-shaped rows for version migrations', () => {
60
+ const legacyRow = [{ width: 12, widget: 'viz-1' }]
61
+ const rows = ensureRowConditionIds([legacyRow] as any)
62
+
63
+ expect(rows[0]).toBe(legacyRow)
64
+ })
65
+
66
+ it('tracks dashboard condition owner filter targets without exposing condition ids as Used By options', () => {
67
+ const rows = [
68
+ {
69
+ columns: [
70
+ {
71
+ width: 12,
72
+ conditionalWidgets: [
73
+ { widget: 'viz-1', dashboardCondition: { id: 'column-condition-1', operator: 'hasData' } },
74
+ { widget: 'viz-3', dashboardCondition: { id: 'column-condition-3', operator: 'hasNoData' } }
75
+ ]
76
+ }
77
+ ],
78
+ dashboardCondition: { id: 'row-condition-1', operator: 'hasData' },
79
+ expandCollapseAllButtons: false
80
+ },
81
+ {
82
+ columns: [
83
+ {
84
+ width: 12,
85
+ conditionalWidgets: [
86
+ { widget: 'viz-row-data', dashboardCondition: { id: 'row-data-condition', operator: 'hasData' } }
87
+ ]
88
+ }
89
+ ],
90
+ dataKey: 'row-data',
91
+ expandCollapseAllButtons: false
92
+ },
93
+ {
94
+ columns: [{ width: 12, widget: 'viz-2' }],
95
+ dashboardCondition: { id: 'row-condition-2', operator: 'hasData' },
96
+ expandCollapseAllButtons: false,
97
+ toggle: true
98
+ }
99
+ ] as any
100
+ const { nameLookup, options } = getSharedFilterTargetOptions(
101
+ {
102
+ dashboard: { sharedFilters: [] },
103
+ rows,
104
+ visualizations: {
105
+ 'viz-1': { uid: 'viz-1', type: 'markup-include', visualizationType: 'markup-include' },
106
+ 'viz-2': { uid: 'viz-2', type: 'markup-include', visualizationType: 'markup-include' },
107
+ 'viz-3': { uid: 'viz-3', type: 'markup-include', visualizationType: 'markup-include' },
108
+ 'viz-row-data': { uid: 'viz-row-data', type: 'markup-include', visualizationType: 'markup-include' }
109
+ }
110
+ } as any,
111
+ {}
112
+ )
113
+
114
+ expect(
115
+ getDashboardConditionTargets(rows).map(conditionTarget => ({
116
+ id: conditionTarget.id,
117
+ filterTarget: conditionTarget.filterTarget
118
+ }))
119
+ ).toEqual([
120
+ { id: 'row-condition-1', filterTarget: 0 },
121
+ { id: 'column-condition-1', filterTarget: 'viz-1' },
122
+ { id: 'column-condition-3', filterTarget: 'viz-3' },
123
+ { id: 'row-data-condition', filterTarget: 1 }
124
+ ])
125
+ expect(options).toEqual(['viz-1', 'viz-2', 'viz-3', 0, 1])
126
+ expect(nameLookup['0']).toBe('Row 1')
127
+ expect(nameLookup['1']).toBe('Row 2')
128
+ expect(nameLookup['row-condition-1']).toBeUndefined()
129
+ expect(nameLookup['column-condition-1']).toBeUndefined()
130
+ expect(nameLookup['row-condition-2']).toBeUndefined()
131
+ })
132
+
133
+ it('preserves existing Used By row targets that are not normally selectable', () => {
134
+ const { nameLookup, options } = getSharedFilterTargetOptions(
135
+ {
136
+ dashboard: { sharedFilters: [] },
137
+ rows: [
138
+ { columns: [{ width: 12, widget: 'markup-1' }] },
139
+ { dataKey: '', columns: [{ width: 12, widget: 'markup-2' }] }
140
+ ],
141
+ visualizations: {
142
+ 'markup-1': { uid: 'markup-1', type: 'markup-include', visualizationType: 'markup-include' },
143
+ 'markup-2': { uid: 'markup-2', type: 'markup-include', visualizationType: 'markup-include' }
144
+ }
145
+ } as any,
146
+ { usedBy: [1, 'missing-target', 'row-condition-1'] }
147
+ )
148
+
149
+ expect(options).toEqual(['markup-1', 'markup-2', 1])
150
+ expect(nameLookup['1']).toBe('Row 2')
151
+ expect(nameLookup['missing-target']).toBeUndefined()
152
+ expect(nameLookup['row-condition-1']).toBeUndefined()
153
+ })
154
+
155
+ it('treats reset-state filters as unresolved instead of hasNoData', () => {
156
+ const filteredData = getDashboardConditionFilteredData(
157
+ { id: 'row-condition-1', datasetKey: 'condition-data', operator: 'hasNoData' },
158
+ {
159
+ sharedFilters: [
160
+ {
161
+ key: 'Region',
162
+ type: 'datafilter',
163
+ columnName: 'region',
164
+ showDropdown: true,
165
+ active: '',
166
+ usedBy: [0]
167
+ }
168
+ ]
169
+ } as any,
170
+ {
171
+ 'condition-data': [{ region: 'East' }]
172
+ },
173
+ 0
174
+ )
175
+
176
+ expect(filteredData).toBeUndefined()
177
+ })
178
+
179
+ it('detects filtersIncomplete conditions without requiring a dataset', () => {
180
+ expect(
181
+ dashboardRowsUseFiltersIncomplete([
182
+ {
183
+ columns: [
184
+ {
185
+ width: 12,
186
+ conditionalWidgets: [
187
+ {
188
+ widget: 'markup-1',
189
+ dashboardCondition: { id: 'column-condition-1', operator: 'filtersIncomplete' }
190
+ }
191
+ ]
192
+ }
193
+ ],
194
+ expandCollapseAllButtons: false
195
+ }
196
+ ] as any)
197
+ ).toBe(true)
198
+
199
+ const filteredData = getDashboardConditionFilteredData(
200
+ { id: 'column-condition-1', operator: 'filtersIncomplete' },
201
+ {
202
+ sharedFilters: [
203
+ {
204
+ key: 'Region',
205
+ type: 'datafilter',
206
+ columnName: 'region',
207
+ showDropdown: true,
208
+ active: '',
209
+ usedBy: ['markup-1']
210
+ }
211
+ ]
212
+ } as any,
213
+ {},
214
+ 'markup-1'
215
+ )
216
+
217
+ expect(filteredData).toEqual([{}])
218
+ expect(
219
+ evaluateDashboardCondition({ id: 'column-condition-1', operator: 'filtersIncomplete' }, filteredData)
220
+ ).toEqual({ matches: true, resolved: true })
221
+ })
222
+
223
+ it('uses owner-target filter semantics for filtersIncomplete, including unscoped filters', () => {
224
+ const dashboard = {
225
+ sharedFilters: [
226
+ {
227
+ key: 'Unscoped Region',
228
+ type: 'datafilter',
229
+ columnName: 'region',
230
+ showDropdown: true,
231
+ active: '',
232
+ usedBy: []
233
+ }
234
+ ]
235
+ } as any
236
+
237
+ expect(
238
+ hasIncompleteFiltersForDashboardCondition(
239
+ { id: 'column-condition-1', operator: 'filtersIncomplete' },
240
+ dashboard,
241
+ 'markup-1'
242
+ )
243
+ ).toBe(true)
244
+ })
245
+
246
+ it('ignores reset filters scoped only to unrelated targets for filtersIncomplete', () => {
247
+ const filteredData = getDashboardConditionFilteredData(
248
+ { id: 'column-condition-1', operator: 'filtersIncomplete' },
249
+ {
250
+ sharedFilters: [
251
+ {
252
+ key: 'Other Region',
253
+ type: 'datafilter',
254
+ columnName: 'region',
255
+ showDropdown: true,
256
+ active: '',
257
+ usedBy: ['other-widget']
258
+ }
259
+ ]
260
+ } as any,
261
+ {},
262
+ 'markup-1'
263
+ )
264
+
265
+ expect(filteredData).toEqual([])
266
+ expect(
267
+ evaluateDashboardCondition({ id: 'column-condition-1', operator: 'filtersIncomplete' }, filteredData)
268
+ ).toEqual({ matches: false, resolved: true })
269
+ })
270
+
271
+ it('ignores filters whose columns are missing from the condition dataset', () => {
272
+ const filteredData = getDashboardConditionFilteredData(
273
+ { id: 'row-condition-1', datasetKey: 'condition-data', operator: 'hasData' },
274
+ {
275
+ sharedFilters: [
276
+ {
277
+ key: 'Missing Column Filter',
278
+ type: 'datafilter',
279
+ columnName: 'missingColumn',
280
+ showDropdown: true,
281
+ active: 'x',
282
+ usedBy: [0]
283
+ }
284
+ ]
285
+ } as any,
286
+ {
287
+ 'condition-data': [{ region: 'East' }]
288
+ },
289
+ 0
290
+ )
291
+
292
+ expect(filteredData).toEqual([{ region: 'East' }])
293
+ })
294
+
295
+ it('matches columnHasAnyValue with loose string coercion', () => {
296
+ const result = evaluateDashboardCondition(
297
+ {
298
+ id: 'column-condition-1',
299
+ datasetKey: 'condition-data',
300
+ operator: 'columnHasAnyValue',
301
+ columnName: 'year',
302
+ values: ['2024', '2025']
303
+ },
304
+ [{ year: 2024 }]
305
+ )
306
+
307
+ expect(result.matches).toBe(true)
308
+ expect(result.resolved).toBe(true)
309
+ })
310
+
311
+ it('does not treat unrelated unscoped filters without matching dataset columns as unresolved', () => {
312
+ const filteredData = getDashboardConditionFilteredData(
313
+ { id: 'row-condition-1', datasetKey: 'condition-data', operator: 'hasData' },
314
+ {
315
+ sharedFilters: [
316
+ {
317
+ key: 'Unscoped URL Filter',
318
+ type: 'urlfilter',
319
+ showDropdown: true,
320
+ active: '',
321
+ usedBy: []
322
+ },
323
+ {
324
+ key: 'Different Dataset Column',
325
+ type: 'datafilter',
326
+ columnName: 'not_in_dataset',
327
+ showDropdown: true,
328
+ active: '',
329
+ usedBy: []
330
+ }
331
+ ]
332
+ } as any,
333
+ {
334
+ 'condition-data': [{ region: 'East' }]
335
+ },
336
+ 0
337
+ )
338
+
339
+ expect(filteredData).toEqual([{ region: 'East' }])
340
+ })
341
+
342
+ it('treats missing precomputed condition data as unresolved', () => {
343
+ const result = evaluateDashboardCondition(
344
+ { id: 'row-condition-1', datasetKey: 'condition-data', operator: 'hasNoData' },
345
+ undefined
346
+ )
347
+
348
+ expect(result).toEqual({ matches: false, resolved: false })
349
+ })
350
+
351
+ it('remaps row usedBy targets when rows are deleted or moved', () => {
352
+ const deletedRowTargets = remapRowTargetsInSharedFilters(
353
+ [
354
+ {
355
+ key: 'Row Filter',
356
+ type: 'datafilter',
357
+ columnName: 'region',
358
+ usedBy: [0, 1, '2', 'viz-1']
359
+ }
360
+ ] as any,
361
+ rowIndex => {
362
+ if (rowIndex === 1) return null
363
+ if (rowIndex > 1) return rowIndex - 1
364
+ return rowIndex
365
+ }
366
+ )
367
+
368
+ expect(deletedRowTargets[0].usedBy).toEqual([0, '1', 'viz-1'])
369
+
370
+ const movedRowTargets = remapRowTargetsInSharedFilters(
371
+ [
372
+ {
373
+ key: 'Row Filter',
374
+ type: 'datafilter',
375
+ columnName: 'region',
376
+ usedBy: [0, 1, 'viz-1']
377
+ }
378
+ ] as any,
379
+ rowIndex => {
380
+ if (rowIndex === 0) return 1
381
+ if (rowIndex === 1) return 0
382
+ return rowIndex
383
+ }
384
+ )
385
+
386
+ expect(movedRowTargets[0].usedBy).toEqual([1, 0, 'viz-1'])
387
+ })
388
+
389
+ it('preserves unknown string usedBy targets when remapping row targets', () => {
390
+ const remappedTargets = remapRowTargetsInSharedFilters(
391
+ [
392
+ {
393
+ key: 'Legacy Footnote Filter',
394
+ type: 'datafilter',
395
+ columnName: 'FootnoteScope',
396
+ usedBy: ['footnotes-legacy-target', 1]
397
+ }
398
+ ] as any,
399
+ rowIndex => (rowIndex === 1 ? 0 : rowIndex)
400
+ )
401
+
402
+ expect(remappedTargets[0].usedBy).toEqual(['footnotes-legacy-target', 0])
403
+ })
404
+
405
+ it('does not treat unrelated unknown targets as owner targets for data-backed conditions', () => {
406
+ const filteredData = getDashboardConditionFilteredData(
407
+ { id: 'row-condition-1', datasetKey: 'condition-data', operator: 'hasData' },
408
+ {
409
+ sharedFilters: [
410
+ {
411
+ key: 'Unknown Target Filter',
412
+ type: 'datafilter',
413
+ columnName: 'region',
414
+ showDropdown: true,
415
+ active: '',
416
+ usedBy: ['legacy-footnote-target']
417
+ }
418
+ ]
419
+ } as any,
420
+ {
421
+ 'condition-data': [{ region: 'East' }]
422
+ },
423
+ 0
424
+ )
425
+
426
+ expect(filteredData).toEqual([{ region: 'East' }])
427
+ })
428
+ })
@@ -49,6 +49,57 @@ describe('cleanSharedFilters', () => {
49
49
  ])
50
50
  })
51
51
 
52
+ it('retains filters when sharedFilterIndexes are stored as numbers', () => {
53
+ const config: DashboardConfig = {
54
+ dashboard: {
55
+ sharedFilters: [
56
+ { id: 1, type: 'filter1' },
57
+ { id: 2, type: 'filter2' }
58
+ ]
59
+ },
60
+ visualizations: {
61
+ viz1: { type: 'dashboardFilters', sharedFilterIndexes: [0, 1] }
62
+ }
63
+ }
64
+
65
+ cleanSharedFilters(config)
66
+
67
+ expect(config.dashboard.sharedFilters).toHaveLength(2)
68
+ })
69
+
70
+ it('retains filters when sharedFilterIndexes are stored as strings (malformed config)', () => {
71
+ // Documents the bug fixed when DashboardFiltersEditor started wrapping e.target.value in Number()
72
+ // so sharedFilterIndexes are stored as numbers instead of strings.
73
+ // cleanSharedFilters normalizes to numbers so malformed configs from older saves are also handled.
74
+ const config: DashboardConfig = {
75
+ dashboard: {
76
+ sharedFilters: [{ id: 1, type: 'filter1' }]
77
+ },
78
+ visualizations: {
79
+ viz1: { type: 'dashboardFilters', sharedFilterIndexes: ['0'] as any }
80
+ }
81
+ }
82
+
83
+ cleanSharedFilters(config)
84
+
85
+ expect(config.dashboard.sharedFilters).toHaveLength(1)
86
+ })
87
+
88
+ it('removes all shared filters when dashboardFilters viz has no sharedFilterIndexes', () => {
89
+ const config: DashboardConfig = {
90
+ dashboard: {
91
+ sharedFilters: [{ id: 1, type: 'filter1' }]
92
+ },
93
+ visualizations: {
94
+ viz1: { type: 'dashboardFilters' } as any
95
+ }
96
+ }
97
+
98
+ cleanSharedFilters(config)
99
+
100
+ expect(config.dashboard.sharedFilters).toEqual([])
101
+ })
102
+
52
103
  it('should remove values from urlfilter type shared filters', () => {
53
104
  const config: DashboardConfig = {
54
105
  dashboard: {