@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
@@ -1,9 +1,17 @@
1
1
  import React from 'react'
2
- import { render } from '@testing-library/react'
3
- import { describe, expect, it } from 'vitest'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { describe, expect, it, vi } from 'vitest'
4
4
  import CdcDashboardComponent from '../CdcDashboardComponent'
5
5
  import type { InitialState } from '../types/InitialState'
6
6
 
7
+ class ResizeObserverMock {
8
+ observe() {}
9
+ unobserve() {}
10
+ disconnect() {}
11
+ }
12
+
13
+ vi.stubGlobal('ResizeObserver', ResizeObserverMock)
14
+
7
15
  describe('CdcDashboardComponent', () => {
8
16
  it('renders dashboard markup through the shared visualization shell', () => {
9
17
  const initialState = {
@@ -71,4 +79,929 @@ describe('CdcDashboardComponent', () => {
71
79
  expect(shell).toHaveClass('type-dashboard', 'is-dashboard-editor')
72
80
  expect(shell).not.toHaveClass('is-editor')
73
81
  })
82
+
83
+ it('only renders the dashboard download button section when a download button is enabled', () => {
84
+ const baseInitialState = {
85
+ config: {
86
+ type: 'dashboard',
87
+ dashboard: {
88
+ title: 'Dashboard Title',
89
+ titleStyle: 'small',
90
+ theme: 'theme-blue',
91
+ sharedFilters: []
92
+ },
93
+ visualizations: {},
94
+ rows: [],
95
+ datasets: {},
96
+ table: {}
97
+ },
98
+ data: {},
99
+ loading: false,
100
+ filteredData: {},
101
+ preview: false,
102
+ tabSelected: 'Dashboard Preview',
103
+ filtersApplied: true
104
+ } as InitialState
105
+
106
+ const disabledRender = render(
107
+ <CdcDashboardComponent initialState={baseInitialState} interactionLabel='dashboard-test' isEditor={false} />
108
+ )
109
+
110
+ expect(disabledRender.container.querySelector('.download-buttons')).not.toBeInTheDocument()
111
+ disabledRender.unmount()
112
+
113
+ const enabledRender = render(
114
+ <CdcDashboardComponent
115
+ initialState={{
116
+ ...baseInitialState,
117
+ config: {
118
+ ...baseInitialState.config,
119
+ table: {
120
+ downloadImageButton: true
121
+ }
122
+ }
123
+ }}
124
+ interactionLabel='dashboard-test'
125
+ isEditor={false}
126
+ />
127
+ )
128
+
129
+ expect(enabledRender.container.querySelector('.download-buttons')).toBeInTheDocument()
130
+ expect(screen.getByRole('button', { name: 'Download Image' })).toBeInTheDocument()
131
+ })
132
+
133
+ it('suppresses rows when row conditions fail and preserves width for condition-hidden components', () => {
134
+ const initialState = {
135
+ config: {
136
+ type: 'dashboard',
137
+ dashboard: {
138
+ title: 'Dashboard Title',
139
+ titleStyle: 'small',
140
+ theme: 'theme-blue',
141
+ sharedFilters: []
142
+ },
143
+ visualizations: {
144
+ 'markup-hidden-row': {
145
+ type: 'markup-include',
146
+ contentEditor: {
147
+ inlineHTML: '<p>Hidden row content</p>',
148
+ showHeader: true,
149
+ srcUrl: '',
150
+ title: 'Hidden Row',
151
+ useInlineHTML: true
152
+ }
153
+ },
154
+ 'markup-hidden-column': {
155
+ type: 'markup-include',
156
+ contentEditor: {
157
+ inlineHTML: '<p>Hidden column content</p>',
158
+ showHeader: true,
159
+ srcUrl: '',
160
+ title: 'Hidden Column',
161
+ useInlineHTML: true
162
+ }
163
+ },
164
+ 'markup-visible-column': {
165
+ type: 'markup-include',
166
+ contentEditor: {
167
+ inlineHTML: '<p>Visible widget content</p>',
168
+ showHeader: true,
169
+ srcUrl: '',
170
+ title: 'Visible Column',
171
+ useInlineHTML: true
172
+ }
173
+ }
174
+ },
175
+ rows: [
176
+ {
177
+ columns: [{ width: 12, widget: 'markup-hidden-row' }],
178
+ dashboardCondition: {
179
+ id: 'row-condition-1',
180
+ datasetKey: 'row-condition-data',
181
+ operator: 'hasData'
182
+ },
183
+ expandCollapseAllButtons: false
184
+ },
185
+ {
186
+ columns: [
187
+ {
188
+ width: 6,
189
+ conditionalWidgets: [
190
+ {
191
+ widget: 'markup-hidden-column',
192
+ dashboardCondition: {
193
+ id: 'column-condition-1',
194
+ datasetKey: 'column-condition-data',
195
+ operator: 'columnHasAnyValue',
196
+ columnName: 'flag',
197
+ values: ['show']
198
+ }
199
+ }
200
+ ]
201
+ },
202
+ {
203
+ width: 6,
204
+ widget: 'markup-visible-column'
205
+ }
206
+ ],
207
+ expandCollapseAllButtons: false
208
+ }
209
+ ],
210
+ datasets: {
211
+ 'row-condition-data': { data: [] },
212
+ 'column-condition-data': { data: [{ flag: 'hide' }] }
213
+ },
214
+ table: {}
215
+ },
216
+ data: {
217
+ 'row-condition-data': [],
218
+ 'column-condition-data': [{ flag: 'hide' }]
219
+ },
220
+ loading: false,
221
+ filteredData: {
222
+ 'column-condition-1': [{ flag: 'hide' }]
223
+ },
224
+ preview: false,
225
+ tabSelected: 'Dashboard Preview',
226
+ filtersApplied: true
227
+ } as InitialState
228
+
229
+ const { container } = render(
230
+ <CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />
231
+ )
232
+
233
+ expect(screen.queryByText('Hidden row content')).not.toBeInTheDocument()
234
+ expect(screen.queryByText('Hidden column content')).not.toBeInTheDocument()
235
+ expect(screen.getByText('Visible widget content')).toBeInTheDocument()
236
+
237
+ const hiddenConditionColumn = container.querySelector('[data-dashboard-condition-hidden="true"]')
238
+ expect(hiddenConditionColumn).toHaveClass('col-md-6')
239
+ })
240
+
241
+ it('skips truly empty runtime columns in multi-visualization rows', () => {
242
+ const initialState = {
243
+ config: {
244
+ type: 'dashboard',
245
+ dashboard: {
246
+ title: 'Dashboard Title',
247
+ titleStyle: 'small',
248
+ theme: 'theme-blue',
249
+ sharedFilters: []
250
+ },
251
+ visualizations: {
252
+ 'markup-visible-column': {
253
+ type: 'markup-include',
254
+ contentEditor: {
255
+ inlineHTML: '<p>Visible widget content</p>',
256
+ showHeader: true,
257
+ srcUrl: '',
258
+ title: 'Visible Column',
259
+ useInlineHTML: true
260
+ }
261
+ }
262
+ },
263
+ rows: [
264
+ {
265
+ multiVizColumn: 'group',
266
+ columns: [{ width: 4 }, { width: 8, widget: 'markup-visible-column' }],
267
+ expandCollapseAllButtons: false
268
+ }
269
+ ],
270
+ datasets: {
271
+ 'column-condition-data': { data: [{ group: 'A' }] }
272
+ },
273
+ table: {}
274
+ },
275
+ data: {
276
+ 'column-condition-data': [{ group: 'A' }]
277
+ },
278
+ loading: false,
279
+ filteredData: {
280
+ 0: [{ group: 'A' }]
281
+ },
282
+ preview: false,
283
+ tabSelected: 'Dashboard Preview',
284
+ filtersApplied: true
285
+ } as InitialState
286
+
287
+ const { container } = render(
288
+ <CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />
289
+ )
290
+
291
+ expect(screen.getByText('Visible widget content')).toBeInTheDocument()
292
+
293
+ const row = container.querySelector('[data-row-index="0"]')
294
+ expect(row?.querySelectorAll('[data-dashboard-condition-hidden="true"]').length).toBe(0)
295
+ expect(row?.querySelectorAll('.col-md-4').length).toBe(0)
296
+ expect(row?.querySelectorAll('.col-md-8').length).toBe(1)
297
+ })
298
+
299
+ it('preserves truly empty runtime columns in ordinary rows', () => {
300
+ const initialState = {
301
+ config: {
302
+ type: 'dashboard',
303
+ dashboard: {
304
+ title: 'Dashboard Title',
305
+ titleStyle: 'small',
306
+ theme: 'theme-blue',
307
+ sharedFilters: []
308
+ },
309
+ visualizations: {
310
+ 'markup-visible-column': {
311
+ type: 'markup-include',
312
+ contentEditor: {
313
+ inlineHTML: '<p>Visible widget content</p>',
314
+ showHeader: true,
315
+ srcUrl: '',
316
+ title: 'Visible Column',
317
+ useInlineHTML: true
318
+ }
319
+ }
320
+ },
321
+ rows: [
322
+ {
323
+ columns: [{ width: 4 }, { width: 8, widget: 'markup-visible-column' }],
324
+ expandCollapseAllButtons: false
325
+ }
326
+ ],
327
+ datasets: {},
328
+ table: {}
329
+ },
330
+ data: {},
331
+ loading: false,
332
+ filteredData: {},
333
+ preview: false,
334
+ tabSelected: 'Dashboard Preview',
335
+ filtersApplied: true
336
+ } as InitialState
337
+
338
+ const { container } = render(
339
+ <CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />
340
+ )
341
+
342
+ expect(screen.getByText('Visible widget content')).toBeInTheDocument()
343
+
344
+ const row = container.querySelector('[data-row-index="0"]')
345
+ expect(row?.querySelectorAll('[data-dashboard-condition-hidden="true"]').length).toBe(0)
346
+ expect(row?.querySelectorAll('.col-md-4').length).toBe(1)
347
+ expect(row?.querySelectorAll('.col-md-8').length).toBe(1)
348
+ })
349
+
350
+ it('uses the resolved conditional widget when excluding filter rows from equal height', () => {
351
+ const initialState = {
352
+ config: {
353
+ type: 'dashboard',
354
+ dashboard: {
355
+ title: 'Dashboard Title',
356
+ titleStyle: 'small',
357
+ theme: 'theme-blue',
358
+ sharedFilters: []
359
+ },
360
+ visualizations: {
361
+ 'dashboard-filters-hidden': {
362
+ type: 'dashboardFilters',
363
+ visualizationType: 'dashboardFilters',
364
+ sharedFilterIndexes: []
365
+ },
366
+ 'markup-visible': {
367
+ type: 'markup-include',
368
+ contentEditor: {
369
+ inlineHTML: '<p>Resolved markup content</p>',
370
+ showHeader: true,
371
+ srcUrl: '',
372
+ title: 'Resolved',
373
+ useInlineHTML: true
374
+ }
375
+ }
376
+ },
377
+ rows: [
378
+ {
379
+ equalHeight: true,
380
+ columns: [
381
+ {
382
+ width: 12,
383
+ conditionalWidgets: [
384
+ {
385
+ widget: 'dashboard-filters-hidden',
386
+ dashboardCondition: {
387
+ id: 'filters-condition',
388
+ datasetKey: 'condition-data',
389
+ operator: 'hasData'
390
+ }
391
+ },
392
+ {
393
+ widget: 'markup-visible',
394
+ dashboardCondition: {
395
+ id: 'markup-condition',
396
+ datasetKey: 'condition-data',
397
+ operator: 'hasData'
398
+ }
399
+ }
400
+ ]
401
+ }
402
+ ],
403
+ expandCollapseAllButtons: false
404
+ }
405
+ ],
406
+ datasets: {
407
+ 'condition-data': { data: [{ value: 'show' }] }
408
+ },
409
+ table: {}
410
+ },
411
+ data: {
412
+ 'condition-data': [{ value: 'show' }]
413
+ },
414
+ loading: false,
415
+ filteredData: {
416
+ 'filters-condition': [],
417
+ 'markup-condition': [{ value: 'show' }]
418
+ },
419
+ preview: false,
420
+ tabSelected: 'Dashboard Preview',
421
+ filtersApplied: true
422
+ } as InitialState
423
+
424
+ const { container } = render(
425
+ <CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />
426
+ )
427
+
428
+ expect(screen.getByText('Resolved markup content')).toBeInTheDocument()
429
+ expect(container.querySelector('[data-row-index="0"]')).toHaveClass('equal-height')
430
+ })
431
+
432
+ it('counts resolved conditional TP5 widgets for automatic equal height', () => {
433
+ const initialState = {
434
+ config: {
435
+ type: 'dashboard',
436
+ dashboard: {
437
+ title: 'Dashboard Title',
438
+ titleStyle: 'small',
439
+ theme: 'theme-blue',
440
+ sharedFilters: []
441
+ },
442
+ visualizations: {
443
+ 'markup-plain-hidden': {
444
+ type: 'markup-include',
445
+ contentEditor: {
446
+ inlineHTML: '<p>Plain hidden content</p>',
447
+ showHeader: true,
448
+ srcUrl: '',
449
+ title: 'Plain Hidden',
450
+ useInlineHTML: true
451
+ }
452
+ },
453
+ 'markup-tp5-resolved': {
454
+ type: 'markup-include',
455
+ contentEditor: {
456
+ inlineHTML: '<p>Resolved TP5 content</p>',
457
+ showHeader: true,
458
+ srcUrl: '',
459
+ style: 'tp5',
460
+ title: 'Resolved TP5',
461
+ useInlineHTML: true
462
+ }
463
+ },
464
+ 'markup-tp5-static': {
465
+ type: 'markup-include',
466
+ contentEditor: {
467
+ inlineHTML: '<p>Static TP5 content</p>',
468
+ showHeader: true,
469
+ srcUrl: '',
470
+ style: 'tp5',
471
+ title: 'Static TP5',
472
+ useInlineHTML: true
473
+ }
474
+ }
475
+ },
476
+ rows: [
477
+ {
478
+ columns: [
479
+ {
480
+ width: 6,
481
+ conditionalWidgets: [
482
+ {
483
+ widget: 'markup-plain-hidden',
484
+ dashboardCondition: {
485
+ id: 'plain-condition',
486
+ datasetKey: 'condition-data',
487
+ operator: 'hasData'
488
+ }
489
+ },
490
+ {
491
+ widget: 'markup-tp5-resolved',
492
+ dashboardCondition: {
493
+ id: 'tp5-condition',
494
+ datasetKey: 'condition-data',
495
+ operator: 'hasData'
496
+ }
497
+ }
498
+ ]
499
+ },
500
+ {
501
+ width: 6,
502
+ widget: 'markup-tp5-static'
503
+ }
504
+ ],
505
+ expandCollapseAllButtons: false
506
+ }
507
+ ],
508
+ datasets: {
509
+ 'condition-data': { data: [{ value: 'show' }] }
510
+ },
511
+ table: {}
512
+ },
513
+ data: {
514
+ 'condition-data': [{ value: 'show' }]
515
+ },
516
+ loading: false,
517
+ filteredData: {
518
+ 'plain-condition': [],
519
+ 'tp5-condition': [{ value: 'show' }]
520
+ },
521
+ preview: false,
522
+ tabSelected: 'Dashboard Preview',
523
+ filtersApplied: true
524
+ } as InitialState
525
+
526
+ const { container } = render(
527
+ <CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />
528
+ )
529
+
530
+ expect(screen.getByText('Resolved TP5 content')).toBeInTheDocument()
531
+ expect(screen.getByText('Static TP5 content')).toBeInTheDocument()
532
+ expect(container.querySelector('[data-row-index="0"]')).toHaveClass('equal-height')
533
+ })
534
+
535
+ it('keeps the legacy incomplete-filter message when no filtersIncomplete condition exists', () => {
536
+ const initialState = {
537
+ config: {
538
+ type: 'dashboard',
539
+ dashboard: {
540
+ title: 'Dashboard Title',
541
+ titleStyle: 'small',
542
+ theme: 'theme-blue',
543
+ sharedFilters: [
544
+ {
545
+ key: 'Region',
546
+ type: 'datafilter',
547
+ columnName: 'region',
548
+ showDropdown: true,
549
+ active: '',
550
+ usedBy: []
551
+ }
552
+ ]
553
+ },
554
+ visualizations: {
555
+ 'markup-default': {
556
+ type: 'markup-include',
557
+ contentEditor: {
558
+ inlineHTML: '<p>Default content</p>',
559
+ showHeader: true,
560
+ srcUrl: '',
561
+ title: 'Default',
562
+ useInlineHTML: true
563
+ }
564
+ }
565
+ },
566
+ rows: [{ columns: [{ width: 12, widget: 'markup-default' }], expandCollapseAllButtons: false }],
567
+ datasets: {},
568
+ table: {}
569
+ },
570
+ data: {},
571
+ loading: false,
572
+ filteredData: {},
573
+ preview: false,
574
+ tabSelected: 'Dashboard Preview',
575
+ filtersApplied: true
576
+ } as InitialState
577
+
578
+ render(<CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />)
579
+
580
+ expect(screen.getByText('Please complete your selection to continue.')).toBeInTheDocument()
581
+ })
582
+
583
+ it('renders authored incomplete-filter content and suppresses the legacy message when opted in', () => {
584
+ const initialState = {
585
+ config: {
586
+ type: 'dashboard',
587
+ dashboard: {
588
+ title: 'Dashboard Title',
589
+ titleStyle: 'small',
590
+ theme: 'theme-blue',
591
+ sharedFilters: [
592
+ {
593
+ key: 'Region',
594
+ type: 'datafilter',
595
+ columnName: 'region',
596
+ showDropdown: true,
597
+ active: '',
598
+ usedBy: ['markup-incomplete']
599
+ }
600
+ ]
601
+ },
602
+ visualizations: {
603
+ 'markup-incomplete': {
604
+ type: 'markup-include',
605
+ contentEditor: {
606
+ inlineHTML: '<p>Select filters authored content</p>',
607
+ showHeader: true,
608
+ srcUrl: '',
609
+ title: 'Incomplete',
610
+ useInlineHTML: true
611
+ }
612
+ }
613
+ },
614
+ rows: [
615
+ {
616
+ columns: [
617
+ {
618
+ width: 12,
619
+ conditionalWidgets: [
620
+ {
621
+ widget: 'markup-incomplete',
622
+ dashboardCondition: {
623
+ id: 'filters-incomplete-condition',
624
+ operator: 'filtersIncomplete'
625
+ }
626
+ }
627
+ ]
628
+ }
629
+ ],
630
+ expandCollapseAllButtons: false
631
+ }
632
+ ],
633
+ datasets: {},
634
+ table: {}
635
+ },
636
+ data: {},
637
+ loading: false,
638
+ filteredData: {
639
+ 'filters-incomplete-condition': [{}]
640
+ },
641
+ preview: false,
642
+ tabSelected: 'Dashboard Preview',
643
+ filtersApplied: true
644
+ } as InitialState
645
+
646
+ render(<CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />)
647
+
648
+ expect(screen.getByText('Select filters authored content')).toBeInTheDocument()
649
+ expect(screen.queryByText('Please complete your selection to continue.')).not.toBeInTheDocument()
650
+ })
651
+
652
+ it('keeps ordinary data modules hidden while authored incomplete-filter content renders', () => {
653
+ const initialState = {
654
+ config: {
655
+ type: 'dashboard',
656
+ dashboard: {
657
+ title: 'Dashboard Title',
658
+ titleStyle: 'small',
659
+ theme: 'theme-blue',
660
+ sharedFilters: [
661
+ {
662
+ key: 'Region',
663
+ type: 'datafilter',
664
+ columnName: 'region',
665
+ showDropdown: true,
666
+ active: '',
667
+ usedBy: ['markup-incomplete']
668
+ }
669
+ ]
670
+ },
671
+ visualizations: {
672
+ 'markup-incomplete': {
673
+ type: 'markup-include',
674
+ contentEditor: {
675
+ inlineHTML: '<p>Select filters authored content</p>',
676
+ showHeader: true,
677
+ srcUrl: '',
678
+ title: 'Incomplete',
679
+ useInlineHTML: true
680
+ }
681
+ },
682
+ 'markup-results': {
683
+ type: 'markup-include',
684
+ dataKey: 'results-data',
685
+ contentEditor: {
686
+ inlineHTML: '<p>Filtered results content</p>',
687
+ showHeader: true,
688
+ srcUrl: '',
689
+ title: 'Results',
690
+ useInlineHTML: true
691
+ }
692
+ }
693
+ },
694
+ rows: [
695
+ {
696
+ columns: [
697
+ {
698
+ width: 12,
699
+ conditionalWidgets: [
700
+ {
701
+ widget: 'markup-incomplete',
702
+ dashboardCondition: {
703
+ id: 'filters-incomplete-condition',
704
+ operator: 'filtersIncomplete'
705
+ }
706
+ }
707
+ ]
708
+ }
709
+ ],
710
+ expandCollapseAllButtons: false
711
+ },
712
+ {
713
+ columns: [{ width: 12, widget: 'markup-results' }],
714
+ expandCollapseAllButtons: false
715
+ }
716
+ ],
717
+ datasets: {
718
+ 'results-data': { data: [{ region: 'North' }] }
719
+ },
720
+ table: {}
721
+ },
722
+ data: {
723
+ 'results-data': [{ region: 'North' }]
724
+ },
725
+ loading: false,
726
+ filteredData: {
727
+ 'filters-incomplete-condition': [{}]
728
+ },
729
+ preview: false,
730
+ tabSelected: 'Dashboard Preview',
731
+ filtersApplied: true
732
+ } as InitialState
733
+
734
+ render(<CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />)
735
+
736
+ expect(screen.getByText('Select filters authored content')).toBeInTheDocument()
737
+ expect(screen.queryByText('Filtered results content')).not.toBeInTheDocument()
738
+ expect(screen.queryByText('Please complete your selection to continue.')).not.toBeInTheDocument()
739
+ })
740
+
741
+ it('does not let dashboard filter widgets or condition-only datasets force the legacy message', () => {
742
+ const initialState = {
743
+ config: {
744
+ type: 'dashboard',
745
+ dashboard: {
746
+ title: 'Dashboard Title',
747
+ titleStyle: 'small',
748
+ theme: 'theme-blue',
749
+ sharedFilters: [
750
+ {
751
+ key: 'Region',
752
+ type: 'datafilter',
753
+ columnName: 'region',
754
+ showDropdown: true,
755
+ active: '',
756
+ usedBy: ['markup-incomplete']
757
+ }
758
+ ]
759
+ },
760
+ visualizations: {
761
+ 'dashboard-filters': {
762
+ type: 'dashboardFilters',
763
+ visualizationType: 'dashboardFilters',
764
+ sharedFilterIndexes: [0]
765
+ },
766
+ 'markup-incomplete': {
767
+ type: 'markup-include',
768
+ contentEditor: {
769
+ inlineHTML: '<p>Select filters authored content</p>',
770
+ showHeader: true,
771
+ srcUrl: '',
772
+ title: 'Incomplete',
773
+ useInlineHTML: true
774
+ }
775
+ }
776
+ },
777
+ rows: [
778
+ { columns: [{ width: 12, widget: 'dashboard-filters' }], expandCollapseAllButtons: false },
779
+ {
780
+ columns: [
781
+ {
782
+ width: 12,
783
+ conditionalWidgets: [
784
+ {
785
+ widget: 'markup-incomplete',
786
+ dashboardCondition: {
787
+ id: 'filters-incomplete-condition',
788
+ operator: 'filtersIncomplete'
789
+ }
790
+ }
791
+ ]
792
+ }
793
+ ],
794
+ dashboardCondition: {
795
+ id: 'condition-only-data',
796
+ datasetKey: 'condition-data',
797
+ operator: 'hasData'
798
+ },
799
+ expandCollapseAllButtons: false
800
+ }
801
+ ],
802
+ datasets: {
803
+ 'condition-data': { data: [{ region: 'North' }] }
804
+ },
805
+ table: {}
806
+ },
807
+ data: {
808
+ 'condition-data': [{ region: 'North' }]
809
+ },
810
+ loading: false,
811
+ filteredData: {
812
+ 'filters-incomplete-condition': [{}],
813
+ 'condition-only-data': [{ region: 'North' }]
814
+ },
815
+ preview: false,
816
+ tabSelected: 'Dashboard Preview',
817
+ filtersApplied: true
818
+ } as InitialState
819
+
820
+ render(<CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />)
821
+
822
+ expect(screen.getByText('Select filters authored content')).toBeInTheDocument()
823
+ expect(screen.queryByText('Please complete your selection to continue.')).not.toBeInTheDocument()
824
+ })
825
+
826
+ it('does not show the legacy message while condition-only data is loading', () => {
827
+ const initialState = {
828
+ config: {
829
+ type: 'dashboard',
830
+ dashboard: {
831
+ title: 'Dashboard Title',
832
+ titleStyle: 'small',
833
+ theme: 'theme-blue',
834
+ sharedFilters: [
835
+ {
836
+ key: 'Region',
837
+ type: 'datafilter',
838
+ columnName: 'region',
839
+ showDropdown: true,
840
+ active: '',
841
+ usedBy: ['markup-incomplete', 1]
842
+ }
843
+ ]
844
+ },
845
+ visualizations: {
846
+ 'markup-incomplete': {
847
+ type: 'markup-include',
848
+ contentEditor: {
849
+ inlineHTML: '<p>Select filters authored content</p>',
850
+ showHeader: true,
851
+ srcUrl: '',
852
+ title: 'Incomplete',
853
+ useInlineHTML: true
854
+ }
855
+ },
856
+ 'markup-results': {
857
+ type: 'markup-include',
858
+ contentEditor: {
859
+ inlineHTML: '<p>Filtered results content</p>',
860
+ showHeader: true,
861
+ srcUrl: '',
862
+ title: 'Results',
863
+ useInlineHTML: true
864
+ }
865
+ }
866
+ },
867
+ rows: [
868
+ {
869
+ columns: [
870
+ {
871
+ width: 12,
872
+ conditionalWidgets: [
873
+ {
874
+ widget: 'markup-incomplete',
875
+ dashboardCondition: {
876
+ id: 'filters-incomplete-condition',
877
+ operator: 'filtersIncomplete'
878
+ }
879
+ }
880
+ ]
881
+ }
882
+ ],
883
+ expandCollapseAllButtons: false
884
+ },
885
+ {
886
+ columns: [{ width: 12, widget: 'markup-results' }],
887
+ dashboardCondition: {
888
+ id: 'condition-only-data',
889
+ datasetKey: 'condition-data',
890
+ operator: 'hasData'
891
+ },
892
+ expandCollapseAllButtons: false
893
+ }
894
+ ],
895
+ datasets: {
896
+ 'condition-data': { data: [{ region: 'North' }] }
897
+ },
898
+ table: {}
899
+ },
900
+ data: {},
901
+ loading: false,
902
+ filteredData: {
903
+ 'filters-incomplete-condition': [{}]
904
+ },
905
+ preview: false,
906
+ tabSelected: 'Dashboard Preview',
907
+ filtersApplied: true
908
+ } as InitialState
909
+
910
+ render(<CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />)
911
+
912
+ expect(screen.getByText('Select filters authored content')).toBeInTheDocument()
913
+ expect(screen.queryByText('Please complete your selection to continue.')).not.toBeInTheDocument()
914
+ })
915
+
916
+ it('renders normal conditioned content after filters become complete', () => {
917
+ const initialState = {
918
+ config: {
919
+ type: 'dashboard',
920
+ dashboard: {
921
+ title: 'Dashboard Title',
922
+ titleStyle: 'small',
923
+ theme: 'theme-blue',
924
+ sharedFilters: [
925
+ {
926
+ key: 'Region',
927
+ type: 'datafilter',
928
+ columnName: 'region',
929
+ showDropdown: true,
930
+ active: 'North',
931
+ usedBy: ['markup-incomplete', 'markup-results']
932
+ }
933
+ ]
934
+ },
935
+ visualizations: {
936
+ 'markup-incomplete': {
937
+ type: 'markup-include',
938
+ contentEditor: {
939
+ inlineHTML: '<p>Select filters authored content</p>',
940
+ showHeader: true,
941
+ srcUrl: '',
942
+ title: 'Incomplete',
943
+ useInlineHTML: true
944
+ }
945
+ },
946
+ 'markup-results': {
947
+ type: 'markup-include',
948
+ contentEditor: {
949
+ inlineHTML: '<p>Filtered results content</p>',
950
+ showHeader: true,
951
+ srcUrl: '',
952
+ title: 'Results',
953
+ useInlineHTML: true
954
+ }
955
+ }
956
+ },
957
+ rows: [
958
+ {
959
+ columns: [
960
+ {
961
+ width: 12,
962
+ conditionalWidgets: [
963
+ {
964
+ widget: 'markup-incomplete',
965
+ dashboardCondition: {
966
+ id: 'filters-incomplete-condition',
967
+ operator: 'filtersIncomplete'
968
+ }
969
+ },
970
+ {
971
+ widget: 'markup-results',
972
+ dashboardCondition: {
973
+ id: 'has-data-condition',
974
+ datasetKey: 'condition-data',
975
+ operator: 'hasData'
976
+ }
977
+ }
978
+ ]
979
+ }
980
+ ],
981
+ expandCollapseAllButtons: false
982
+ }
983
+ ],
984
+ datasets: {
985
+ 'condition-data': { data: [{ region: 'North' }] }
986
+ },
987
+ table: {}
988
+ },
989
+ data: {
990
+ 'condition-data': [{ region: 'North' }]
991
+ },
992
+ loading: false,
993
+ filteredData: {
994
+ 'filters-incomplete-condition': [],
995
+ 'has-data-condition': [{ region: 'North' }]
996
+ },
997
+ preview: false,
998
+ tabSelected: 'Dashboard Preview',
999
+ filtersApplied: true
1000
+ } as InitialState
1001
+
1002
+ render(<CdcDashboardComponent initialState={initialState} interactionLabel='dashboard-test' isEditor={false} />)
1003
+
1004
+ expect(screen.queryByText('Select filters authored content')).not.toBeInTheDocument()
1005
+ expect(screen.getByText('Filtered results content')).toBeInTheDocument()
1006
+ })
74
1007
  })