@cdc/core 4.26.2 → 4.26.3

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 (186) hide show
  1. package/LICENSE +201 -0
  2. package/_stories/Gallery.Charts.stories.tsx +1 -1
  3. package/_stories/Gallery.DataBite.stories.tsx +1 -1
  4. package/_stories/Gallery.Maps.stories.tsx +1 -1
  5. package/_stories/PageART.stories.tsx +1 -1
  6. package/_stories/PageBRFSS.stories.tsx +1 -1
  7. package/_stories/PageCancerRegistries.stories.tsx +1 -1
  8. package/_stories/PageEasternEquineEncephalitis.stories.tsx +3 -3
  9. package/_stories/PageExcessiveAlcoholUse.stories.tsx +1 -1
  10. package/_stories/PageMaternalMortality.stories.tsx +1 -1
  11. package/_stories/PageOralHealth.stories.tsx +1 -1
  12. package/_stories/PageRespiratory.stories.tsx +4 -4
  13. package/_stories/PageSmokingTobacco.stories.tsx +1 -1
  14. package/_stories/PageStateDiabetesProfiles.stories.tsx +1 -1
  15. package/_stories/PageWastewater.stories.tsx +4 -4
  16. package/_stories/VegaImport.stories.tsx +3 -3
  17. package/assets/callout-flag.svg +7 -0
  18. package/components/AdvancedEditor/EmbedEditor.tsx +1 -1
  19. package/components/Alert/components/Alert.styles.css +2 -2
  20. package/components/ComboBox/combobox.styles.css +48 -48
  21. package/components/CustomColorsEditor/CustomColorsEditor.css +53 -53
  22. package/components/DataTable/DataTable.tsx +46 -18
  23. package/components/DataTable/DataTableStandAlone.tsx +1 -0
  24. package/components/DataTable/components/ChartHeader.tsx +21 -12
  25. package/components/DataTable/components/MapHeader.tsx +34 -28
  26. package/components/DataTable/components/SortIcon/sort-icon.css +5 -5
  27. package/components/DataTable/data-table.css +50 -52
  28. package/components/DataTable/helpers/applyCustomOrder.ts +17 -0
  29. package/components/DataTable/helpers/getChartCellValue.ts +10 -7
  30. package/components/DataTable/helpers/getMapDataTableColumnKeys.ts +22 -0
  31. package/components/DataTable/helpers/mapCellMatrix.tsx +33 -23
  32. package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +33 -0
  33. package/components/DownloadButton.tsx +14 -6
  34. package/components/EditorPanel/ColumnsEditor.tsx +38 -31
  35. package/components/EditorPanel/CustomSortOrder.tsx +94 -0
  36. package/components/EditorPanel/DataTableEditor.tsx +139 -23
  37. package/components/EditorPanel/EditorPanel.styles.css +71 -71
  38. package/components/EditorPanel/EditorPanel.tsx +3 -8
  39. package/components/EditorPanel/EditorPanelDispatch.tsx +4 -4
  40. package/components/EditorPanel/FootnotesEditor.tsx +2 -2
  41. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +7 -6
  42. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +12 -10
  43. package/components/EditorPanel/components/MarkupVariablesEditor.tsx +160 -106
  44. package/components/EditorPanel/components/PanelMarkup.tsx +5 -1
  45. package/{styles/v2/components → components/EditorPanel}/editor.scss +67 -13
  46. package/components/EditorPanel/sections/StyleTreatmentSection.tsx +99 -0
  47. package/components/EditorPanel/sections/VisualSection.tsx +11 -0
  48. package/components/EditorWrapper/editor-wrapper.style.css +1 -1
  49. package/components/Filters/Filters.tsx +3 -5
  50. package/components/Filters/components/Tabs.tsx +19 -7
  51. package/{styles → components/Filters}/filters.scss +3 -3
  52. package/components/Footnotes/FootnotesStandAlone.tsx +4 -2
  53. package/components/HeaderThemeSelector/HeaderThemeSelector.css +61 -5
  54. package/components/Layout/components/Responsive.tsx +14 -6
  55. package/components/Layout/components/Sidebar/components/Sidebar.tsx +1 -1
  56. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +12 -18
  57. package/components/Layout/components/Visualization/index.tsx +39 -38
  58. package/components/Layout/components/Visualization/visualizations.scss +232 -15
  59. package/components/Layout/components/VisualizationContainer.test.tsx +67 -0
  60. package/components/Layout/components/VisualizationContainer.tsx +37 -0
  61. package/components/Layout/components/VisualizationContent.test.tsx +182 -0
  62. package/components/Layout/components/VisualizationContent.tsx +75 -0
  63. package/components/Layout/index.tsx +5 -5
  64. package/components/Layout/styles/editor-utils.scss +3 -3
  65. package/components/Layout/styles/editor.scss +4 -4
  66. package/components/Legend/Legend.Gradient.tsx +7 -1
  67. package/components/Loader/loader.styles.css +2 -2
  68. package/components/Loading.jsx +1 -1
  69. package/components/MediaControls.tsx +10 -2
  70. package/components/MultiSelect/multiselect.styles.css +19 -19
  71. package/components/NestedDropdown/nesteddropdown.styles.css +15 -15
  72. package/components/PaletteSelector/PaletteSelector.css +15 -15
  73. package/components/RichTooltip/richTooltip.css +6 -6
  74. package/components/Table/table.styles.css +2 -2
  75. package/components/Waiting.tsx +1 -1
  76. package/components/_stories/Filters.stories.tsx +1 -1
  77. package/components/_stories/styles.scss +0 -1
  78. package/components/elements/Button.jsx +1 -1
  79. package/components/elements/Card.jsx +1 -1
  80. package/{styles/v2/components → components/elements}/button.scss +9 -8
  81. package/components/inputs/InputCheckbox.jsx +1 -1
  82. package/components/inputs/InputSelect.tsx +1 -1
  83. package/components/inputs/InputText.jsx +1 -1
  84. package/components/inputs/InputToggle.tsx +1 -1
  85. package/{styles/v2/components/input → components/inputs}/_input-check-radio.scss +2 -2
  86. package/{styles/v2/components/input → components/inputs}/_input-group.scss +3 -3
  87. package/{styles/v2/components/input → components/inputs}/_input-slider.scss +2 -2
  88. package/{styles/v2/components/input → components/inputs}/_input.scss +5 -5
  89. package/{styles/v2/components/input → components/inputs}/index.scss +2 -2
  90. package/{styles → components}/loading.scss +1 -1
  91. package/components/managers/DataDesigner.tsx +1 -1
  92. package/{styles/v2/components → components/managers}/data-designer.scss +6 -7
  93. package/components/ui/Accordion.jsx +1 -1
  94. package/components/ui/Icon.tsx +1 -1
  95. package/components/ui/LoadSpin.jsx +1 -1
  96. package/components/ui/Modal.jsx +1 -1
  97. package/components/ui/Overlay.jsx +1 -1
  98. package/components/ui/Title/index.test.tsx +34 -0
  99. package/components/ui/Title/index.tsx +24 -7
  100. package/components/ui/Title/title.styles.css +119 -25
  101. package/components/ui/Tooltip.tsx +1 -1
  102. package/components/ui/_stories/Title.stories.tsx +1 -1
  103. package/{styles/v2/components → components/ui}/accordion.scss +3 -3
  104. package/components/ui/accordion.styles.css +11 -11
  105. package/{styles/v2/components → components/ui}/modal.scss +2 -2
  106. package/{styles/v2/components → components/ui}/overlay.scss +6 -6
  107. package/{styles/v2/components → components}/ui/tooltip.scss +1 -1
  108. package/{styles → components}/waiting.scss +9 -3
  109. package/devTemplate/dev.js +50 -0
  110. package/dist/cove-main.css +528 -231
  111. package/dist/cove-main.css.map +1 -1
  112. package/generateViteConfig.js +2 -2
  113. package/helpers/backfillDefaults.ts +35 -0
  114. package/helpers/constants.ts +12 -0
  115. package/helpers/cove/date.ts +32 -3
  116. package/helpers/cove/number.ts +29 -15
  117. package/helpers/coveUpdateWorker.ts +12 -8
  118. package/helpers/displayDataAsText.ts +1 -1
  119. package/helpers/embed/embedHelper.js +13 -2
  120. package/helpers/embed/index.ts +0 -4
  121. package/helpers/extractDataAndMetadata.ts +20 -0
  122. package/helpers/fetchRemoteData.ts +14 -8
  123. package/helpers/labelHash.ts +9 -0
  124. package/helpers/markupProcessor.ts +56 -38
  125. package/helpers/prepareScreenshot.ts +6 -3
  126. package/helpers/testing.ts +1 -1
  127. package/helpers/tests/abbreviateNumber.test.ts +59 -0
  128. package/helpers/tests/backfillDefaults.test.ts +253 -0
  129. package/helpers/tests/date.test.ts +46 -0
  130. package/helpers/tests/extractDataAndMetadata.test.ts +93 -0
  131. package/helpers/tests/markupProcessor.test.ts +315 -124
  132. package/helpers/tests/number.test.ts +42 -0
  133. package/helpers/tests/prepareScreenshot.test.ts +28 -28
  134. package/helpers/tests/testStandaloneBuild.ts +36 -26
  135. package/helpers/tests/useDataVizClasses.test.ts +66 -0
  136. package/helpers/tests/visualizationWrapperUsage.test.ts +57 -0
  137. package/helpers/useDataVizClasses.ts +13 -7
  138. package/helpers/ver/4.24.4.ts +24 -0
  139. package/helpers/ver/4.26.3.ts +44 -0
  140. package/helpers/ver/4.26.4.ts +31 -0
  141. package/helpers/ver/tests/4.26.3.test.ts +168 -0
  142. package/helpers/ver/tests/4.26.4.test.ts +88 -0
  143. package/helpers/ver/tests/coveUpdateWorker.test.ts +57 -0
  144. package/package.json +2 -2
  145. package/styles/_global.scss +7 -7
  146. package/styles/_reset.scss +2 -2
  147. package/styles/{v2/base → base}/_file-selector.scss +4 -4
  148. package/styles/{v2/base → base}/_general.scss +2 -4
  149. package/styles/{v2/base → base}/index.scss +1 -1
  150. package/styles/base.scss +107 -165
  151. package/styles/cove-main.scss +3 -6
  152. package/styles/layout/_component.scss +110 -0
  153. package/styles/{v2/layout → layout}/_data-table.scss +7 -7
  154. package/styles/layout/_wrapper-padding.scss +27 -0
  155. package/styles/{v2/main.scss → main.scss} +3 -1
  156. package/styles/{v2/themes → themes}/_color-definitions.scss +46 -41
  157. package/styles/{_accessibility.scss → utils/_accessibility.scss} +1 -1
  158. package/styles/{_global-variables.scss → utils/_properties.scss} +133 -112
  159. package/styles/{v2/utils → utils}/index.scss +2 -1
  160. package/types/Axis.ts +2 -0
  161. package/types/ComponentStyles.ts +1 -0
  162. package/types/ConfigureData.ts +1 -0
  163. package/types/MarkupInclude.ts +1 -0
  164. package/types/MarkupVariable.ts +2 -1
  165. package/types/Palette.ts +1 -0
  166. package/types/Table.ts +9 -0
  167. package/types/Visualization.ts +1 -0
  168. package/styles/_common-components.css +0 -73
  169. package/styles/_variables.scss +0 -63
  170. package/styles/v2/layout/_component.scss +0 -21
  171. package/styles/v2/utils/_variables.scss +0 -9
  172. package/{styles/v2/components/card.scss → components/elements/card.css} +2 -2
  173. /package/{styles/v2/components → components/ui}/icon.scss +0 -0
  174. /package/{styles/v2/components → components/ui}/loadspin.scss +0 -0
  175. /package/styles/{v2/base → base}/_heading.scss +0 -0
  176. /package/styles/{v2/base → base}/_reset.scss +0 -0
  177. /package/styles/{v2/layout → layout}/_alert.scss +0 -0
  178. /package/styles/{v2/layout → layout}/_progression.scss +0 -0
  179. /package/styles/{v2/layout → layout}/_tooltip.scss +0 -0
  180. /package/styles/{v2/layout → layout}/index.scss +0 -0
  181. /package/styles/{v2/themes → themes}/index.scss +0 -0
  182. /package/styles/{v2/utils → utils}/_align.scss +0 -0
  183. /package/styles/{v2/utils → utils}/_animations.scss +0 -0
  184. /package/styles/{v2/utils → utils}/_breakpoints.scss +0 -0
  185. /package/styles/{v2/utils → utils}/_grid.scss +0 -0
  186. /package/styles/{v2/utils → utils}/_mixins.scss +0 -0
@@ -1,24 +1,21 @@
1
1
  // main visualization wrapper
2
- import { ChartConfig } from '@cdc/chart/src/types/ChartConfig'
3
2
  import React, { forwardRef } from 'react'
4
- import { Config as DataBiteConfig } from '@cdc/data-bite/src/types/Config'
5
- import { Config as DataTableConfig } from '@cdc/data-table/src/types/Config'
6
3
  import './visualizations.scss'
7
- import { Config as WaffleChartConfig } from '@cdc/waffle-chart/src/types/Config'
8
- import { MarkupIncludeConfig } from '@cdc/core/types/MarkupInclude'
9
- import { DashboardFilters } from '@cdc/dashboard/src/types/DashboardFilters'
10
- import { MapConfig } from '@cdc/map/src/types/MapConfig'
4
+ import type { AnyVisualization } from '@cdc/core/types/Visualization'
5
+
6
+ export type VisualizationShellConfig = Partial<AnyVisualization> & {
7
+ type?: AnyVisualization['type'] | 'dashboard'
8
+ theme?: string
9
+ visual?: {
10
+ highlightWrappers?: boolean
11
+ whiteBackground?: boolean
12
+ }
13
+ }
11
14
 
12
15
  type VisualizationWrapper = {
13
16
  children: React.ReactNode
14
- config:
15
- | ChartConfig
16
- | DataBiteConfig
17
- | WaffleChartConfig
18
- | MarkupIncludeConfig
19
- | DashboardFilters
20
- | MapConfig
21
- | DataTableConfig
17
+ className?: string
18
+ config: VisualizationShellConfig
22
19
  currentViewport?: string
23
20
  imageId?: string
24
21
  isEditor: boolean
@@ -35,15 +32,17 @@ const Visualization = forwardRef<HTMLDivElement, VisualizationWrapper>((props, r
35
32
  className
36
33
  } = props
37
34
 
35
+ const themeClass = config.type === 'map' ? config?.general?.headerColor || config?.theme : config?.theme
36
+
38
37
  const getWrappingClasses = () => {
39
- let classes = ['cdc-open-viz-module', `${currentViewport}`, `${config?.theme}`]
38
+ let classes = ['cove-visualization', 'cdc-open-viz-module', `${currentViewport}`, `${themeClass}`]
40
39
 
41
40
  if (className) {
42
41
  classes.push(className)
43
42
  }
44
43
 
45
44
  isEditor && classes.push('spacing-wrapper')
46
- isEditor && classes.push('isEditor')
45
+ isEditor && classes.push('is-editor')
47
46
 
48
47
  if (isEditor && showEditorPanel) {
49
48
  classes = classes.filter(item => item !== 'editor-panel--hidden')
@@ -55,9 +54,18 @@ const Visualization = forwardRef<HTMLDivElement, VisualizationWrapper>((props, r
55
54
  classes.push('editor-panel--hidden')
56
55
  }
57
56
 
57
+ if (isEditor && config.visual?.highlightWrappers) {
58
+ classes.push('cove-highlight-wrappers')
59
+ }
60
+
58
61
  if (config.type === 'filtered-text') {
59
62
  classes.push('type-filtered-text', `font-${config.fontSize}`)
60
- classes = classes.filter(item => item !== 'cove-component__content')
63
+ classes = classes.filter(item => item !== 'cove-visualization__body')
64
+ return classes
65
+ }
66
+
67
+ if (config.type === 'dashboard') {
68
+ classes.push('type-dashboard')
61
69
  return classes
62
70
  }
63
71
 
@@ -66,41 +74,31 @@ const Visualization = forwardRef<HTMLDivElement, VisualizationWrapper>((props, r
66
74
  config?.visualizationType === 'Spark Line' && classes.push(`type-sparkline`)
67
75
  return classes
68
76
  }
77
+
69
78
  if (config.type === 'map') {
70
79
  classes.push(`type-map`)
71
- if (config?.runtime?.editorErrorMessage.length !== 0) classes.push('type-map--has-error')
80
+ if (config?.runtime?.editorErrorMessage?.length) classes.push('type-map--has-error')
81
+ return classes
72
82
  }
73
83
 
74
84
  if (config.type === 'table') {
75
85
  classes.push('type-data-table')
86
+ return classes
76
87
  }
77
88
 
78
89
  if (config.type === 'data-bite') {
79
- classes.push('cdc-open-viz-module', 'type-data-bite', currentViewport, config.theme, `font-${config.fontSize}`)
80
- if (isEditor) {
81
- classes.push('is-editor')
82
- }
90
+ classes.push('type-data-bite', `font-${config.fontSize}`)
91
+ return classes
83
92
  }
84
93
 
85
94
  if (config.type === 'markup-include') {
86
- classes.push('markup-include', 'cdc-open-viz-module')
95
+ classes.push('type-markup-include')
96
+ return classes
87
97
  }
88
98
 
89
99
  if (config.type === 'waffle-chart') {
90
- classes.push(
91
- 'cove',
92
- 'cdc-open-viz-module',
93
- 'type-waffle-chart',
94
- currentViewport,
95
- config.theme,
96
- 'font-' + config.overallFontSize
97
- )
98
-
99
- if (isEditor) {
100
- classes.push('is-editor')
101
- }
100
+ classes.push('type-waffle-chart', 'font-' + config.overallFontSize)
102
101
 
103
- // Add TP5 style classes
104
102
  if (config.visualizationType === 'TP5 Waffle') {
105
103
  classes.push('waffle__style--tp5')
106
104
  if (config.visual?.whiteBackground) {
@@ -115,8 +113,9 @@ const Visualization = forwardRef<HTMLDivElement, VisualizationWrapper>((props, r
115
113
  }
116
114
  }
117
115
 
118
- classes.push('cove-component', 'waffle-chart')
116
+ return classes
119
117
  }
118
+
120
119
  return classes
121
120
  }
122
121
 
@@ -132,4 +131,6 @@ const Visualization = forwardRef<HTMLDivElement, VisualizationWrapper>((props, r
132
131
  )
133
132
  })
134
133
 
134
+ Visualization.displayName = 'Visualization'
135
+
135
136
  export default Visualization
@@ -1,46 +1,263 @@
1
- .cdc-open-viz-module {
2
- .cdc-chart-inner-container .cove-component__content {
3
- padding: 0 0 27px 0 !important;
4
- }
5
- &.isEditor {
1
+ .cove-visualization {
2
+ &.is-editor {
6
3
  overflow: auto;
7
4
  display: grid;
5
+ position: relative;
8
6
  transition: grid-template-columns 400ms ease-in-out;
9
7
  min-height: 100vh;
10
8
 
11
9
  .editor-panel__toggle {
12
- transition: left 400ms ease-in-out;
10
+ transition: left 400ms ease-in-out, background 0.1s;
13
11
  }
14
12
 
15
13
  .sidebar {
16
- transition: left 400ms ease-in-out;
14
+ overflow: hidden;
15
+ }
16
+
17
+ // Override dashboard's .editor-panel absolute positioning that conflicts with the grid layout.
18
+ // .cove-visualization.type-dashboard scopes .editor-panel { position: absolute } (specificity 0,3,0),
19
+ // so we need 4 classes here (specificity 0,4,0) to win.
20
+ .editor-panel.sidebar {
21
+ position: static;
22
+ z-index: auto;
23
+ overflow: hidden;
24
+ top: auto;
25
+ bottom: auto;
26
+ width: auto;
17
27
  }
18
28
 
19
29
  &.editor-panel--visible {
20
30
  grid-template-areas: 'panel content';
21
31
  grid-template-columns: 350px calc(100% - 350px);
22
- overflow: hidden;
32
+
33
+ .cove-visualization__outer {
34
+ overflow: hidden;
35
+ }
23
36
  }
24
37
 
25
38
  &.editor-panel--hidden {
26
39
  grid-template-areas: 'panel content';
27
40
  grid-template-columns: 0px 100%;
41
+ overflow: hidden;
28
42
  }
29
43
 
30
44
  .cove-editor__content,
31
- .cove-component__content {
45
+ .cove-visualization__outer,
46
+ .cove-visualization__inner {
32
47
  grid-area: content;
33
48
  position: relative;
34
49
  left: 0;
35
50
  width: 100% !important;
36
- grid-area: content;
51
+ }
52
+
53
+ .cove-editor__content {
37
54
  padding: 1rem;
55
+ }
38
56
 
39
- // Prevent double padding on nested .cove-component__content divs
40
- // (e.g., in markup-include, waffle-chart, filtered-text)
41
- .cove-component__content {
42
- padding: 0;
43
- }
57
+ .cove-visualization__outer {
58
+ padding: 1rem;
44
59
  }
45
60
  }
46
61
  }
62
+
63
+ // Developer highlight tool — only active when .is-editor + config.visual.highlightWrappers
64
+ // Enabled via ?isCoveDeveloper=true URL param + "Highlight Layout Wrappers" checkbox
65
+ .cove-visualization.is-editor.cove-highlight-wrappers {
66
+ outline: 2px dashed #0050d8;
67
+ position: relative;
68
+
69
+ &::before {
70
+ background: #0050d8;
71
+ color: #fff;
72
+ content: 'cove-visualization';
73
+ font-family: monospace;
74
+ font-size: 10px;
75
+ line-height: 1;
76
+ padding: 2px 4px;
77
+ pointer-events: none;
78
+ position: absolute;
79
+ top: 0;
80
+ left: 0;
81
+ z-index: 9999;
82
+ }
83
+
84
+ .cove-visualization__outer {
85
+ outline: 2px dashed #00a91c;
86
+ position: relative;
87
+
88
+ &::before {
89
+ background: #00a91c;
90
+ color: #fff;
91
+ content: '__outer';
92
+ font-family: monospace;
93
+ font-size: 10px;
94
+ line-height: 1;
95
+ padding: 2px 4px;
96
+ pointer-events: none;
97
+ position: absolute;
98
+ top: 0;
99
+ left: 0;
100
+ z-index: 9999;
101
+ }
102
+ }
103
+
104
+ .cove-visualization__inner {
105
+ outline: 2px dashed #e52207;
106
+ position: relative;
107
+
108
+ &::before {
109
+ background: #e52207;
110
+ color: #fff;
111
+ content: '__inner';
112
+ font-family: monospace;
113
+ font-size: 10px;
114
+ line-height: 1;
115
+ padding: 2px 4px;
116
+ pointer-events: none;
117
+ position: absolute;
118
+ top: 0;
119
+ left: 0;
120
+ z-index: 9999;
121
+ }
122
+ }
123
+
124
+ .cove-visualization__title,
125
+ .cove-visualization__header {
126
+ outline: 2px dashed #8a2be2;
127
+ position: relative;
128
+
129
+ &::before {
130
+ background: #8a2be2;
131
+ color: #fff;
132
+ content: '__title';
133
+ font-family: monospace;
134
+ font-size: 10px;
135
+ line-height: 1;
136
+ padding: 2px 4px;
137
+ pointer-events: none;
138
+ position: absolute;
139
+ top: 0;
140
+ left: 0;
141
+ z-index: 9999;
142
+ }
143
+ }
144
+
145
+ .cove-visualization__body {
146
+ outline: 2px dashed #e52207;
147
+ position: relative;
148
+
149
+ &::before {
150
+ background: #e52207;
151
+ color: #fff;
152
+ content: '__body';
153
+ font-family: monospace;
154
+ font-size: 10px;
155
+ line-height: 1;
156
+ padding: 2px 4px;
157
+ pointer-events: none;
158
+ position: absolute;
159
+ top: 0;
160
+ left: 0;
161
+ z-index: 9999;
162
+ }
163
+ }
164
+
165
+ .cove-visualization__message {
166
+ outline: 2px dashed #0050d8;
167
+ position: relative;
168
+
169
+ &::before {
170
+ background: #0050d8;
171
+ color: #fff;
172
+ content: '__message';
173
+ font-family: monospace;
174
+ font-size: 10px;
175
+ line-height: 1;
176
+ padding: 2px 4px;
177
+ pointer-events: none;
178
+ position: absolute;
179
+ top: 0;
180
+ left: 0;
181
+ z-index: 9999;
182
+ }
183
+ }
184
+
185
+ .cove-visualization__body-wrap {
186
+ outline: 2px dashed #e66f0e;
187
+ position: relative;
188
+
189
+ &::before {
190
+ background: #e66f0e;
191
+ color: #fff;
192
+ content: '__body-wrap';
193
+ font-family: monospace;
194
+ font-size: 10px;
195
+ line-height: 1;
196
+ padding: 2px 4px;
197
+ pointer-events: none;
198
+ position: absolute;
199
+ top: 0;
200
+ left: 0;
201
+ z-index: 9999;
202
+ }
203
+ }
204
+
205
+ }
206
+
207
+ .cove-visualization {
208
+ font-size: var(--app-font-size);
209
+
210
+ .cove-visualization__body,
211
+ .cove-visualization__body-wrap,
212
+ .cove-visualization__message-section,
213
+ .cove-visualization__content-section,
214
+ .cove-visualization__body-subtext-section,
215
+ .cove-visualization__body-footer-section,
216
+ .cove-visualization__subtext-section {
217
+ color: var(--baseColor);
218
+ font-family: var(--visualization-body-font-family, var(--app-font-main));
219
+ line-height: var(--visualization-body-line-height, 1.5);
220
+ }
221
+
222
+ .cove-visualization__content-section,
223
+ .cove-visualization__message-section,
224
+ .cove-visualization__body-subtext-section,
225
+ .cove-visualization__body-footer-section,
226
+ .cove-visualization__subtext-section {
227
+ width: 100%;
228
+ }
229
+
230
+ .cove-visualization__title~.cove-visualization__body .cove-visualization__body-wrap,
231
+ .cove-visualization__body.component--has-legacy-border .cove-visualization__body-wrap,
232
+ .cove-visualization__body.component--has-border-color-theme .cove-visualization__body-wrap,
233
+ .cove-visualization__body.component--has-background .cove-visualization__body-wrap {
234
+ padding-top: var(--cove-visualization-section-gap, 1.5rem);
235
+ padding-bottom: var(--cove-visualization-section-gap, 1.5rem);
236
+ }
237
+
238
+ .cove-visualization__body.component--has-legacy-border .cove-visualization__content-section,
239
+ .cove-visualization__body.component--has-border-color-theme .cove-visualization__content-section,
240
+ .cove-visualization__body.component--has-background .cove-visualization__content-section {
241
+ padding-inline: 0;
242
+ }
243
+
244
+ &.type-data-bite .cove-visualization__body-wrap {
245
+ padding-top: var(--cove-visualization-section-gap, 1.5rem);
246
+ padding-bottom: var(--cove-visualization-section-gap, 1.5rem);
247
+ }
248
+
249
+ .cove-visualization__body-wrap {
250
+ display: flex;
251
+ flex-direction: column;
252
+ row-gap: var(--cove-visualization-section-gap, 1.5rem);
253
+
254
+ // Prevent content margins from stacking with row-gap
255
+ >*> :first-child {
256
+ margin-top: 0;
257
+ }
258
+
259
+ >*> :last-child {
260
+ margin-bottom: 0;
261
+ }
262
+ }
263
+ }
@@ -0,0 +1,67 @@
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import { describe, expect, it } from 'vitest'
4
+ import VisualizationContainer from './VisualizationContainer'
5
+
6
+ describe('VisualizationContainer', () => {
7
+ it('renders the shared outer shell contract for dashboard wrappers', () => {
8
+ const { container } = render(
9
+ <VisualizationContainer
10
+ config={{ type: 'dashboard', theme: 'theme-blue' }}
11
+ currentViewport='md'
12
+ imageId='dashboard-image'
13
+ isEditor={false}
14
+ renderResponsive={false}
15
+ >
16
+ <div>Dashboard body</div>
17
+ </VisualizationContainer>
18
+ )
19
+
20
+ const shell = container.querySelector('.cove-visualization')
21
+
22
+ expect(shell).toHaveClass('cdc-open-viz-module', 'type-dashboard', 'md', 'theme-blue')
23
+ expect(shell).toHaveAttribute('data-download-id', 'dashboard-image')
24
+ expect(shell?.querySelector('.cove-visualization__outer')).toBeNull()
25
+ })
26
+
27
+ it('renders the editor panel and responsive wrapper when enabled', () => {
28
+ const { container } = render(
29
+ <VisualizationContainer
30
+ config={{ type: 'chart', showEditorPanel: true }}
31
+ currentViewport='lg'
32
+ editorPanel={<div data-testid='editor-panel'>Editor Panel</div>}
33
+ isEditor={true}
34
+ >
35
+ <div>Chart body</div>
36
+ </VisualizationContainer>
37
+ )
38
+
39
+ expect(screen.getByTestId('editor-panel')).toBeInTheDocument()
40
+ expect(container.querySelector('.cove-visualization')).toHaveClass('type-chart', 'is-editor', 'editor-panel--visible')
41
+ expect(container.querySelector('.cove-visualization__outer')).toBeInTheDocument()
42
+ })
43
+
44
+ it('hides the editor panel class when editor panels are disabled in config', () => {
45
+ const { container } = render(
46
+ <VisualizationContainer config={{ type: 'map', showEditorPanel: false }} isEditor={true}>
47
+ <div>Map body</div>
48
+ </VisualizationContainer>
49
+ )
50
+
51
+ expect(container.querySelector('.cove-visualization')).toHaveClass('editor-panel--hidden')
52
+ })
53
+
54
+ it('uses the map header theme on the shared visualization shell', () => {
55
+ const { container } = render(
56
+ <VisualizationContainer
57
+ config={{ type: 'map', general: { headerColor: 'theme-purple' } }}
58
+ currentViewport='lg'
59
+ isEditor={false}
60
+ >
61
+ <div>Map body</div>
62
+ </VisualizationContainer>
63
+ )
64
+
65
+ expect(container.querySelector('.cove-visualization')).toHaveClass('type-map', 'theme-purple')
66
+ })
67
+ })
@@ -0,0 +1,37 @@
1
+ import React, { forwardRef } from 'react'
2
+ import Responsive from './Responsive'
3
+ import Visualization, { type VisualizationShellConfig } from './Visualization'
4
+
5
+ type VisualizationContainerProps = {
6
+ children: React.ReactNode
7
+ className?: string
8
+ config: VisualizationShellConfig
9
+ currentViewport?: string
10
+ editorPanel?: React.ReactNode
11
+ imageId?: string
12
+ isEditor: boolean
13
+ renderResponsive?: boolean
14
+ }
15
+
16
+ const VisualizationContainer = forwardRef<HTMLDivElement, VisualizationContainerProps>(
17
+ ({ children, className, config, currentViewport, editorPanel, imageId, isEditor, renderResponsive = true }, ref) => {
18
+ return (
19
+ <Visualization
20
+ ref={ref}
21
+ config={config}
22
+ isEditor={isEditor}
23
+ showEditorPanel={config?.showEditorPanel}
24
+ currentViewport={currentViewport}
25
+ imageId={imageId}
26
+ className={className}
27
+ >
28
+ {isEditor && editorPanel}
29
+ {renderResponsive ? <Responsive isEditor={isEditor}>{children}</Responsive> : children}
30
+ </Visualization>
31
+ )
32
+ }
33
+ )
34
+
35
+ VisualizationContainer.displayName = 'VisualizationContainer'
36
+
37
+ export default VisualizationContainer