@cdc/dashboard 4.26.3 → 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 (151) hide show
  1. package/CONFIG.md +219 -0
  2. package/README.md +60 -20
  3. package/dist/cdcdashboard-CY9IcPSi.es.js +6 -0
  4. package/dist/cdcdashboard-DlpiY3fQ.es.js +4 -0
  5. package/dist/cdcdashboard.js +61559 -58048
  6. package/examples/__data__/data-2.json +6 -0
  7. package/examples/__data__/data.json +6 -0
  8. package/examples/dashboard-conditions-filters-incomplete.json +221 -0
  9. package/examples/dashboard-missing-datasets-multi.json +174 -0
  10. package/examples/dashboard-missing-datasets-single.json +121 -0
  11. package/examples/dashboard-multi-dashboard-version-regression.json +146 -0
  12. package/examples/dashboard-shared-filter-row-delete-cleanup.json +186 -0
  13. package/examples/dashboard-stale-dataset-keys.json +181 -0
  14. package/examples/dashboard-tiered-filter-regression.json +190 -0
  15. package/examples/legend-issue.json +1 -1
  16. package/examples/minimal-example.json +34 -0
  17. package/examples/private/cfa-dashboard.json +651 -0
  18. package/examples/private/data-bite-wrap.json +6936 -0
  19. package/examples/private/dengue.json +4640 -0
  20. package/examples/private/link_to_file.json +16662 -0
  21. package/examples/private/multi-dash-fix.json +16963 -0
  22. package/examples/private/versions.json +41612 -0
  23. package/examples/sankey.json +3 -3
  24. package/examples/test-api-filter-reset.json +4 -4
  25. package/examples/tp5-test.json +86 -4
  26. package/examples/us-map-filter-example.json +1074 -0
  27. package/package.json +9 -9
  28. package/src/CdcDashboard.tsx +6 -2
  29. package/src/CdcDashboardComponent.tsx +179 -88
  30. package/src/DashboardCopyPasteContext.test.tsx +33 -0
  31. package/src/DashboardCopyPasteContext.tsx +48 -0
  32. package/src/_stories/Dashboard.EditorRegression.stories.tsx +72 -0
  33. package/src/_stories/Dashboard.Regression.stories.tsx +196 -0
  34. package/src/_stories/Dashboard.Zoom.stories.tsx +88 -0
  35. package/src/_stories/Dashboard.smoke.stories.tsx +33 -0
  36. package/src/_stories/Dashboard.stories.tsx +337 -2
  37. package/src/_stories/FilteredTextMigrationComparison.stories.tsx +87 -0
  38. package/src/_stories/_mock/dashboard-data-driven-colors.json +171 -0
  39. package/src/_stories/_mock/tp5-test.json +86 -5
  40. package/src/components/Column.test.tsx +176 -0
  41. package/src/components/Column.tsx +214 -13
  42. package/src/components/DashboardConditionModal.test.tsx +420 -0
  43. package/src/components/DashboardConditionModal.tsx +367 -0
  44. package/src/components/DashboardConditionSummary.tsx +59 -0
  45. package/src/components/DashboardEditors.tsx +23 -0
  46. package/src/components/DashboardFilters/DashboardFilters.test.tsx +267 -0
  47. package/src/components/DashboardFilters/DashboardFilters.tsx +193 -172
  48. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.test.tsx +164 -0
  49. package/src/components/DashboardFilters/DashboardFiltersEditor/DashboardFiltersEditor.tsx +46 -6
  50. package/src/components/DashboardFilters/DashboardFiltersEditor/components/APIModal.tsx +5 -3
  51. package/src/components/DashboardFilters/DashboardFiltersEditor/components/DeleteFilterModal.tsx +59 -58
  52. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.test.tsx +304 -0
  53. package/src/components/DashboardFilters/DashboardFiltersEditor/components/FilterEditor.tsx +43 -36
  54. package/src/components/DashboardFilters/DashboardFiltersEditor/components/NestedDropDownDashboard.tsx +2 -2
  55. package/src/components/DashboardFilters/DashboardFiltersWrapper.test.tsx +142 -0
  56. package/src/components/DashboardFilters/DashboardFiltersWrapper.tsx +32 -27
  57. package/src/components/DashboardFilters/dashboardfilter.styles.css +42 -27
  58. package/src/components/DataDesignerModal.tsx +2 -1
  59. package/src/components/ExpandCollapseButtons.tsx +6 -4
  60. package/src/components/Grid.tsx +12 -7
  61. package/src/components/Header/Header.tsx +36 -17
  62. package/src/components/MultiConfigTabs/MultiConfigTabs.tsx +141 -140
  63. package/src/components/Row.test.tsx +228 -0
  64. package/src/components/Row.tsx +104 -28
  65. package/src/components/VisualizationRow.test.tsx +396 -0
  66. package/src/components/VisualizationRow.tsx +177 -51
  67. package/src/components/VisualizationsPanel/VisualizationsPanel.test.tsx +49 -0
  68. package/src/components/VisualizationsPanel/VisualizationsPanel.tsx +14 -13
  69. package/src/components/Widget/Widget.test.tsx +218 -0
  70. package/src/components/Widget/Widget.tsx +123 -20
  71. package/src/components/Widget/widget.styles.css +58 -14
  72. package/src/components/dashboard-condition-modal.css +76 -0
  73. package/src/components/dashboard-condition-summary.css +87 -0
  74. package/src/data/initial-state.js +1 -0
  75. package/src/helpers/addValuesToDashboardFilters.ts +3 -5
  76. package/src/helpers/addVisualization.ts +17 -4
  77. package/src/helpers/cloneDashboardWidget.ts +127 -0
  78. package/src/helpers/dashboardColumnWidgets.ts +99 -0
  79. package/src/helpers/dashboardConditionUi.ts +47 -0
  80. package/src/helpers/dashboardConditions.ts +200 -0
  81. package/src/helpers/dashboardFilterTargets.ts +156 -0
  82. package/src/helpers/filterData.ts +4 -9
  83. package/src/helpers/filterVisibility.ts +20 -0
  84. package/src/helpers/formatConfigBeforeSave.ts +2 -2
  85. package/src/helpers/getFilteredData.ts +18 -5
  86. package/src/helpers/getUpdateConfig.ts +43 -12
  87. package/src/helpers/getVizRowColumnLocator.ts +11 -1
  88. package/src/helpers/iconHash.tsx +9 -3
  89. package/src/helpers/mapDataToConfig.ts +31 -29
  90. package/src/helpers/reloadURLHelpers.ts +25 -5
  91. package/src/helpers/removeDashboardFilter.ts +33 -33
  92. package/src/helpers/tests/addVisualization.test.ts +53 -9
  93. package/src/helpers/tests/cloneDashboardWidget.test.ts +136 -0
  94. package/src/helpers/tests/dashboardColumnWidgets.test.ts +99 -0
  95. package/src/helpers/tests/dashboardConditionUi.test.ts +41 -0
  96. package/src/helpers/tests/dashboardConditions.test.ts +428 -0
  97. package/src/helpers/tests/formatConfigBeforeSave.test.ts +51 -0
  98. package/src/helpers/tests/getFilteredData.test.ts +265 -86
  99. package/src/helpers/tests/getUpdateConfig.test.ts +338 -0
  100. package/src/helpers/tests/reloadURLHelpers.test.ts +394 -238
  101. package/src/index.tsx +6 -3
  102. package/src/scss/grid.scss +281 -22
  103. package/src/scss/main.scss +215 -64
  104. package/src/store/dashboard.actions.ts +17 -4
  105. package/src/store/dashboard.reducer.test.ts +538 -0
  106. package/src/store/dashboard.reducer.ts +136 -22
  107. package/src/test/CdcDashboard.test.jsx +24 -0
  108. package/src/test/CdcDashboard.test.tsx +148 -0
  109. package/src/test/CdcDashboardComponent.test.tsx +935 -2
  110. package/src/types/ConfigRow.ts +15 -0
  111. package/src/types/DashboardFilters.ts +4 -0
  112. package/src/types/SharedFilter.ts +2 -0
  113. package/tests/fixtures/dashboard-config-with-metadata.json +1 -1
  114. package/dist/cdcdashboard-vr9HZwRt.es.js +0 -6
  115. package/examples/DEV-6574.json +0 -2224
  116. package/examples/api-dashboard-data.json +0 -272
  117. package/examples/api-dashboard-years.json +0 -11
  118. package/examples/api-geographies-data.json +0 -11
  119. package/examples/chart-data.json +0 -5409
  120. package/examples/custom/css/respiratory.css +0 -236
  121. package/examples/custom/js/respiratory.js +0 -242
  122. package/examples/default-data.json +0 -368
  123. package/examples/default-filter-control.json +0 -209
  124. package/examples/default-multi-dataset-shared-filter.json +0 -1729
  125. package/examples/default-multi-dataset.json +0 -506
  126. package/examples/ed-visits-county-file.json +0 -402
  127. package/examples/filters/Alabama.json +0 -72
  128. package/examples/filters/Alaska.json +0 -1737
  129. package/examples/filters/Arkansas.json +0 -4713
  130. package/examples/filters/California.json +0 -212
  131. package/examples/filters/Colorado.json +0 -1500
  132. package/examples/filters/Connecticut.json +0 -559
  133. package/examples/filters/Delaware.json +0 -63
  134. package/examples/filters/DistrictofColumbia.json +0 -63
  135. package/examples/filters/Florida.json +0 -4217
  136. package/examples/filters/States.json +0 -146
  137. package/examples/state-level.json +0 -90136
  138. package/examples/state-points.json +0 -10474
  139. package/examples/temp-example-data.json +0 -130
  140. package/examples/test-dashboard-simple.json +0 -503
  141. package/examples/test-example.json +0 -752
  142. package/examples/test-file.json +0 -147
  143. package/examples/test.json +0 -752
  144. package/examples/testing.json +0 -94456
  145. /package/examples/{data → __data__}/data-with-metadata.json +0 -0
  146. /package/examples/{legend-issue-data.json → __data__/legend-issue-data.json} +0 -0
  147. /package/examples/api-test/{categories.json → __data__/categories.json} +0 -0
  148. /package/examples/api-test/{chart-data.json → __data__/chart-data.json} +0 -0
  149. /package/examples/api-test/{topics.json → __data__/topics.json} +0 -0
  150. /package/examples/api-test/{years.json → __data__/years.json} +0 -0
  151. /package/src/_stories/{Dashboard.Pages.stories.tsx → Dashboard.Pages.smoke.stories.tsx} +0 -0
@@ -0,0 +1,87 @@
1
+ import React from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react-vite'
3
+ import CdcFilteredText from '@cdc/filtered-text/src/CdcFilteredText'
4
+ import CdcMarkupInclude from '@cdc/markup-include/src/CdcMarkupInclude'
5
+ import { assertVisualizationRendered } from '@cdc/core/helpers/testing'
6
+ import { expect, waitFor } from 'storybook/test'
7
+
8
+ const comparisonData = [
9
+ {
10
+ State: 'CA',
11
+ Message: 'Representative filtered text output'
12
+ }
13
+ ]
14
+
15
+ const meta: Meta = {
16
+ title: 'Components/Pages/Dashboard/Filtered Text Migration Comparison'
17
+ }
18
+
19
+ type Story = StoryObj
20
+
21
+ export const Standalone_Handoff_And_Migrated_Output: Story = {
22
+ render: () => (
23
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
24
+ <CdcFilteredText
25
+ config={{
26
+ type: 'filtered-text',
27
+ title: 'Legacy filtered text',
28
+ textColumn: 'Message',
29
+ data: comparisonData,
30
+ filters: [{ columnName: 'State', columnValue: 'CA' }]
31
+ }}
32
+ isEditor={false}
33
+ />
34
+ <CdcMarkupInclude
35
+ config={
36
+ {
37
+ type: 'markup-include',
38
+ theme: 'theme-blue',
39
+ data: comparisonData,
40
+ enableMarkupVariables: true,
41
+ markupVariables: [
42
+ {
43
+ sourceType: 'column',
44
+ outputType: 'value',
45
+ name: 'Message',
46
+ tag: '{{message}}',
47
+ columnName: 'Message',
48
+ conditions: [{ columnName: 'State', isOrIsNotEqualTo: 'is', value: 'CA' }],
49
+ selectionMode: 'first'
50
+ }
51
+ ],
52
+ contentEditor: {
53
+ title: 'Migrated markup include',
54
+ inlineHTML: '{{message}}',
55
+ useInlineHTML: true,
56
+ srcUrl: '',
57
+ showHeader: true,
58
+ style: 'default'
59
+ },
60
+ visual: {
61
+ border: false,
62
+ accent: false,
63
+ background: false,
64
+ hideBackgroundColor: false,
65
+ borderColorTheme: false
66
+ }
67
+ } as any
68
+ }
69
+ configUrl=''
70
+ datasets={{}}
71
+ isEditor={false}
72
+ isDashboard={false}
73
+ setConfig={() => {}}
74
+ />
75
+ </div>
76
+ ),
77
+ play: async ({ canvasElement }) => {
78
+ await assertVisualizationRendered(canvasElement)
79
+ await waitFor(() => {
80
+ expect(canvasElement.textContent).toContain('Filtered Text Has Been Migrated')
81
+ expect(canvasElement.textContent).toContain('Use the markup-include package to render this visualization.')
82
+ expect(canvasElement.textContent?.match(/Representative filtered text output/g)?.length).toBe(1)
83
+ })
84
+ }
85
+ }
86
+
87
+ export default meta
@@ -0,0 +1,171 @@
1
+ {
2
+ "type": "dashboard",
3
+ "title": "COVID-19 County Case Tracker",
4
+ "description": "Select a state to view COVID-19 case counts by county. Background colors reflect the current alert status.",
5
+ "datasets": {
6
+ "covid-counties": {
7
+ "data": [
8
+ {
9
+ "State": "California",
10
+ "County1": "Los Angeles", "Cases1": "12847", "Status1": "Critical",
11
+ "County2": "San Diego", "Cases2": "4521", "Status2": "High",
12
+ "County3": "Sacramento", "Cases3": "1893", "Status3": "Medium"
13
+ },
14
+ {
15
+ "State": "Texas",
16
+ "County1": "Harris", "Cases1": "9734", "Status1": "High",
17
+ "County2": "Dallas", "Cases2": "7218", "Status2": "High",
18
+ "County3": "Travis", "Cases3": "2105", "Status3": "Medium"
19
+ },
20
+ {
21
+ "State": "New York",
22
+ "County1": "Kings", "Cases1": "3842", "Status1": "Medium",
23
+ "County2": "Queens", "Cases2": "3156", "Status2": "Medium",
24
+ "County3": "Erie", "Cases3": "987", "Status3": "Low"
25
+ },
26
+ {
27
+ "State": "Florida",
28
+ "County1": "Miami-Dade", "Cases1": "11203", "Status1": "Critical",
29
+ "County2": "Broward", "Cases2": "6890", "Status2": "High",
30
+ "County3": "Hillsborough", "Cases3": "3214", "Status3": "Medium"
31
+ },
32
+ {
33
+ "State": "Ohio",
34
+ "County1": "Franklin", "Cases1": "1542", "Status1": "Low",
35
+ "County2": "Cuyahoga", "Cases2": "1321", "Status2": "Low",
36
+ "County3": "Hamilton", "Cases3": "876", "Status3": "Low"
37
+ }
38
+ ]
39
+ }
40
+ },
41
+ "dashboard": {
42
+ "theme": "theme-blue",
43
+ "sharedFilters": [
44
+ {
45
+ "key": "State Filter",
46
+ "label": "Select State",
47
+ "columnName": "State",
48
+ "showDropdown": true,
49
+ "active": "California",
50
+ "values": ["California", "Florida", "New York", "Ohio", "Texas"],
51
+ "usedBy": ["bite-county-1", "bite-county-2", "bite-county-3"],
52
+ "tier": 1,
53
+ "type": "datafilter"
54
+ }
55
+ ]
56
+ },
57
+ "rows": [
58
+ [
59
+ { "width": 4, "widget": "bite-county-1" },
60
+ { "width": 4, "widget": "bite-county-2" },
61
+ { "width": 4, "widget": "bite-county-3" }
62
+ ]
63
+ ],
64
+ "visualizations": {
65
+ "bite-county-1": {
66
+ "uid": "bite-county-1",
67
+ "type": "data-bite",
68
+ "title": "County 1",
69
+ "biteStyle": "tp5",
70
+ "dataKey": "covid-counties",
71
+ "dataColumn": "Cases1",
72
+ "dataFunction": "Pass Through",
73
+ "biteBody": "confirmed cases",
74
+ "subtext": "",
75
+ "dataFormat": {
76
+ "roundToPlace": 0,
77
+ "commas": true,
78
+ "prefix": "",
79
+ "suffix": ""
80
+ },
81
+ "filters": [],
82
+ "theme": "theme-blue",
83
+ "visual": {
84
+ "border": false,
85
+ "accent": false,
86
+ "background": false,
87
+ "hideBackgroundColor": false,
88
+ "borderColorTheme": false
89
+ },
90
+ "dataColors": {
91
+ "column": "Status1",
92
+ "mappings": [
93
+ { "sourceValue": "Low", "color": "#d7f2ed" },
94
+ { "sourceValue": "Medium", "color": "#b8e5ac" },
95
+ { "sourceValue": "High", "color": "#fea82f" },
96
+ { "sourceValue": "Critical", "color": "#a03169" }
97
+ ]
98
+ }
99
+ },
100
+ "bite-county-2": {
101
+ "uid": "bite-county-2",
102
+ "type": "data-bite",
103
+ "title": "County 2",
104
+ "biteStyle": "tp5",
105
+ "dataKey": "covid-counties",
106
+ "dataColumn": "Cases2",
107
+ "dataFunction": "Pass Through",
108
+ "biteBody": "confirmed cases",
109
+ "subtext": "",
110
+ "dataFormat": {
111
+ "roundToPlace": 0,
112
+ "commas": true,
113
+ "prefix": "",
114
+ "suffix": ""
115
+ },
116
+ "filters": [],
117
+ "theme": "theme-blue",
118
+ "visual": {
119
+ "border": false,
120
+ "accent": false,
121
+ "background": false,
122
+ "hideBackgroundColor": false,
123
+ "borderColorTheme": false
124
+ },
125
+ "dataColors": {
126
+ "column": "Status2",
127
+ "mappings": [
128
+ { "sourceValue": "Low", "color": "#d7f2ed" },
129
+ { "sourceValue": "Medium", "color": "#b8e5ac" },
130
+ { "sourceValue": "High", "color": "#fea82f" },
131
+ { "sourceValue": "Critical", "color": "#a03169" }
132
+ ]
133
+ }
134
+ },
135
+ "bite-county-3": {
136
+ "uid": "bite-county-3",
137
+ "type": "data-bite",
138
+ "title": "County 3",
139
+ "biteStyle": "tp5",
140
+ "dataKey": "covid-counties",
141
+ "dataColumn": "Cases3",
142
+ "dataFunction": "Pass Through",
143
+ "biteBody": "confirmed cases",
144
+ "subtext": "",
145
+ "dataFormat": {
146
+ "roundToPlace": 0,
147
+ "commas": true,
148
+ "prefix": "",
149
+ "suffix": ""
150
+ },
151
+ "filters": [],
152
+ "theme": "theme-blue",
153
+ "visual": {
154
+ "border": false,
155
+ "accent": false,
156
+ "background": false,
157
+ "hideBackgroundColor": false,
158
+ "borderColorTheme": false
159
+ },
160
+ "dataColors": {
161
+ "column": "Status3",
162
+ "mappings": [
163
+ { "sourceValue": "Low", "color": "#d7f2ed" },
164
+ { "sourceValue": "Medium", "color": "#b8e5ac" },
165
+ { "sourceValue": "High", "color": "#fea82f" },
166
+ { "sourceValue": "Critical", "color": "#a03169" }
167
+ ]
168
+ }
169
+ }
170
+ }
171
+ }
@@ -40,13 +40,23 @@
40
40
  { "width": 4, "widget": "gauge1" },
41
41
  { "width": 4, "widget": "gauge2" },
42
42
  { "width": 4, "widget": "gauge3" }
43
+ ],
44
+ [
45
+ { "width": 4, "widget": "markup1" },
46
+ { "width": 4, "widget": "markup2" },
47
+ { "width": 4, "widget": "markup3" }
48
+ ],
49
+ [
50
+ { "width": 4, "widget": "waffle2" },
51
+ { "width": 4, "widget": "bite3" },
52
+ { "width": 4, "widget": "markup2" }
43
53
  ]
44
54
  ],
45
55
  "visualizations": {
46
56
  "waffle1": {
47
57
  "uid": "waffle1",
48
58
  "type": "waffle-chart",
49
- "title": "Vaccination Coverage",
59
+ "title": "Vaccination",
50
60
  "visualizationType": "TP5 Waffle",
51
61
  "visualizationSubType": "linear",
52
62
  "showPercent": true,
@@ -71,7 +81,7 @@
71
81
  "waffle2": {
72
82
  "uid": "waffle2",
73
83
  "type": "waffle-chart",
74
- "title": "Health Insurance Coverage Rate",
84
+ "title": "Health",
75
85
  "visualizationType": "TP5 Waffle",
76
86
  "visualizationSubType": "linear",
77
87
  "showPercent": true,
@@ -96,7 +106,7 @@
96
106
  "waffle3": {
97
107
  "uid": "waffle3",
98
108
  "type": "waffle-chart",
99
- "title": "Cancer Screening Completion",
109
+ "title": "Cancer",
100
110
  "visualizationType": "TP5 Waffle",
101
111
  "visualizationSubType": "linear",
102
112
  "showPercent": true,
@@ -245,7 +255,7 @@
245
255
  "bite3": {
246
256
  "uid": "bite3",
247
257
  "type": "data-bite",
248
- "title": "Cancer Screening Completion",
258
+ "title": "Cancer Screening Completion, and a little something else to make the title taller",
249
259
  "biteStyle": "tp5",
250
260
  "dataColumn": "Screening Rate",
251
261
  "dataFunction": "Mean (Average)",
@@ -261,7 +271,78 @@
261
271
  "visual": {
262
272
  "hideBackgroundColor": false
263
273
  }
274
+ },
275
+ "markup1": {
276
+ "uid": "markup1",
277
+ "type": "markup-include",
278
+ "title": "",
279
+ "theme": "theme-blue",
280
+ "contentEditor": {
281
+ "style": "tp5",
282
+ "title": "TP5 Markup Include",
283
+ "titleStyle": "small",
284
+ "useInlineHTML": true,
285
+ "inlineHTML": "<p><strong>68.5%</strong> of adults were vaccinated against seasonal flu in this sample dataset.</p>",
286
+ "srcUrl": "#example",
287
+ "showHeader": true,
288
+ "showNoDataMessage": false,
289
+ "noDataMessageText": "No Data Available"
290
+ },
291
+ "visual": {
292
+ "border": true,
293
+ "accent": false,
294
+ "whiteBackground": false,
295
+ "hideBackgroundColor": false,
296
+ "borderColorTheme": false
297
+ }
298
+ },
299
+ "markup2": {
300
+ "uid": "markup2",
301
+ "type": "markup-include",
302
+ "title": "",
303
+ "theme": "theme-teal",
304
+ "contentEditor": {
305
+ "style": "tp5",
306
+ "title": "White Background TP5",
307
+ "titleStyle": "small",
308
+ "useInlineHTML": true,
309
+ "inlineHTML": "<p>This TP5 markup include uses white-background styling with border enabled for comparison.</p>",
310
+ "srcUrl": "#example",
311
+ "showHeader": true,
312
+ "showNoDataMessage": false,
313
+ "noDataMessageText": "No Data Available"
314
+ },
315
+ "visual": {
316
+ "border": true,
317
+ "accent": false,
318
+ "whiteBackground": true,
319
+ "hideBackgroundColor": false,
320
+ "borderColorTheme": false
321
+ }
322
+ },
323
+ "markup3": {
324
+ "uid": "markup3",
325
+ "type": "markup-include",
326
+ "title": "",
327
+ "theme": "theme-purple",
328
+ "contentEditor": {
329
+ "style": "tp5",
330
+ "title": "TP5 Markup Include (No White BG)",
331
+ "titleStyle": "small",
332
+ "useInlineHTML": true,
333
+ "inlineHTML": "<p>This TP5 markup include uses the standard background and border settings for side-by-side alignment testing.</p>",
334
+ "srcUrl": "#example",
335
+ "showHeader": true,
336
+ "showNoDataMessage": false,
337
+ "noDataMessageText": "No Data Available"
338
+ },
339
+ "visual": {
340
+ "border": true,
341
+ "accent": false,
342
+ "whiteBackground": false,
343
+ "hideBackgroundColor": false,
344
+ "borderColorTheme": false
345
+ }
264
346
  }
265
347
  }
266
348
  }
267
-
@@ -0,0 +1,176 @@
1
+ import React from 'react'
2
+ import { fireEvent, render, screen } from '@testing-library/react'
3
+ import { describe, expect, it, vi } from 'vitest'
4
+ import { DashboardContext, DashboardDispatchContext, initialState } from '../DashboardContext'
5
+ import { DashboardCopyPasteContext } from '../DashboardCopyPasteContext'
6
+ import Column from './Column'
7
+
8
+ vi.mock('react-dnd', () => ({
9
+ useDrop: () => [{ isOver: false, canDrop: false }, vi.fn()]
10
+ }))
11
+
12
+ vi.mock('./Widget/Widget', () => ({
13
+ default: ({ title }) => <div>{title}</div>
14
+ }))
15
+
16
+ const renderColumn = ({
17
+ data,
18
+ copiedWidget = undefined,
19
+ sharedFilters = [],
20
+ visualizations = {}
21
+ }: {
22
+ data: any
23
+ copiedWidget?: any
24
+ sharedFilters?: any[]
25
+ visualizations?: any
26
+ }) => {
27
+ const dispatch = vi.fn()
28
+ const clearCopiedWidget = vi.fn()
29
+
30
+ render(
31
+ <DashboardContext.Provider
32
+ value={
33
+ {
34
+ ...initialState,
35
+ config: {
36
+ type: 'dashboard',
37
+ activeDashboard: 0,
38
+ dashboard: { sharedFilters },
39
+ datasets: {},
40
+ rows: [{ columns: [data], expandCollapseAllButtons: false }],
41
+ visualizations: {
42
+ 'existing-widget': {
43
+ uid: 'existing-widget',
44
+ type: 'markup-include',
45
+ visualizationType: 'markup-include',
46
+ contentEditor: { title: 'Existing' }
47
+ },
48
+ ...visualizations
49
+ }
50
+ },
51
+ outerContainerRef: vi.fn(),
52
+ setParentConfig: vi.fn(),
53
+ isDebug: false,
54
+ isEditor: true,
55
+ reloadURLData: vi.fn(),
56
+ loadAPIFilters: vi.fn(),
57
+ setAPIFilterDropdowns: vi.fn(),
58
+ setAPILoading: vi.fn(),
59
+ data: {}
60
+ } as any
61
+ }
62
+ >
63
+ <DashboardDispatchContext.Provider value={dispatch}>
64
+ <DashboardCopyPasteContext.Provider value={{ copiedWidget, copyWidget: vi.fn(), clearCopiedWidget }}>
65
+ <Column data={data} rowIdx={0} colIdx={0} toggleRow={false} />
66
+ </DashboardCopyPasteContext.Provider>
67
+ </DashboardDispatchContext.Provider>
68
+ </DashboardContext.Provider>
69
+ )
70
+
71
+ return { dispatch, clearCopiedWidget }
72
+ }
73
+
74
+ describe('Column copy paste slots', () => {
75
+ it('shows paste-ready text in an empty simple column and dispatches clone on click', () => {
76
+ const copiedWidget = { sourceWidgetKey: 'source-widget', label: 'Source' }
77
+ const { dispatch, clearCopiedWidget } = renderColumn({ data: { width: 12 }, copiedWidget })
78
+
79
+ fireEvent.click(
80
+ screen.getByRole('button', { name: 'Click here to paste copied component or drag and drop a new visualization' })
81
+ )
82
+
83
+ expect(dispatch).toHaveBeenCalledWith({
84
+ type: 'CLONE_VISUALIZATION',
85
+ payload: { sourceWidgetKey: 'source-widget', rowIdx: 0, colIdx: 0 }
86
+ })
87
+ expect(clearCopiedWidget).toHaveBeenCalled()
88
+ })
89
+
90
+ it('shows paste-ready text in the empty conditional slot and dispatches with entry index', () => {
91
+ const copiedWidget = { sourceWidgetKey: 'source-widget', label: 'Source' }
92
+ const { dispatch } = renderColumn({
93
+ data: { width: 12, conditionalWidgets: [{ widget: 'existing-widget' }] },
94
+ copiedWidget
95
+ })
96
+
97
+ fireEvent.click(
98
+ screen.getByRole('button', {
99
+ name: 'Click here to paste copied alternate visualization or drag and drop a new alternate'
100
+ })
101
+ )
102
+
103
+ expect(dispatch).toHaveBeenCalledWith({
104
+ type: 'CLONE_VISUALIZATION',
105
+ payload: { sourceWidgetKey: 'source-widget', rowIdx: 0, colIdx: 0, entryIdx: 1 }
106
+ })
107
+ })
108
+
109
+ it('labels empty conditional slots as first-match alternates', () => {
110
+ renderColumn({
111
+ data: { width: 12, conditionalWidgets: [{ widget: 'existing-widget' }] }
112
+ })
113
+
114
+ expect(screen.getByText('Drag and drop an alternate visualization.')).toBeInTheDocument()
115
+ expect(
116
+ screen.getByText('If multiple conditions match, only the first match in this column is shown.')
117
+ ).toBeInTheDocument()
118
+ })
119
+ })
120
+
121
+ describe('Column widget summaries', () => {
122
+ it('shows dashboard filter labels from user-entered keys when they are configured', () => {
123
+ renderColumn({
124
+ data: { width: 12, widget: 'dashboard-filters' },
125
+ sharedFilters: [
126
+ { key: 'State', columnName: 'state' },
127
+ { key: 'Year', columnName: 'year' },
128
+ { key: 'Topic', columnName: 'topic' },
129
+ { key: 'Response', columnName: 'response' }
130
+ ],
131
+ visualizations: {
132
+ 'dashboard-filters': {
133
+ uid: 'dashboard-filters',
134
+ type: 'dashboardFilters',
135
+ visualizationType: 'dashboardFilters',
136
+ sharedFilterIndexes: [0, 1, 2, 3]
137
+ }
138
+ }
139
+ })
140
+
141
+ expect(screen.getByText('State, Year, Topic, Response')).toBeInTheDocument()
142
+ })
143
+
144
+ it('falls back to dashboard filter columns when labels are not configured', () => {
145
+ renderColumn({
146
+ data: { width: 12, widget: 'dashboard-filters' },
147
+ sharedFilters: [{ key: '', columnName: 'state' }, { columnName: 'year' }],
148
+ visualizations: {
149
+ 'dashboard-filters': {
150
+ uid: 'dashboard-filters',
151
+ type: 'dashboardFilters',
152
+ visualizationType: 'dashboardFilters',
153
+ sharedFilterIndexes: [0, 1]
154
+ }
155
+ }
156
+ })
157
+
158
+ expect(screen.getByText('state, year')).toBeInTheDocument()
159
+ })
160
+
161
+ it('uses the table label for table summaries', () => {
162
+ renderColumn({
163
+ data: { width: 12, widget: 'table-widget' },
164
+ visualizations: {
165
+ 'table-widget': {
166
+ uid: 'table-widget',
167
+ type: 'table',
168
+ visualizationType: 'table',
169
+ table: { label: 'Custom Table' }
170
+ }
171
+ }
172
+ })
173
+
174
+ expect(screen.getByText('Custom Table')).toBeInTheDocument()
175
+ })
176
+ })