@cdc/core 4.26.1 → 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 (249) hide show
  1. package/.claude/agents/qa-test-developer.md +126 -0
  2. package/CLAUDE.local.md +67 -0
  3. package/LICENSE +201 -0
  4. package/_stories/Gallery.Charts.stories.tsx +35 -42
  5. package/_stories/Gallery.DataBite.stories.tsx +15 -8
  6. package/_stories/Gallery.Maps.stories.tsx +37 -28
  7. package/_stories/Gallery.WaffleChart.stories.tsx +1 -1
  8. package/_stories/PageART.stories.tsx +5 -4
  9. package/_stories/PageBRFSS.stories.tsx +21 -16
  10. package/_stories/PageCancerRegistries.stories.tsx +15 -15
  11. package/_stories/PageEasternEquineEncephalitis.stories.tsx +33 -19
  12. package/_stories/PageExcessiveAlcoholUse.stories.tsx +148 -143
  13. package/_stories/PageMaternalMortality.stories.tsx +5 -4
  14. package/_stories/PageOralHealth.stories.tsx +15 -10
  15. package/_stories/PageRespiratory.stories.tsx +4 -4
  16. package/_stories/PageSmokingTobacco.stories.tsx +15 -10
  17. package/_stories/PageStateDiabetesProfiles.stories.tsx +15 -10
  18. package/_stories/PageWastewater.stories.tsx +44 -30
  19. package/_stories/VegaImport.stories.tsx +401 -0
  20. package/_stories/vega-fixtures/bars-with-line.json +444 -0
  21. package/_stories/vega-fixtures/bars.json +58 -0
  22. package/_stories/vega-fixtures/combo-bar-rolling-mean.json +88 -0
  23. package/_stories/vega-fixtures/combo.json +68 -0
  24. package/_stories/vega-fixtures/grouped-horizontal-bars.json +83 -0
  25. package/_stories/vega-fixtures/grouped-horizontal-bars2.json +231 -0
  26. package/_stories/vega-fixtures/horizontal-bar.json +427 -0
  27. package/_stories/vega-fixtures/horizontal-bars-with-bad-colors.json +197 -0
  28. package/_stories/vega-fixtures/horizontal-bars2.json +58 -0
  29. package/_stories/vega-fixtures/lines.json +227 -0
  30. package/_stories/vega-fixtures/measles-bars.json +348 -0
  31. package/_stories/vega-fixtures/measles-map.json +11101 -0
  32. package/_stories/vega-fixtures/measles-stacked-bars.json +2147 -0
  33. package/_stories/vega-fixtures/multi-dataset.json +255 -0
  34. package/_stories/vega-fixtures/no-data.json +14 -0
  35. package/_stories/vega-fixtures/pie-chart.json +94 -0
  36. package/_stories/vega-fixtures/repeat-spec.json +47 -0
  37. package/_stories/vega-fixtures/stacked-area.json +222 -0
  38. package/_stories/vega-fixtures/stacked-bar-with-rect.json +3412 -0
  39. package/_stories/vega-fixtures/stacked-bars-with-line.json +364 -0
  40. package/_stories/vega-fixtures/stacked-bars.json +212 -0
  41. package/_stories/vega-fixtures/stacked-horizontal-bars.json +140 -0
  42. package/_stories/vega-fixtures/warning-combo.json +59 -0
  43. package/_stories/vega-fixtures/warning-scatter-and-line.json +1182 -0
  44. package/assets/callout-flag.svg +7 -0
  45. package/assets/icon-chart-area.svg +1 -0
  46. package/assets/icon-chart-radar.svg +23 -0
  47. package/assets/logo2.svg +31 -0
  48. package/components/AdvancedEditor/EmbedEditor.tsx +270 -38
  49. package/components/Alert/components/Alert.styles.css +2 -2
  50. package/components/ComboBox/combobox.styles.css +48 -48
  51. package/components/CustomColorsEditor/CustomColorsEditor.css +53 -53
  52. package/components/CustomColorsEditor/CustomColorsEditor.tsx +3 -10
  53. package/components/DataTable/DataTable.tsx +46 -18
  54. package/components/DataTable/DataTableStandAlone.tsx +1 -0
  55. package/components/DataTable/components/ChartHeader.tsx +21 -12
  56. package/components/DataTable/components/MapHeader.tsx +34 -28
  57. package/components/DataTable/components/SortIcon/sort-icon.css +5 -5
  58. package/components/DataTable/data-table.css +50 -52
  59. package/components/DataTable/helpers/applyCustomOrder.ts +17 -0
  60. package/components/DataTable/helpers/getChartCellValue.ts +10 -7
  61. package/components/DataTable/helpers/getMapDataTableColumnKeys.ts +22 -0
  62. package/components/DataTable/helpers/getSeriesName.ts +6 -0
  63. package/components/DataTable/helpers/mapCellMatrix.tsx +33 -23
  64. package/components/DataTable/helpers/tests/mapCellMatrix.test.ts +33 -0
  65. package/components/DownloadButton.tsx +14 -6
  66. package/components/EditorPanel/ColumnsEditor.tsx +38 -31
  67. package/components/EditorPanel/CustomSortOrder.tsx +94 -0
  68. package/components/EditorPanel/DataTableEditor.tsx +139 -23
  69. package/components/EditorPanel/EditorPanel.styles.css +71 -71
  70. package/components/EditorPanel/EditorPanel.tsx +3 -8
  71. package/components/EditorPanel/EditorPanelDispatch.tsx +4 -4
  72. package/components/EditorPanel/FootnotesEditor.tsx +2 -2
  73. package/components/EditorPanel/VizFilterEditor/NestedDropdownEditor.tsx +21 -12
  74. package/components/EditorPanel/VizFilterEditor/VizFilterEditor.tsx +16 -10
  75. package/components/EditorPanel/VizFilterEditor/components/FilterOrder.tsx +33 -29
  76. package/components/EditorPanel/components/MarkupVariablesEditor.tsx +160 -106
  77. package/components/EditorPanel/components/PanelMarkup.tsx +5 -1
  78. package/{styles/v2/components → components/EditorPanel}/editor.scss +76 -22
  79. package/components/EditorPanel/sections/StyleTreatmentSection.tsx +99 -0
  80. package/components/EditorPanel/sections/VisualSection.tsx +11 -0
  81. package/components/EditorWrapper/editor-wrapper.style.css +1 -1
  82. package/components/Filters/Filters.tsx +3 -5
  83. package/components/Filters/components/Tabs.tsx +19 -7
  84. package/{styles → components/Filters}/filters.scss +3 -3
  85. package/components/Footnotes/FootnotesStandAlone.tsx +4 -2
  86. package/components/HeaderThemeSelector/HeaderThemeSelector.css +61 -5
  87. package/components/Layout/components/Responsive.tsx +14 -6
  88. package/components/Layout/components/Sidebar/components/Sidebar.tsx +1 -1
  89. package/components/Layout/components/Sidebar/components/sidebar.styles.scss +14 -20
  90. package/components/Layout/components/Visualization/index.tsx +50 -38
  91. package/components/Layout/components/Visualization/visualizations.scss +232 -15
  92. package/components/Layout/components/VisualizationContainer.test.tsx +67 -0
  93. package/components/Layout/components/VisualizationContainer.tsx +37 -0
  94. package/components/Layout/components/VisualizationContent.test.tsx +182 -0
  95. package/components/Layout/components/VisualizationContent.tsx +75 -0
  96. package/components/Layout/index.tsx +5 -5
  97. package/components/Layout/styles/editor-utils.scss +3 -3
  98. package/components/Layout/styles/editor.scss +4 -4
  99. package/components/Legend/Legend.Gradient.tsx +7 -1
  100. package/components/Loader/loader.styles.css +2 -2
  101. package/components/Loading.jsx +1 -1
  102. package/components/MediaControls.tsx +10 -3
  103. package/components/MultiSelect/multiselect.styles.css +19 -19
  104. package/components/NestedDropdown/nesteddropdown.styles.css +15 -15
  105. package/components/PaletteSelector/PaletteSelector.css +15 -15
  106. package/components/RichTooltip/richTooltip.css +6 -6
  107. package/components/Table/table.styles.css +2 -2
  108. package/components/Waiting.tsx +1 -1
  109. package/components/_stories/CustomColorsEditor.stories.tsx +37 -0
  110. package/components/_stories/DataTable.stories.tsx +1 -0
  111. package/components/_stories/Filters.stories.tsx +1 -1
  112. package/components/_stories/styles.scss +0 -1
  113. package/components/elements/Button.jsx +1 -1
  114. package/components/elements/Card.jsx +1 -1
  115. package/{styles/v2/components → components/elements}/button.scss +9 -8
  116. package/components/inputs/InputCheckbox.jsx +1 -1
  117. package/components/inputs/InputSelect.tsx +1 -1
  118. package/components/inputs/InputText.jsx +1 -1
  119. package/components/inputs/InputToggle.tsx +1 -1
  120. package/{styles/v2/components/input → components/inputs}/_input-check-radio.scss +2 -2
  121. package/{styles/v2/components/input → components/inputs}/_input-group.scss +3 -3
  122. package/{styles/v2/components/input → components/inputs}/_input-slider.scss +2 -2
  123. package/{styles/v2/components/input → components/inputs}/_input.scss +5 -5
  124. package/{styles/v2/components/input → components/inputs}/index.scss +2 -2
  125. package/{styles → components}/loading.scss +1 -1
  126. package/components/managers/DataDesigner.tsx +1 -1
  127. package/{styles/v2/components → components/managers}/data-designer.scss +6 -7
  128. package/components/ui/Accordion.jsx +1 -1
  129. package/components/ui/Icon.tsx +1 -1
  130. package/components/ui/LoadSpin.jsx +1 -1
  131. package/components/ui/Modal.jsx +1 -1
  132. package/components/ui/Overlay.jsx +1 -1
  133. package/components/ui/Title/index.test.tsx +34 -0
  134. package/components/ui/Title/index.tsx +24 -7
  135. package/components/ui/Title/title.styles.css +119 -25
  136. package/components/ui/Tooltip.tsx +1 -1
  137. package/components/ui/_stories/Title.stories.tsx +1 -1
  138. package/{styles/v2/components → components/ui}/accordion.scss +3 -3
  139. package/components/ui/accordion.styles.css +11 -11
  140. package/{styles/v2/components → components/ui}/modal.scss +2 -2
  141. package/{styles/v2/components → components/ui}/overlay.scss +6 -6
  142. package/{styles/v2/components → components}/ui/tooltip.scss +1 -1
  143. package/{styles → components}/waiting.scss +9 -3
  144. package/data/colorPalettes.ts +18 -5
  145. package/data/mapColorPalettes.ts +10 -0
  146. package/devTemplate/dev.js +285 -0
  147. package/devTemplate/index.html +30 -0
  148. package/devTemplate/preview.html +1503 -0
  149. package/devTemplate/sidebar.css +151 -0
  150. package/dist/cove-main.css +2530 -3901
  151. package/dist/cove-main.css.map +1 -1
  152. package/generateViteConfig.js +111 -2
  153. package/helpers/DataTransform.ts +1 -5
  154. package/helpers/backfillDefaults.ts +35 -0
  155. package/helpers/constants.ts +12 -0
  156. package/helpers/cove/date.ts +64 -3
  157. package/helpers/cove/number.ts +29 -15
  158. package/helpers/cove/string.ts +29 -0
  159. package/helpers/coveUpdateWorker.ts +14 -8
  160. package/helpers/displayDataAsText.ts +1 -1
  161. package/helpers/embed/embedCodeGenerator.ts +80 -0
  162. package/helpers/embed/embedHelper.js +169 -0
  163. package/helpers/embed/filterUtils.ts +121 -0
  164. package/helpers/embed/index.ts +17 -0
  165. package/helpers/embed/urlValidation.ts +119 -0
  166. package/helpers/extractDataAndMetadata.ts +20 -0
  167. package/helpers/fetchRemoteData.ts +14 -8
  168. package/helpers/filterVizData.ts +6 -1
  169. package/helpers/getFileExtension.ts +0 -6
  170. package/helpers/labelHash.ts +9 -0
  171. package/helpers/markupProcessor.ts +56 -38
  172. package/helpers/metrics/types.ts +3 -0
  173. package/helpers/palettes/colorDistributions.ts +1 -1
  174. package/helpers/palettes/utils.ts +12 -12
  175. package/helpers/parseCsvWithQuotes.ts +15 -14
  176. package/helpers/prepareScreenshot.ts +33 -10
  177. package/helpers/testing.ts +44 -0
  178. package/helpers/tests/DataTransform.test.ts +125 -0
  179. package/helpers/tests/abbreviateNumber.test.ts +59 -0
  180. package/helpers/tests/backfillDefaults.test.ts +253 -0
  181. package/helpers/tests/date.test.ts +110 -0
  182. package/helpers/tests/extractDataAndMetadata.test.ts +93 -0
  183. package/helpers/tests/markupProcessor.test.ts +315 -124
  184. package/helpers/tests/number.test.ts +42 -0
  185. package/helpers/tests/prepareScreenshot.test.ts +28 -28
  186. package/helpers/tests/testStandaloneBuild.ts +36 -26
  187. package/helpers/tests/useDataVizClasses.test.ts +66 -0
  188. package/helpers/tests/visualizationWrapperUsage.test.ts +57 -0
  189. package/helpers/useDataVizClasses.ts +13 -7
  190. package/helpers/vegaConfig.ts +1 -1
  191. package/helpers/vegaConfigImport.ts +160 -0
  192. package/helpers/ver/4.24.4.ts +24 -0
  193. package/helpers/ver/4.26.1.ts +1 -1
  194. package/helpers/ver/4.26.2.ts +84 -0
  195. package/helpers/ver/4.26.3.ts +44 -0
  196. package/helpers/ver/4.26.4.ts +31 -0
  197. package/helpers/ver/tests/4.26.1.test.ts +105 -0
  198. package/helpers/ver/tests/4.26.2.test.ts +298 -0
  199. package/helpers/ver/tests/4.26.3.test.ts +168 -0
  200. package/helpers/ver/tests/4.26.4.test.ts +88 -0
  201. package/helpers/ver/tests/coveUpdateWorker.test.ts +57 -0
  202. package/helpers/viewports.ts +2 -0
  203. package/package.json +27 -32
  204. package/styles/_global.scss +7 -7
  205. package/styles/_reset.scss +2 -2
  206. package/styles/{v2/base → base}/_file-selector.scss +4 -4
  207. package/styles/{v2/base → base}/_general.scss +2 -4
  208. package/styles/{v2/base → base}/index.scss +1 -1
  209. package/styles/base.scss +107 -165
  210. package/styles/cove-main.scss +3 -6
  211. package/styles/layout/_component.scss +110 -0
  212. package/styles/{v2/layout → layout}/_data-table.scss +7 -7
  213. package/styles/layout/_wrapper-padding.scss +27 -0
  214. package/styles/{v2/main.scss → main.scss} +3 -1
  215. package/styles/{v2/themes → themes}/_color-definitions.scss +46 -41
  216. package/styles/{_accessibility.scss → utils/_accessibility.scss} +1 -1
  217. package/styles/{v2/utils → utils}/_grid.scss +8 -3
  218. package/styles/{_global-variables.scss → utils/_properties.scss} +133 -112
  219. package/styles/{v2/utils → utils}/index.scss +2 -1
  220. package/types/Annotation.ts +10 -11
  221. package/types/Axis.ts +2 -0
  222. package/types/ComponentStyles.ts +1 -0
  223. package/types/ConfigureData.ts +1 -0
  224. package/types/General.ts +2 -0
  225. package/types/MarkupInclude.ts +1 -0
  226. package/types/MarkupVariable.ts +2 -1
  227. package/types/Palette.ts +22 -0
  228. package/types/Table.ts +9 -0
  229. package/types/Visualization.ts +7 -0
  230. package/_stories/StoryRenderingTests.stories.tsx +0 -164
  231. package/helpers/embedCodeGenerator.ts +0 -109
  232. package/styles/_common-components.css +0 -73
  233. package/styles/_variables.scss +0 -63
  234. package/styles/v2/layout/_component.scss +0 -21
  235. package/styles/v2/utils/_variables.scss +0 -9
  236. package/{styles/v2/components/card.scss → components/elements/card.css} +2 -2
  237. /package/{styles/v2/components → components/ui}/icon.scss +0 -0
  238. /package/{styles/v2/components → components/ui}/loadspin.scss +0 -0
  239. /package/styles/{v2/base → base}/_heading.scss +0 -0
  240. /package/styles/{v2/base → base}/_reset.scss +0 -0
  241. /package/styles/{v2/layout → layout}/_alert.scss +0 -0
  242. /package/styles/{v2/layout → layout}/_progression.scss +0 -0
  243. /package/styles/{v2/layout → layout}/_tooltip.scss +0 -0
  244. /package/styles/{v2/layout → layout}/index.scss +0 -0
  245. /package/styles/{v2/themes → themes}/index.scss +0 -0
  246. /package/styles/{v2/utils → utils}/_align.scss +0 -0
  247. /package/styles/{v2/utils → utils}/_animations.scss +0 -0
  248. /package/styles/{v2/utils → utils}/_breakpoints.scss +0 -0
  249. /package/styles/{v2/utils → utils}/_mixins.scss +0 -0
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
3
+ <svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="481.000000pt" height="563.000000pt" viewBox="0 0 481.000000 563.000000" preserveAspectRatio="xMidYMid meet">
4
+ <g transform="translate(0.000000,563.000000) scale(0.100000,-0.100000)" fill="#2c7a99" stroke="none">
5
+ <path d="M890 5514 c-161 -43 -308 -201 -354 -381 -14 -55 -16 -314 -16 -2527 0 -2015 2 -2466 13 -2466 7 0 51 14 97 30 47 17 92 33 100 35 8 2 16 4 18 5 1 2 7 4 12 5 6 1 11 3 13 4 1 2 13 4 26 7 14 2 30 9 38 15 7 6 13 8 13 4 0 -4 7 -2 15 5 8 7 15 10 15 6 0 -3 17 1 38 10 49 21 856 293 905 305 21 5 45 13 55 19 9 5 40 16 67 24 28 8 162 52 299 99 l249 85 151 -53 c83 -29 253 -90 379 -134 264 -94 556 -198 974 -346 164 -58 308 -110 321 -115 13 -6 27 -10 33 -10 11 0 8 4951 -4 4994 -22 80 -84 183 -152 252 -75 76 -141 114 -233 133 -38 8 -477 11 -1535 10 -1268 0 -1490 -3 -1537 -15z m2687 -1570 c49 -31 82 -134 64 -201 -10 -37 -41 -64 -422 -369 -227 -181 -430 -341 -452 -356 -43 -30 -84 -35 -136 -17 -17 6 -156 107 -309 224 l-277 213 -40 -31 c-50 -39 -318 -259 -325 -267 -3 -3 -27 -24 -55 -45 -27 -22 -55 -44 -61 -50 -86 -77 -253 -202 -277 -207 -20 -4 -53 -1 -84 8 -69 19 -105 64 -111 140 -6 70 8 100 69 149 105 84 134 107 168 133 19 15 92 74 164 132 71 58 144 116 162 130 86 68 114 90 187 150 124 102 156 120 206 120 23 0 57 -7 75 -16 18 -9 145 -103 282 -209 283 -217 293 -224 303 -210 4 6 42 39 86 74 43 35 88 71 100 81 31 27 153 127 231 190 37 30 111 91 164 135 142 119 217 145 288 99z m-1431 -983 c60 -44 59 -29 62 -601 3 -596 4 -589 -70 -638 -76 -49 -171 -29 -228 50 l-30 41 0 526 c0 591 -3 569 75 623 35 25 48 28 100 25 44 -2 69 -9 91 -26z m1252 15 c37 -19 67 -55 81 -95 8 -25 10 -183 9 -555 -3 -579 -1 -555 -71 -603 -67 -45 -144 -35 -211 27 l-41 39 -3 543 -2 544 23 35 c46 68 149 99 215 65z m-610 -343 c71 -57 71 -57 71 -448 0 -322 -1 -353 -19 -392 -27 -60 -83 -95 -155 -95 -63 -1 -95 16 -132 72 -23 33 -23 36 -23 407 0 406 -1 401 57 455 34 32 54 38 116 34 35 -2 57 -11 85 -33z m-1313 -142 c54 -24 92 -71 100 -124 4 -23 5 -159 3 -302 -3 -293 -3 -293 -82 -343 -66 -41 -160 -30 -207 24 -34 40 -39 84 -39 361 0 303 3 322 61 366 53 39 105 45 164 18z"/>
6
+ </g>
7
+ </svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free v6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path d="M64 64c0-17.7-14.3-32-32-32S0 46.3 0 64L0 400c0 44.2 35.8 80 80 80l400 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L80 416c-8.8 0-16-7.2-16-16L64 64zm96 288l288 0c17.7 0 32-14.3 32-32l0-68.2c0-7.6-2.7-15-7.7-20.8l-65.8-76.8c-12.1-14.2-33.7-15-46.9-1.8l-21 21c-10 10-26.4 9.2-35.4-1.6l-39.2-47c-12.6-15.1-35.7-15.4-48.7-.6L135.9 215c-5.1 5.8-7.9 13.3-7.9 21.1l0 84c0 17.7 14.3 32 32 32z"/></svg>
@@ -0,0 +1,23 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor">
2
+ <!-- Outer pentagon (grid) -->
3
+ <path d="M256 32l211.25 153.5L175 478.5H337l81.25-293L256 32zm0 0L44.75 185.5L175 478.5h162L175 478.5 44.75 185.5 256 32z" fill="none" stroke="currentColor" stroke-width="20" stroke-linejoin="round"/>
4
+ <!-- Middle pentagon (grid ring) -->
5
+ <path d="M256 112l126.75 92.1L207.5 375.3h97l48.75-171.2L256 112z" fill="none" stroke="currentColor" stroke-width="12" stroke-opacity="0.4" stroke-linejoin="round"/>
6
+ <path d="M256 112l-126.75 92.1L207.5 375.3h-97l-48.75-171.2L256 112z" fill="none" stroke="currentColor" stroke-width="12" stroke-opacity="0.4" stroke-linejoin="round"/>
7
+ <!-- Inner pentagon (grid ring) -->
8
+ <path d="M256 192l63.4 46L289 332.7h-66l-30-94.7L256 192z" fill="none" stroke="currentColor" stroke-width="8" stroke-opacity="0.3" stroke-linejoin="round"/>
9
+ <!-- Axis lines -->
10
+ <line x1="256" y1="256" x2="256" y2="32" stroke="currentColor" stroke-width="8" stroke-opacity="0.5"/>
11
+ <line x1="256" y1="256" x2="467.25" y2="185.5" stroke="currentColor" stroke-width="8" stroke-opacity="0.5"/>
12
+ <line x1="256" y1="256" x2="337" y2="478.5" stroke="currentColor" stroke-width="8" stroke-opacity="0.5"/>
13
+ <line x1="256" y1="256" x2="175" y2="478.5" stroke="currentColor" stroke-width="8" stroke-opacity="0.5"/>
14
+ <line x1="256" y1="256" x2="44.75" y2="185.5" stroke="currentColor" stroke-width="8" stroke-opacity="0.5"/>
15
+ <!-- Data polygon (filled) -->
16
+ <polygon points="256,80 400,200 350,420 162,420 112,200" fill="currentColor" fill-opacity="0.3" stroke="currentColor" stroke-width="16" stroke-linejoin="round"/>
17
+ <!-- Data points -->
18
+ <circle cx="256" cy="80" r="12" fill="currentColor"/>
19
+ <circle cx="400" cy="200" r="12" fill="currentColor"/>
20
+ <circle cx="350" cy="420" r="12" fill="currentColor"/>
21
+ <circle cx="162" cy="420" r="12" fill="currentColor"/>
22
+ <circle cx="112" cy="200" r="12" fill="currentColor"/>
23
+ </svg>
@@ -0,0 +1,31 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="135 160 228 147" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#0055B8;}
7
+ .st1{fill:#FFFFFF;}
8
+ </style>
9
+ <g>
10
+ <path class="st0" d="M141.22,300.14H186h151.44c10.88,0,19.84-8.27,20.91-18.87c0.07-0.71,0.11-1.42,0.11-2.15V165.04H188.93
11
+ h-26.68c-7.26,0-13.65,3.68-17.43,9.27c-0.76,1.12-1.41,2.31-1.94,3.57c-1.06,2.52-1.65,5.28-1.65,8.18V300.14z"/>
12
+ <path class="st1" d="M162.25,160.83c-13.91,0-25.23,11.32-25.23,25.23v118.28h31.62h6.95h161.85c13.91,0,25.23-11.32,25.23-25.23
13
+ V160.83H162.25z M326.53,239.21c-0.2-0.23-0.56-0.25-0.78-0.05c-1.32,1.19-5.84,4.76-12.61,4.88c-8.53,0.14-17.17-6.93-17.17-19.2
14
+ c0-12.27,8.92-19.21,17.27-19.21c6.2,0,10.2,2.92,11.41,3.95c0.23,0.19,0.57,0.17,0.77-0.06l7.57-8.41c0.18-0.2,0.2-0.51,0.02-0.72
15
+ c-1.24-1.49-6.57-6.89-18.51-6.89c-1.11,0-2.25,0.06-3.41,0.17l45.61-28.63h1.76v62.45l-31.15,12.59L326.53,239.21z M243.33,242.49
16
+ h-6.37c-0.31,0-0.56-0.25-0.56-0.56v-34.78c0-0.31,0.25-0.56,0.56-0.56h7.31c11.28,0,18.77,5.56,18.77,17.43
17
+ C263.04,237.79,255.72,242.49,243.33,242.49z M240.95,194.29h-17.32c-0.31,0-0.56,0.25-0.56,0.56v59.4c0,0.08,0.02,0.15,0.04,0.22
18
+ l-65.24,45.68h-6.1l40.05-44.48c2.03,0.34,4.03,0.51,5.93,0.51c11.85,0,18.13-6.24,19.55-7.87c0.19-0.21,0.19-0.53,0-0.74
19
+ l-7.52-8.36c-0.2-0.23-0.56-0.25-0.78-0.05c-1.32,1.19-5.84,4.76-12.61,4.88c-8.53,0.14-17.17-6.93-17.17-19.2
20
+ c0-12.27,8.92-19.21,17.27-19.21c6.2,0,10.2,2.92,11.41,3.95c0.23,0.19,0.57,0.17,0.77-0.06l7.57-8.41c0.18-0.2,0.2-0.51,0.02-0.72
21
+ c-1.24-1.49-6.57-6.89-18.51-6.89c-5.92,0-12.69,1.53-18.48,5.1l12.88-33.56h76.66L240.95,194.29z M142.88,177.88
22
+ c0.53-1.26,1.18-2.45,1.94-3.57c3.78-5.59,10.18-9.27,17.43-9.27h26.35l-14.39,37.49c-5.11,4.98-8.62,12.23-8.62,22.3
23
+ c0,0.05,0,0.1,0,0.15l-24.37,63.49V186.06C141.22,183.16,141.81,180.39,142.88,177.88z M141.22,297.77l25.1-65.41
24
+ c2.66,12.93,12.13,19.81,21.77,22.47l-40.8,45.31h-6.07V297.77z M163.69,300.14l64.76-45.34h16.43c18.03,0,32.01-10.51,32.01-30.08
25
+ c0-21.2-13.14-29.7-31.38-30.37l27.92-29.3h77.01l-49.53,31.09c-9.9,4-18.58,12.87-18.58,28.7c0,14.08,6.93,22.65,15.44,27.19
26
+ l-119.11,48.12H163.69z M358.35,281.27c-1.08,10.6-10.03,18.87-20.91,18.87h-149.9l114.43-46.23c4.21,1.55,8.58,2.26,12.53,2.26
27
+ c11.85,0,18.13-6.24,19.55-7.87c0.19-0.21,0.19-0.53,0-0.74l-4.37-4.85l28.78-11.63v48.03
28
+ C358.46,279.84,358.43,280.56,358.35,281.27z"/>
29
+ </g>
30
+
31
+ </svg>
@@ -1,31 +1,55 @@
1
1
  import React, { useState, useEffect, useMemo } from 'react'
2
- import { generateEmbedCode } from '../../helpers/embedCodeGenerator'
2
+ import {
3
+ generateEmbedCode,
4
+ extractFilters,
5
+ initializeFilterState,
6
+ buildFilterUrlParams,
7
+ type FilterMetadata,
8
+ type FilterState
9
+ } from '../../helpers/embed'
10
+ import '../../helpers/embed/embedHelper.js' // Initialize embed helper for iframe resizing
3
11
 
4
12
  type EmbedEditorProps = {
5
13
  config?: any // Current visualization config
6
14
  }
7
15
 
16
+ type TabId = 'preview' | 'code'
17
+
8
18
  /**
9
19
  * EmbedEditor - Provides "Share with Partners" functionality
10
20
  * Generates embed codes for iframe embedding of visualizations
21
+ * Now includes filter customization, preview, and embed code generation
11
22
  */
12
23
  export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
13
24
  const [configUrl, setConfigUrl] = useState<string | null>(null)
14
25
  const [showEmbedModal, setShowEmbedModal] = useState(false)
15
- const [embedCode, setEmbedCode] = useState('')
16
- const [embedCodeCopied, setEmbedCodeCopied] = useState(false)
17
26
  const [isExpanded, setIsExpanded] = useState(false)
27
+ const [activeTab, setActiveTab] = useState<TabId>('preview')
28
+ const [embedCodeCopied, setEmbedCodeCopied] = useState(false)
29
+
30
+ // Extract filters from config
31
+ const filters = useMemo(() => extractFilters(config), [config])
32
+
33
+ // Initialize filter state
34
+ const [filterState, setFilterState] = useState<Record<string, FilterState>>({})
35
+
36
+ // Update filter state when filters change
37
+ useEffect(() => {
38
+ if (filters.length > 0) {
39
+ setFilterState(initializeFilterState(filters))
40
+ }
41
+ }, [filters])
18
42
 
19
43
  // Check if all filters have setByQueryParameter
20
44
  const filtersAreValid = useMemo(() => {
21
45
  if (!config) return true
22
46
 
23
47
  // Check regular filters
24
- const filters = config.filters || []
48
+ const regularFilters = config.filters || []
25
49
  // Check dashboard shared filters
26
50
  const sharedFilters = config.dashboard?.sharedFilters || []
27
51
 
28
- const allFilters = [...filters, ...sharedFilters]
52
+ const allFilters = [...regularFilters, ...sharedFilters]
29
53
 
30
54
  // If no filters, valid
31
55
  if (allFilters.length === 0) return true
@@ -34,6 +58,20 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
34
58
  return allFilters.every((filter: any) => !!filter.setByQueryParameter)
35
59
  }, [config])
36
60
 
61
+ // Determine if we have valid filters to show
62
+ const hasFilters = filters.length > 0 && filtersAreValid
63
+
64
+ // Generate embed code with current filter settings
65
+ const embedCode = useMemo(() => {
66
+ if (!configUrl) return ''
67
+
68
+ const urlParams = buildFilterUrlParams(filters, filterState)
69
+ return generateEmbedCode({
70
+ configUrl,
71
+ urlParams
72
+ })
73
+ }, [configUrl, filters, filterState])
74
+
37
75
  // Detect configUrl from WCMS permalink or use dev fallback
38
76
  useEffect(() => {
39
77
  // Try to get config URL from WCMS permalink element
@@ -74,12 +112,33 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
74
112
  return
75
113
  }
76
114
 
77
- const code = generateEmbedCode({ configUrl })
78
- setEmbedCode(code)
115
+ setActiveTab('preview')
79
116
  setShowEmbedModal(true)
80
117
  setEmbedCodeCopied(false)
81
118
  }
82
119
 
120
+ // Handle filter value change
121
+ const handleFilterChange = (filterKey: string, value: string) => {
122
+ setFilterState(prev => ({
123
+ ...prev,
124
+ [filterKey]: {
125
+ ...prev[filterKey],
126
+ value
127
+ }
128
+ }))
129
+ }
130
+
131
+ // Handle filter hide toggle
132
+ const handleHideToggle = (filterKey: string, hide: boolean) => {
133
+ setFilterState(prev => ({
134
+ ...prev,
135
+ [filterKey]: {
136
+ ...prev[filterKey],
137
+ hide
138
+ }
139
+ }))
140
+ }
141
+
83
142
  // Handle copying embed code from modal
84
143
  const handleCopyFromModal = async () => {
85
144
  try {
@@ -98,9 +157,6 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
98
157
  setEmbedCodeCopied(false)
99
158
  }
100
159
 
101
- // Hide embed section until released
102
- return null
103
-
104
160
  return (
105
161
  <>
106
162
  {/* Collapsible Share with Partners Section */}
@@ -155,8 +211,7 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
155
211
  ) : (
156
212
  <>
157
213
  <p style={{ fontSize: '0.85em', marginBottom: '1em', color: '#666' }}>
158
- Generate embed codes for partners to add this visualization to their websites. Your visualization will
159
- need to be published to Link (www.cdc.gov) before it can be embedded by a partner.
214
+ Generate embed codes for partners to add this visualization to their website.
160
215
  </p>
161
216
 
162
217
  <div style={{ display: 'flex', flexDirection: 'column', gap: '0.5em' }}>
@@ -174,7 +229,7 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
174
229
  )}
175
230
  </div>
176
231
 
177
- {/* Embed Code Modal */}
232
+ {/* Embed Code Modal with Tabs */}
178
233
  {showEmbedModal && (
179
234
  <div
180
235
  className='modal-overlay'
@@ -186,8 +241,9 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
186
241
  bottom: 0,
187
242
  backgroundColor: 'rgba(0, 0, 0, 0.5)',
188
243
  display: 'flex',
189
- alignItems: 'center',
244
+ alignItems: 'flex-start',
190
245
  justifyContent: 'center',
246
+ paddingTop: '5vh',
191
247
  zIndex: 9999
192
248
  }}
193
249
  onClick={handleCloseModal}
@@ -198,12 +254,16 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
198
254
  backgroundColor: 'white',
199
255
  borderRadius: '8px',
200
256
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
201
- maxWidth: '600px',
257
+ maxWidth: '800px',
202
258
  width: '90%',
203
- margin: '20px'
259
+ maxHeight: '90vh',
260
+ margin: '20px',
261
+ display: 'flex',
262
+ flexDirection: 'column'
204
263
  }}
205
264
  onClick={e => e.stopPropagation()}
206
265
  >
266
+ {/* Modal Header */}
207
267
  <div
208
268
  className='modal-header'
209
269
  style={{
@@ -216,7 +276,7 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
216
276
  borderRadius: '8px 8px 0 0'
217
277
  }}
218
278
  >
219
- <h3 style={{ color: 'white', margin: 0 }}>Embed Code</h3>
279
+ <h3 style={{ color: 'white', margin: 0 }}>Share with Partners</h3>
220
280
  <button
221
281
  onClick={handleCloseModal}
222
282
  style={{
@@ -234,26 +294,196 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
234
294
  </button>
235
295
  </div>
236
296
 
237
- <div className='modal-body' style={{ padding: '20px' }}>
238
- <p style={{ marginBottom: '10px', color: '#666' }}>Copy this code and paste it into your website:</p>
239
- <textarea
240
- readOnly
241
- value={embedCode}
242
- style={{
243
- width: '100%',
244
- height: '180px',
245
- fontFamily: 'monospace',
246
- fontSize: '0.85em',
247
- padding: '10px',
248
- border: '1px solid #ddd',
249
- borderRadius: '4px',
250
- resize: 'vertical',
251
- boxSizing: 'border-box'
252
- }}
253
- onFocus={e => e.target.select()}
254
- />
297
+ {/* Tab Navigation */}
298
+ <div
299
+ style={{
300
+ display: 'flex',
301
+ borderBottom: '1px solid #e0e0e0',
302
+ backgroundColor: '#f5f5f5'
303
+ }}
304
+ >
305
+ {(['preview', 'code'] as TabId[]).map(tab => {
306
+ const tabLabels: Record<TabId, string> = {
307
+ preview: 'Preview Visualization',
308
+ code: 'Get Embed Code'
309
+ }
310
+
311
+ return (
312
+ <button
313
+ key={tab}
314
+ onClick={() => setActiveTab(tab)}
315
+ style={{
316
+ flex: 1,
317
+ padding: '12px 16px',
318
+ border: 'none',
319
+ backgroundColor: activeTab === tab ? 'white' : 'transparent',
320
+ borderBottom: activeTab === tab ? '2px solid #005eaa' : '2px solid transparent',
321
+ color: activeTab === tab ? '#005eaa' : '#666',
322
+ fontWeight: activeTab === tab ? 'bold' : 'normal',
323
+ cursor: 'pointer',
324
+ transition: 'all 0.2s'
325
+ }}
326
+ >
327
+ {tabLabels[tab]}
328
+ </button>
329
+ )
330
+ })}
255
331
  </div>
256
332
 
333
+ {/* Tab Content */}
334
+ <div
335
+ className='modal-body'
336
+ style={{
337
+ padding: '20px',
338
+ flex: 1,
339
+ overflow: 'auto'
340
+ }}
341
+ >
342
+ {/* Preview Tab - Contains filter controls (if filters exist) and preview */}
343
+ {activeTab === 'preview' && (
344
+ <div>
345
+ {/* Filter Settings - only shown if there are valid filters */}
346
+ {hasFilters && (
347
+ <>
348
+ <h4 style={{ marginTop: 0, marginBottom: '1rem' }}>Filter Settings</h4>
349
+ <p style={{ marginBottom: '1rem', color: '#666' }}>
350
+ Set default values and visibility for filters in the partner's embedded visualization.
351
+ </p>
352
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', marginBottom: '2rem' }}>
353
+ {filters.map((filter, index) => {
354
+ const state = filterState[filter.key] || { value: '', hide: false }
355
+ const hasValues = filter.values && filter.values.length > 0
356
+
357
+ return (
358
+ <div
359
+ key={filter.key || index}
360
+ style={{
361
+ padding: '1rem',
362
+ background: 'white',
363
+ border: '1px solid #ddd',
364
+ borderRadius: '4px'
365
+ }}
366
+ >
367
+ <label
368
+ htmlFor={`filter-${index}`}
369
+ style={{
370
+ display: 'block',
371
+ marginBottom: '0.5rem',
372
+ fontWeight: 'bold'
373
+ }}
374
+ >
375
+ {filter.label}
376
+ </label>
377
+
378
+ {hasValues ? (
379
+ <select
380
+ id={`filter-${index}`}
381
+ value={state.value}
382
+ onChange={e => handleFilterChange(filter.key, e.target.value)}
383
+ style={{
384
+ width: '100%',
385
+ padding: '0.5rem',
386
+ fontSize: '0.9rem',
387
+ border: '2px solid #d1d5db',
388
+ borderRadius: '6px',
389
+ backgroundColor: '#f9fafb',
390
+ cursor: 'pointer'
391
+ }}
392
+ >
393
+ {filter.values?.map((value, valueIndex) => (
394
+ <option key={valueIndex} value={value}>
395
+ {value}
396
+ </option>
397
+ ))}
398
+ </select>
399
+ ) : (
400
+ <div style={{ color: '#999', fontStyle: 'italic' }}>No values available</div>
401
+ )}
402
+
403
+ <div style={{ marginTop: '0.75rem' }}>
404
+ <label
405
+ style={{
406
+ display: 'flex',
407
+ alignItems: 'center',
408
+ cursor: 'pointer',
409
+ fontWeight: 'normal'
410
+ }}
411
+ >
412
+ <input
413
+ type='checkbox'
414
+ checked={state.hide}
415
+ onChange={e => handleHideToggle(filter.key, e.target.checked)}
416
+ style={{ marginRight: '0.5rem' }}
417
+ />
418
+ <span style={{ color: '#666' }}>Hide filter in embed</span>
419
+ </label>
420
+ </div>
421
+ </div>
422
+ )
423
+ })}
424
+ </div>
425
+ </>
426
+ )}
427
+
428
+ {/* Preview Section - title only shown if there are filters */}
429
+ {hasFilters && <h4 style={{ marginBottom: '1rem' }}>Preview</h4>}
430
+ <p style={{ marginBottom: '1rem', color: '#666' }}>
431
+ This shows how the visualization will appear on the partner website
432
+ {hasFilters ? ' with your selected settings' : ''}. The partner will have control over the width of
433
+ the embedded visualization. If you do not see the latest version of the visualization, save it and
434
+ reopen this popup.
435
+ </p>
436
+ <div
437
+ style={{
438
+ border: '2px dashed #999',
439
+ borderRadius: '4px',
440
+ padding: '1rem'
441
+ }}
442
+ >
443
+ <div
444
+ key={`${configUrl}-${JSON.stringify(filterState)}`}
445
+ data-cove-embed
446
+ data-config-url={(() => {
447
+ const urlParams = buildFilterUrlParams(filters, filterState)
448
+ const params = new URLSearchParams()
449
+ Object.entries(urlParams).forEach(([key, value]) => {
450
+ if (value) params.set(key, value)
451
+ })
452
+ return params.toString() ? `${configUrl}?${params.toString()}` : configUrl || ''
453
+ })()}
454
+ />
455
+ </div>
456
+ </div>
457
+ )}
458
+
459
+ {/* Embed Code Tab */}
460
+ {activeTab === 'code' && (
461
+ <div>
462
+ <p style={{ marginBottom: '10px', color: '#666' }}>
463
+ Copy this code and send it to a partner so they can add it to their site. Your visualization will
464
+ need to be published to Link (www.cdc.gov) before it can be embedded by a partner.
465
+ </p>
466
+ <textarea
467
+ readOnly
468
+ value={embedCode}
469
+ style={{
470
+ width: '100%',
471
+ height: '200px',
472
+ fontFamily: 'monospace',
473
+ fontSize: '0.85em',
474
+ padding: '10px',
475
+ border: '1px solid #ddd',
476
+ borderRadius: '4px',
477
+ resize: 'vertical',
478
+ boxSizing: 'border-box'
479
+ }}
480
+ onFocus={e => e.target.select()}
481
+ />
482
+ </div>
483
+ )}
484
+ </div>
485
+
486
+ {/* Modal Footer */}
257
487
  <div
258
488
  className='modal-footer'
259
489
  style={{
@@ -267,9 +497,11 @@ export const EmbedEditor: React.FC<EmbedEditorProps> = ({ config }) => {
267
497
  <button className='btn btn-secondary' onClick={handleCloseModal}>
268
498
  Close
269
499
  </button>
270
- <button className='btn btn-primary' onClick={handleCopyFromModal} style={{ minWidth: '120px' }}>
271
- {embedCodeCopied ? ' Copied!' : 'Copy to Clipboard'}
272
- </button>
500
+ {activeTab === 'code' && (
501
+ <button className='btn btn-primary' onClick={handleCopyFromModal} style={{ minWidth: '120px' }}>
502
+ {embedCodeCopied ? '✓ Copied!' : 'Copy to Clipboard'}
503
+ </button>
504
+ )}
273
505
  </div>
274
506
  </div>
275
507
  </div>
@@ -1,12 +1,12 @@
1
- .cdc-open-viz-module {
1
+ .cove-visualization {
2
2
  .accordion__panel > .alert {
3
3
  margin-bottom: 15px;
4
4
  }
5
5
 
6
6
  .alert {
7
7
  align-items: center;
8
- justify-content: center;
9
8
  display: inline-flex;
9
+ justify-content: center;
10
10
 
11
11
  .cove-icon {
12
12
  margin: 5px;
@@ -1,54 +1,54 @@
1
- .cdc-open-viz-module {
1
+ .cove-visualization {
2
2
  .cove-combobox {
3
3
  position: relative;
4
4
  width: 100%;
5
5
  }
6
6
 
7
7
  .cove-combobox-wrapper {
8
- position: relative;
9
- display: flex;
10
8
  align-items: center;
9
+ display: flex;
10
+ position: relative;
11
11
  width: 100%;
12
12
  }
13
13
 
14
14
  input.cove-combobox-input[role='combobox'] {
15
- width: 100%;
15
+ -webkit-appearance: none;
16
+ -moz-appearance: none;
17
+ appearance: none;
18
+ background-clip: padding-box;
19
+ background-color: white;
20
+ border: 1px solid var(--cool-gray-10) !important;
21
+ border-radius: 0.333rem;
22
+ color: var(--cool-gray-90);
23
+ cursor: text;
16
24
  display: block;
17
- padding: 0.5rem 3rem 0.5rem 0.5rem;
18
25
  font-family: var(--app-font-secondary);
19
- font-weight: 300;
20
26
  font-size: 0.833rem;
27
+ font-weight: 300;
21
28
  line-height: normal;
22
- color: var(--cool-gray-90);
23
- background-color: white;
24
- background-clip: padding-box;
25
- border: 1px solid var(--cool-gray-10) !important;
26
- border-radius: 0.333rem;
29
+ padding: 0.5rem 3rem 0.5rem 0.5rem;
27
30
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
28
- appearance: none;
29
- -webkit-appearance: none;
30
- -moz-appearance: none;
31
- cursor: text;
31
+ width: 100%;
32
32
  }
33
33
 
34
34
  input.cove-combobox-input[role='combobox']:focus {
35
- color: var(--cool-gray-90);
36
35
  background-color: white;
37
36
  border: 1px solid var(--cool-gray-10) !important;
38
37
  box-shadow: none;
38
+ color: var(--cool-gray-90);
39
39
  }
40
40
 
41
41
  input.cove-combobox-input[role='combobox']:focus-visible {
42
+ border-radius: 6px !important;
42
43
  outline: dashed 2px rgb(0, 122, 153) !important;
43
44
  outline-offset: 3px !important;
44
- border-radius: 6px !important;
45
45
  }
46
46
 
47
47
  input.cove-combobox-input[role='combobox']:disabled {
48
48
  background-color: var(--lightestGray);
49
49
  color: var(--cool-gray-90);
50
- opacity: 1;
51
50
  cursor: default;
51
+ opacity: 1;
52
52
  }
53
53
 
54
54
  input.cove-combobox-input[role='combobox']:disabled::placeholder {
@@ -62,21 +62,21 @@
62
62
  }
63
63
 
64
64
  .cove-combobox-button {
65
- position: absolute;
66
- right: 0.5rem;
67
- top: 50%;
68
- transform: translateY(-50%);
69
- display: flex;
70
65
  align-items: center;
71
- justify-content: center;
72
- width: 1em;
73
- height: 1em;
74
66
  background: transparent;
75
67
  border: none;
76
68
  cursor: pointer;
69
+ display: flex;
70
+ height: 1em;
71
+ justify-content: center;
77
72
  padding: 0;
78
- z-index: 2;
79
73
  pointer-events: none;
74
+ position: absolute;
75
+ right: 0.5rem;
76
+ top: 50%;
77
+ transform: translateY(-50%);
78
+ width: 1em;
79
+ z-index: 2;
80
80
  }
81
81
 
82
82
  .cove-combobox-button:disabled {
@@ -94,21 +94,21 @@
94
94
  }
95
95
 
96
96
  .cove-combobox-listbox {
97
- position: absolute;
98
- top: calc(100% + 0.125rem);
99
- left: 0;
100
- right: auto;
101
- width: 150%;
102
- z-index: 1000;
103
- max-height: 20rem;
104
- margin: 0;
105
- padding: 0;
106
- overflow-y: auto;
107
- list-style: none;
108
97
  background-color: white;
109
98
  border: 1px solid var(--cool-gray-10);
110
99
  border-radius: 0.333rem;
111
100
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
101
+ left: 0;
102
+ list-style: none;
103
+ margin: 0;
104
+ max-height: 20rem;
105
+ overflow-y: auto;
106
+ padding: 0;
107
+ position: absolute;
108
+ right: auto;
109
+ top: calc(100% + 0.125rem);
110
+ width: 150%;
111
+ z-index: 1000;
112
112
  }
113
113
 
114
114
  .cove-combobox-listbox:has(.no-results) {
@@ -116,14 +116,14 @@
116
116
  }
117
117
 
118
118
  .cove-combobox-option {
119
- padding: 0.5rem 0.5rem;
120
- font-family: var(--app-font-secondary);
121
- font-weight: 300;
122
- font-size: 0.778rem;
123
119
  color: var(--cool-gray-90);
124
120
  cursor: pointer;
125
- user-select: none;
121
+ font-family: var(--app-font-secondary);
122
+ font-size: 0.778rem;
123
+ font-weight: 300;
124
+ padding: 0.5rem 0.5rem;
126
125
  transition: background-color 0.15s ease-in-out;
126
+ user-select: none;
127
127
  }
128
128
 
129
129
  .cove-combobox-option .cove-combobox-option-highlight {
@@ -148,15 +148,15 @@
148
148
  }
149
149
 
150
150
  .sr-only {
151
- position: absolute;
152
- width: 1px;
151
+ border: 0;
152
+ clip: rect(0, 0, 0, 0);
153
153
  height: 1px;
154
- padding: 0;
155
154
  margin: -1px;
156
155
  overflow: hidden;
157
- clip: rect(0, 0, 0, 0);
156
+ padding: 0;
157
+ position: absolute;
158
158
  white-space: nowrap;
159
- border: 0;
159
+ width: 1px;
160
160
  }
161
161
 
162
162
  @media (prefers-reduced-motion: reduce) {