@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
@@ -2,6 +2,8 @@
2
2
 
3
3
  $blue-light: #8bc6f7;
4
4
  $red: #f74242;
5
+ $copy-paste-orange: #b86f33;
6
+ $copy-paste-orange-bg: #fff7ef;
5
7
 
6
8
  .layout-container {
7
9
  display: flex;
@@ -143,11 +145,10 @@ $red: #f74242;
143
145
  align-items: center;
144
146
  position: relative;
145
147
  height: 180px;
146
- margin-left: 0.5rem;
147
- margin-right: 0.5rem;
148
148
  background-color: transparent;
149
149
  border: 2px dashed #c2c2c2;
150
150
  flex: 1 1 auto;
151
+ min-width: 0;
151
152
 
152
153
  &:hover {
153
154
  border-color: color.adjust(#c2c2c2, $lightness: -20%);
@@ -157,6 +158,49 @@ $red: #f74242;
157
158
  background: none;
158
159
  border: none;
159
160
  }
161
+
162
+ &.column--paste-ready {
163
+ background-color: $copy-paste-orange-bg;
164
+ border-color: $copy-paste-orange;
165
+ cursor: copy;
166
+ }
167
+ }
168
+
169
+ .builder-column--conditional {
170
+ align-items: stretch;
171
+ background: none;
172
+ border: none;
173
+ flex-direction: column;
174
+ gap: 0.75rem;
175
+ height: auto;
176
+ justify-content: flex-start;
177
+ }
178
+
179
+ .builder-column--conditional__slot {
180
+ align-items: center;
181
+ background-color: transparent;
182
+ border: 2px dashed #c2c2c2;
183
+ display: flex;
184
+ flex: 0 0 180px;
185
+ justify-content: center;
186
+ min-height: 180px;
187
+ position: relative;
188
+ width: 100%;
189
+
190
+ &:hover {
191
+ border-color: color.adjust(#c2c2c2, $lightness: -20%);
192
+ }
193
+
194
+ &.column--populated {
195
+ background: none;
196
+ border: none;
197
+ }
198
+
199
+ &.column--paste-ready {
200
+ background-color: $copy-paste-orange-bg;
201
+ border-color: $copy-paste-orange;
202
+ cursor: copy;
203
+ }
160
204
  }
161
205
 
162
206
  .column--drop {
@@ -191,6 +235,20 @@ $red: #f74242;
191
235
  color: var(--mediumGray);
192
236
  }
193
237
 
238
+ .builder-column__hint {
239
+ display: block;
240
+ margin-top: 0.25rem;
241
+ font-size: 13px;
242
+ line-height: 15px;
243
+ }
244
+
245
+ .column--paste-ready .builder-column__text {
246
+ max-width: 16rem;
247
+ overflow-wrap: normal;
248
+ white-space: normal;
249
+ width: calc(100% - 2rem);
250
+ }
251
+
194
252
  .widget {
195
253
  display: flex;
196
254
  height: 100%;
@@ -204,6 +262,7 @@ $red: #f74242;
204
262
  padding: 0;
205
263
  position: relative;
206
264
  cursor: move;
265
+ overflow: hidden;
207
266
 
208
267
  &:hover {
209
268
  $lightGray: #c7c7c7;
@@ -220,6 +279,15 @@ $red: #f74242;
220
279
  }
221
280
  }
222
281
 
282
+ &.widget--copied-source {
283
+ border-color: $copy-paste-orange;
284
+ box-shadow: 0 0 0 2px $copy-paste-orange, 0 0 0 5px rgba(184, 111, 51, 0.18);
285
+ }
286
+
287
+ &.widget--copied-source:hover {
288
+ border-color: $copy-paste-orange;
289
+ }
290
+
223
291
  .drag-icon,
224
292
  .drag-icon svg {
225
293
  fill: var(--lightGray);
@@ -233,6 +301,42 @@ $red: #f74242;
233
301
  }
234
302
  }
235
303
 
304
+ .widget__copied-badge {
305
+ align-items: center;
306
+ background: $copy-paste-orange-bg;
307
+ border: 1px solid $copy-paste-orange;
308
+ border-radius: 999px;
309
+ color: $copy-paste-orange;
310
+ cursor: pointer;
311
+ display: inline-flex;
312
+ font-size: 0.75rem;
313
+ font-weight: 600;
314
+ gap: 0.35rem;
315
+ left: 0.5rem;
316
+ line-height: 1;
317
+ padding: 0.25rem 0.45rem 0.25rem 0.55rem;
318
+ position: absolute;
319
+ top: 0.5rem;
320
+ z-index: 2;
321
+
322
+ svg {
323
+ display: block;
324
+ height: 0.65rem;
325
+ margin: 0;
326
+ width: 0.65rem;
327
+ }
328
+
329
+ &:hover,
330
+ &:focus {
331
+ background: color.adjust($copy-paste-orange-bg, $lightness: -3%);
332
+ outline: none;
333
+ }
334
+
335
+ &:focus-visible {
336
+ box-shadow: 0 0 0 2px #fff, 0 0 0 4px $copy-paste-orange;
337
+ }
338
+ }
339
+
236
340
  .widget__media {
237
341
  display: flex;
238
342
  justify-content: center;
@@ -263,12 +367,97 @@ $red: #f74242;
263
367
  flex-direction: column;
264
368
  align-items: center;
265
369
  justify-content: center;
370
+ min-height: 0;
371
+ overflow: hidden;
266
372
 
267
373
  svg {
268
374
  margin-bottom: 0.5em;
269
375
  }
270
376
  }
271
377
 
378
+ .widget--in-row .widget__content--with-menu:not(.widget__content--has-condition) {
379
+ justify-content: flex-start;
380
+ padding-top: 2.75rem;
381
+ }
382
+
383
+ .widget--in-row .widget__content--with-menu.widget__content--has-condition {
384
+ justify-content: flex-start;
385
+ padding-top: 2rem;
386
+ }
387
+
388
+ .widget--in-row .widget__summary {
389
+ align-items: center;
390
+ display: inline-flex;
391
+ gap: 0.75rem;
392
+ justify-content: center;
393
+ margin-bottom: 0.75rem;
394
+ max-width: 100%;
395
+ min-width: 0;
396
+ }
397
+
398
+ .widget--in-row .widget__type-icon {
399
+ align-items: center;
400
+ display: inline-flex;
401
+ flex: 0 0 auto;
402
+ height: 42px;
403
+ justify-content: center;
404
+ width: 42px;
405
+
406
+ svg {
407
+ display: block;
408
+ flex: 0 0 auto;
409
+ height: 42px;
410
+ margin: 0;
411
+ max-height: 42px;
412
+ max-width: 42px;
413
+ width: 42px;
414
+ }
415
+ }
416
+
417
+ .widget--in-row .widget__summary-text {
418
+ display: flex;
419
+ flex: 1 1 auto;
420
+ flex-direction: column;
421
+ font-size: 16px;
422
+ justify-content: center;
423
+ line-height: 1.2;
424
+ min-height: 42px;
425
+ min-width: 0;
426
+ text-align: left;
427
+ }
428
+
429
+ .widget--in-row .widget__type-label,
430
+ .widget--in-row .widget__title {
431
+ font-size: 16px;
432
+ line-height: 1.2;
433
+ overflow-wrap: break-word;
434
+ }
435
+
436
+ .widget--in-row .widget__title {
437
+ display: block;
438
+ flex: 0 1 auto;
439
+ max-width: 100%;
440
+ min-height: 0;
441
+ overflow: hidden;
442
+ text-align: center;
443
+ }
444
+
445
+ .widget--in-row .widget__type-label {
446
+ font-weight: 600;
447
+ }
448
+
449
+ .widget-menu .cove-button.btn-configure.btn-configure--copy.is-active {
450
+ background: $copy-paste-orange-bg !important;
451
+ box-shadow: inset 0 0 0 1px $copy-paste-orange;
452
+ color: $copy-paste-orange;
453
+ }
454
+
455
+ .widget-menu .cove-button.btn-configure.btn-configure--copy.is-active:hover:not(:disabled),
456
+ .widget-menu .cove-button.btn-configure.btn-configure--copy.is-active:active:not(:disabled) {
457
+ background: color.adjust($copy-paste-orange-bg, $lightness: -3%) !important;
458
+ box-shadow: inset 0 0 0 1px $copy-paste-orange;
459
+ }
460
+
272
461
  .widget-card {
273
462
  position: relative;
274
463
  display: flex;
@@ -291,29 +480,64 @@ $red: #f74242;
291
480
  .builder-row {
292
481
  position: relative;
293
482
 
483
+ .builder-row__label {
484
+ align-items: center;
485
+ color: var(--darkGray);
486
+ display: inline-flex;
487
+ font-size: 16px;
488
+ left: 1rem;
489
+ line-height: 1;
490
+ min-height: 28px;
491
+ position: absolute;
492
+ top: 0.375rem;
493
+ }
494
+
294
495
  .btn-configure-row {
295
496
  align-items: center;
296
497
  background: transparent !important;
297
498
  border: 0;
298
- border-radius: 0;
499
+ border-radius: 999px;
299
500
  box-shadow: none;
300
- color: var(--blue);
501
+ color: var(--mediumGray);
301
502
  display: inline-flex;
503
+ height: 28px;
302
504
  justify-content: center;
303
- min-height: 32px;
304
- min-width: 32px;
305
- padding: 0.375rem;
505
+ line-height: 1;
506
+ min-height: 28px;
507
+ min-width: 28px;
508
+ padding: 4px;
306
509
  position: absolute;
307
- right: 1em;
308
- top: 0;
510
+ right: 1rem;
511
+ top: 0.375rem;
309
512
  transform: none;
513
+ width: 28px;
310
514
 
311
515
  &:hover:not(:disabled),
312
516
  &:active:not(:disabled) {
313
- background: transparent !important;
517
+ background: rgba(0, 94, 170, 0.08) !important;
314
518
  box-shadow: none;
315
519
  transform: none;
316
520
  }
521
+
522
+ &.btn-configure-row--data {
523
+ right: calc(1rem + 28px + 6px);
524
+ }
525
+
526
+ &.btn-configure-row--condition {
527
+ right: 1rem;
528
+ }
529
+
530
+ &.is-active {
531
+ background: var(--dashboard-condition-active-blue-bg, #f2f7fb) !important;
532
+ box-shadow: inset 0 0 0 1px var(--dashboard-condition-active-blue, var(--blue));
533
+ color: var(--dashboard-condition-active-blue, var(--blue));
534
+ }
535
+
536
+ &.is-active:hover:not(:disabled),
537
+ &.is-active:active:not(:disabled) {
538
+ background: var(--dashboard-condition-active-blue-bg-hover, #e6eff7) !important;
539
+ box-shadow: inset 0 0 0 1px var(--dashboard-condition-active-blue, var(--blue));
540
+ }
317
541
  }
318
542
 
319
543
  .footnotes {
@@ -329,7 +553,7 @@ $red: #f74242;
329
553
  }
330
554
  }
331
555
 
332
- padding: 2em 1em 1em;
556
+ padding: 2.125rem 1em 1em;
333
557
  border: 1px solid #c2c2c2;
334
558
  transition: border 300ms cubic-bezier(0.16, 1, 0.3, 1);
335
559
  background-color: #f2f2f2;
@@ -342,19 +566,16 @@ $red: #f74242;
342
566
  .column-container {
343
567
  display: flex;
344
568
  flex-flow: row;
569
+ gap: 1rem;
345
570
  width: 100%;
346
571
  }
347
572
 
348
573
  .widget__content {
349
- padding: 0 2em;
350
-
351
- > svg {
352
- width: 100%;
353
- height: auto;
354
- max-width: 100px;
355
- max-height: 80px;
356
- flex-grow: 1;
357
- }
574
+ padding: 0 1em;
575
+ }
576
+
577
+ .widget--in-row .widget__content--with-menu:not(.widget__content--has-condition) {
578
+ padding-top: 2rem;
358
579
  }
359
580
 
360
581
  &:hover {
@@ -377,7 +598,15 @@ $red: #f74242;
377
598
 
378
599
  .row-menu__btn svg,
379
600
  .builder-row .btn-configure-row svg {
601
+ display: block;
602
+ height: 1.1em;
380
603
  margin-bottom: 0;
381
604
  top: 0;
382
605
  vertical-align: middle;
606
+ width: 1.1em;
607
+ }
608
+
609
+ .builder-row .btn-configure-row--condition svg {
610
+ transform: scale(1.15);
611
+ transform-origin: center;
383
612
  }
@@ -1,10 +1,15 @@
1
1
  @import '@cdc/core/styles/utils/breakpoints';
2
+ @import '@cdc/core/styles/layout/callout';
2
3
 
3
4
  button.row-menu__btn[title*='Equal Height Rows'] {
4
5
  margin-left: 15px;
5
6
  }
6
7
 
7
8
  .cove-visualization.type-dashboard {
9
+ --dashboard-condition-active-blue: var(--blue);
10
+ --dashboard-condition-active-blue-bg: #f2f7fb;
11
+ --dashboard-condition-active-blue-bg-hover: #e6eff7;
12
+
8
13
  @import 'editor-panel';
9
14
  @import 'grid';
10
15
 
@@ -66,6 +71,26 @@ button.row-menu__btn[title*='Equal Height Rows'] {
66
71
  height: 25px;
67
72
  }
68
73
 
74
+ .download-image-controls {
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: flex-start;
78
+ flex-basis: 100%;
79
+ }
80
+
81
+ .download-image-mode-select {
82
+ width: 14em;
83
+ max-width: 100%;
84
+ height: 25px;
85
+ }
86
+
87
+ .download-image-label-input {
88
+ width: 14em;
89
+ max-width: 14em;
90
+ margin-top: 5px;
91
+ height: 25px;
92
+ }
93
+
69
94
  label {
70
95
  display: block;
71
96
  width: 100%;
@@ -196,18 +221,26 @@ button.row-menu__btn[title*='Equal Height Rows'] {
196
221
 
197
222
  // Override Bootstrap's row negative margins to prevent horizontal scrollbar
198
223
  .row {
224
+ --dashboard-column-gap: 1.5rem;
199
225
  margin-right: 0;
200
226
  margin-left: 0;
227
+ column-gap: 0;
201
228
  // Row spacing for desktop
202
229
  margin-bottom: 1.5rem;
203
230
 
204
- // Remove padding from first and last columns to keep content flush to edges
205
- > [class*='col-']:first-child {
231
+ > [class*='col-'] {
232
+ // Remove side padding when columns stack to full width.
206
233
  padding-left: 0;
234
+ padding-right: 0;
235
+ flex: 0 0 100%;
236
+ max-width: 100%;
237
+ width: 100%;
238
+ margin-bottom: 1.5rem;
207
239
  }
208
240
 
241
+ // Last column doesn't need margin (row margin handles gap to next row)
209
242
  > [class*='col-']:last-child {
210
- padding-right: 0;
243
+ margin-bottom: 0;
211
244
  }
212
245
 
213
246
  // Last row doesn't need bottom margin
@@ -216,30 +249,6 @@ button.row-menu__btn[title*='Equal Height Rows'] {
216
249
  }
217
250
  }
218
251
 
219
- // Mobile layout adjustments
220
- @include breakpoint(xs) {
221
- .row {
222
- margin-bottom: 1.5rem;
223
-
224
- > [class*='col-'] {
225
- // Remove side padding when columns stack to full width
226
- padding-left: 0;
227
- padding-right: 0;
228
- margin-bottom: 1.5rem;
229
- }
230
-
231
- // Last column doesn't need margin (row margin handles gap to next row)
232
- > [class*='col-']:last-child {
233
- margin-bottom: 0;
234
- }
235
-
236
- // Last row doesn't need extra margin
237
- &:last-child {
238
- margin-bottom: 0;
239
- }
240
- }
241
- }
242
-
243
252
  .dashboard-row {
244
253
  display: flex;
245
254
  flex-direction: column;
@@ -274,7 +283,43 @@ button.row-menu__btn[title*='Equal Height Rows'] {
274
283
  width: 100%;
275
284
  }
276
285
 
277
- @include breakpointClass(md) {
286
+ @include breakpoint(md) {
287
+ .row {
288
+ column-gap: var(--dashboard-column-gap);
289
+
290
+ > [class*='col-'] {
291
+ min-width: 0;
292
+ flex-grow: 0;
293
+ flex-shrink: 0;
294
+ margin-bottom: 0;
295
+ }
296
+
297
+ > .col-md-12 {
298
+ flex-basis: 100%;
299
+ max-width: 100%;
300
+ width: 100%;
301
+ }
302
+ > .col-md-8 {
303
+ flex-basis: calc((100% * 8 / 12) - (var(--dashboard-column-gap) * 4 / 12));
304
+ max-width: calc((100% * 8 / 12) - (var(--dashboard-column-gap) * 4 / 12));
305
+ width: calc((100% * 8 / 12) - (var(--dashboard-column-gap) * 4 / 12));
306
+ }
307
+ > .col-md-6 {
308
+ flex-basis: calc((100% * 6 / 12) - (var(--dashboard-column-gap) * 6 / 12));
309
+ max-width: calc((100% * 6 / 12) - (var(--dashboard-column-gap) * 6 / 12));
310
+ width: calc((100% * 6 / 12) - (var(--dashboard-column-gap) * 6 / 12));
311
+ }
312
+ > .col-md-4 {
313
+ flex-basis: calc((100% * 4 / 12) - (var(--dashboard-column-gap) * 8 / 12));
314
+ max-width: calc((100% * 4 / 12) - (var(--dashboard-column-gap) * 8 / 12));
315
+ width: calc((100% * 4 / 12) - (var(--dashboard-column-gap) * 8 / 12));
316
+ }
317
+ > .col-md-3 {
318
+ flex-basis: calc((100% * 3 / 12) - (var(--dashboard-column-gap) * 9 / 12));
319
+ max-width: calc((100% * 3 / 12) - (var(--dashboard-column-gap) * 9 / 12));
320
+ width: calc((100% * 3 / 12) - (var(--dashboard-column-gap) * 9 / 12));
321
+ }
322
+ }
278
323
  .dashboard-row {
279
324
  flex-direction: row;
280
325
  margin: 1em 0;
@@ -293,6 +338,10 @@ button.row-menu__btn[title*='Equal Height Rows'] {
293
338
  .dashboard-col-4 {
294
339
  width: 33%;
295
340
  }
341
+
342
+ .dashboard-col-3 {
343
+ width: 25%;
344
+ }
296
345
  }
297
346
 
298
347
  @include breakpointClass(sm) {
@@ -325,7 +374,6 @@ button.row-menu__btn[title*='Equal Height Rows'] {
325
374
  [class*='col-'] {
326
375
  display: flex;
327
376
  flex-direction: column !important;
328
- flex: 1 1 auto !important;
329
377
  align-items: stretch !important;
330
378
  min-height: 0 !important;
331
379
 
@@ -525,6 +573,37 @@ button.row-menu__btn[title*='Equal Height Rows'] {
525
573
  min-height: 0 !important;
526
574
  }
527
575
  }
576
+ @include breakpoint(md) {
577
+ > .col-md-12 {
578
+ flex-basis: 100% !important;
579
+ max-width: 100% !important;
580
+ width: 100% !important;
581
+ }
582
+
583
+ > .col-md-8 {
584
+ flex-basis: calc((100% * 8 / 12) - (var(--dashboard-column-gap) * 4 / 12)) !important;
585
+ max-width: calc((100% * 8 / 12) - (var(--dashboard-column-gap) * 4 / 12)) !important;
586
+ width: calc((100% * 8 / 12) - (var(--dashboard-column-gap) * 4 / 12)) !important;
587
+ }
588
+
589
+ > .col-md-6 {
590
+ flex-basis: calc((100% * 6 / 12) - (var(--dashboard-column-gap) * 6 / 12)) !important;
591
+ max-width: calc((100% * 6 / 12) - (var(--dashboard-column-gap) * 6 / 12)) !important;
592
+ width: calc((100% * 6 / 12) - (var(--dashboard-column-gap) * 6 / 12)) !important;
593
+ }
594
+
595
+ > .col-md-4 {
596
+ flex-basis: calc((100% * 4 / 12) - (var(--dashboard-column-gap) * 8 / 12)) !important;
597
+ max-width: calc((100% * 4 / 12) - (var(--dashboard-column-gap) * 8 / 12)) !important;
598
+ width: calc((100% * 4 / 12) - (var(--dashboard-column-gap) * 8 / 12)) !important;
599
+ }
600
+
601
+ > .col-md-3 {
602
+ flex-basis: calc((100% * 3 / 12) - (var(--dashboard-column-gap) * 9 / 12)) !important;
603
+ max-width: calc((100% * 3 / 12) - (var(--dashboard-column-gap) * 9 / 12)) !important;
604
+ width: calc((100% * 3 / 12) - (var(--dashboard-column-gap) * 9 / 12)) !important;
605
+ }
606
+ }
528
607
  }
529
608
 
530
609
  // TP5 adjustments when there are 2+ data bites in a row
@@ -5,19 +5,31 @@ import { ConfigRow } from '../types/ConfigRow'
5
5
  import { AnyVisualization } from '@cdc/core/types/Visualization'
6
6
  import { SharedFilter } from '../types/SharedFilter'
7
7
 
8
- type ADD_VISUALIZATION = Action<'ADD_VISUALIZATION', { rowIdx: number; colIdx: number; newViz: AnyVisualization }>
8
+ type ADD_VISUALIZATION = Action<
9
+ 'ADD_VISUALIZATION',
10
+ { rowIdx: number; colIdx: number; entryIdx?: number; newViz: AnyVisualization }
11
+ >
9
12
  type APPLY_CONFIG = Action<'APPLY_CONFIG', [Config, Object?]>
10
13
  type DELETE_WIDGET = Action<'DELETE_WIDGET', { uid: string }>
14
+ type CLONE_VISUALIZATION = Action<
15
+ 'CLONE_VISUALIZATION',
16
+ { sourceWidgetKey: string; rowIdx: number; colIdx: number; entryIdx?: number }
17
+ >
11
18
  type MOVE_VISUALIZATION = Action<
12
19
  'MOVE_VISUALIZATION',
13
- { rowIdx: number; colIdx: number; widget: AnyVisualization & { rowIdx: number; colIdx: number } }
20
+ {
21
+ rowIdx: number
22
+ colIdx: number
23
+ entryIdx?: number
24
+ widget: AnyVisualization & { rowIdx: number; colIdx: number; entryIdx?: number }
25
+ }
14
26
  >
15
27
  type SET_CONFIG = Action<'SET_CONFIG', Partial<Config> & { activeDashboard?: number }>
16
28
  type UPDATE_CONFIG = Action<'UPDATE_CONFIG', [Config, Object?]>
17
- type SET_DATA = Action<'SET_DATA', Record<string, any[]>>
29
+ type SET_DATA = Action<'SET_DATA', { data: Record<string, any[]>; activeDashboard?: number }>
18
30
  type SET_LOADING = Action<'SET_LOADING', boolean>
19
31
  type SET_PREVIEW = Action<'SET_PREVIEW', boolean>
20
- type SET_FILTERED_DATA = Action<'SET_FILTERED_DATA', Object>
32
+ type SET_FILTERED_DATA = Action<'SET_FILTERED_DATA', { filteredData: Object; activeDashboard?: number }>
21
33
  type SET_SHARED_FILTERS = Action<'SET_SHARED_FILTERS', SharedFilter[]>
22
34
  type SET_TAB_SELECTED = Action<'SET_TAB_SELECTED', Tab>
23
35
  type RENAME_DASHBOARD_TAB = Action<'RENAME_DASHBOARD_TAB', { current: string; new: string }>
@@ -38,6 +50,7 @@ type DashboardActions =
38
50
  | ADD_VISUALIZATION
39
51
  | APPLY_CONFIG
40
52
  | ADD_NEW_DASHBOARD
53
+ | CLONE_VISUALIZATION
41
54
  | DELETE_WIDGET
42
55
  | MOVE_VISUALIZATION
43
56
  | SET_CONFIG