@tpzdsp/next-toolkit 2.5.1 → 3.1.0

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 (357) hide show
  1. package/package.json +21 -12
  2. package/src/assets/styles/globals.css +75 -95
  3. package/src/assets/styles/ol.css +57 -16
  4. package/src/assets/styles/utils.css +80 -0
  5. package/src/components/Accordion/Accordion.css +85 -0
  6. package/src/components/Accordion/Accordion.stories.tsx +264 -0
  7. package/src/components/Accordion/Accordion.tsx +98 -0
  8. package/src/components/Alert/Alert.css +129 -0
  9. package/src/components/Alert/Alert.stories.tsx +291 -0
  10. package/src/components/Alert/Alert.tsx +76 -0
  11. package/src/components/BackLink/BackLink.css +12 -0
  12. package/src/components/BackLink/BackLink.stories.tsx +52 -0
  13. package/src/components/BackLink/BackLink.tsx +44 -0
  14. package/src/components/BackToTop/BackToTop.css +23 -0
  15. package/src/components/BackToTop/BackToTop.stories.tsx +41 -0
  16. package/src/components/BackToTop/BackToTop.tsx +48 -0
  17. package/src/components/Breadcrumb/Breadcrumb.css +42 -0
  18. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +94 -0
  19. package/src/components/Breadcrumb/Breadcrumb.tsx +135 -0
  20. package/src/components/Button/Button.css +229 -0
  21. package/src/components/Button/Button.stories.tsx +140 -20
  22. package/src/components/Button/Button.tsx +53 -28
  23. package/src/components/Button/ButtonSkeleton.stories.tsx +50 -0
  24. package/src/components/Button/ButtonSkeleton.tsx +14 -0
  25. package/src/components/ButtonGroup/ButtonGroup.css +52 -0
  26. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +156 -0
  27. package/src/components/ButtonGroup/ButtonGroup.tsx +50 -0
  28. package/src/components/ButtonLink/ButtonLink.css +114 -0
  29. package/src/components/ButtonLink/ButtonLink.stories.tsx +38 -51
  30. package/src/components/ButtonLink/ButtonLink.tsx +54 -23
  31. package/src/components/Card/Card.css +13 -0
  32. package/src/components/Card/Card.stories.tsx +98 -57
  33. package/src/components/Card/Card.tsx +4 -13
  34. package/src/components/CardGroup/CardGroup.css +10 -0
  35. package/src/components/CardGroup/CardGroup.stories.tsx +81 -0
  36. package/src/components/CardGroup/CardGroup.tsx +9 -0
  37. package/src/components/Checkbox/Checkbox.css +49 -0
  38. package/src/components/Checkbox/Checkbox.stories.tsx +86 -0
  39. package/src/components/Checkbox/Checkbox.tsx +32 -0
  40. package/src/components/Chip/Chip.css +54 -0
  41. package/src/components/Chip/Chip.stories.tsx +94 -0
  42. package/src/components/Chip/Chip.tsx +34 -0
  43. package/src/components/CookieBanner/CookieBanner.css +19 -0
  44. package/src/components/CookieBanner/CookieBanner.stories.tsx +52 -0
  45. package/src/components/CookieBanner/CookieBanner.tsx +55 -0
  46. package/src/components/CopyButton/CopyButton.stories.tsx +28 -0
  47. package/src/components/CopyButton/CopyButton.tsx +65 -0
  48. package/src/components/DateInput/DateInput.css +13 -0
  49. package/src/components/DateInput/DateInput.stories.tsx +131 -0
  50. package/src/components/DateInput/DateInput.test.tsx +21 -0
  51. package/src/components/DateInput/DateInput.tsx +98 -0
  52. package/src/components/Details/Details.css +28 -0
  53. package/src/components/Details/Details.stories.tsx +48 -0
  54. package/src/components/Details/Details.tsx +41 -0
  55. package/src/components/Dropdown/Dropdown.css +81 -0
  56. package/src/components/Dropdown/Dropdown.stories.tsx +131 -0
  57. package/src/components/Dropdown/Dropdown.tsx +69 -0
  58. package/src/components/ErrorBoundary/ErrorBoundary.stories.tsx +4 -39
  59. package/src/components/ErrorBoundary/ErrorBoundary.tsx +7 -4
  60. package/src/components/ErrorBoundary/ErrorFallback.tsx +32 -66
  61. package/src/components/ErrorModal/ErrorModal.css +34 -0
  62. package/src/components/ErrorModal/ErrorModal.stories.tsx +100 -0
  63. package/src/components/ErrorModal/ErrorModal.tsx +123 -0
  64. package/src/components/ErrorText/ErrorText.css +22 -0
  65. package/src/components/ErrorText/ErrorText.stories.tsx +42 -27
  66. package/src/components/ErrorText/ErrorText.tsx +10 -8
  67. package/src/components/ExternalLink/ExternalLink.css +12 -0
  68. package/src/components/ExternalLink/ExternalLink.stories.tsx +43 -0
  69. package/src/components/ExternalLink/ExternalLink.tsx +43 -0
  70. package/src/components/Fieldset/Fieldset.css +103 -0
  71. package/src/components/Fieldset/Fieldset.stories.tsx +304 -0
  72. package/src/components/Fieldset/Fieldset.tsx +38 -0
  73. package/src/components/FileUpload/FileUpload.css +85 -0
  74. package/src/components/FileUpload/FileUpload.stories.tsx +106 -0
  75. package/src/components/FileUpload/FileUpload.tsx +99 -0
  76. package/src/components/{layout/footer → Footer}/Copyright.tsx +3 -2
  77. package/src/components/Footer/Footer.stories.tsx +27 -0
  78. package/src/components/Footer/Footer.tsx +46 -0
  79. package/src/components/Footer/Licence.tsx +18 -0
  80. package/src/components/Footer/MetaLinks.tsx +47 -0
  81. package/src/components/Footer/Small/SmallFooter.stories.tsx +3 -0
  82. package/src/components/Footer/Small/SmallFooter.tsx +53 -41
  83. package/src/components/Form/FormGroup.css +18 -0
  84. package/src/components/Form/FormGroup.stories.tsx +87 -0
  85. package/src/components/Form/FormGroup.tsx +16 -0
  86. package/src/components/{layout/header → Header}/Header.stories.tsx +26 -12
  87. package/src/components/Header/Header.test.tsx +87 -0
  88. package/src/components/Header/Header.tsx +304 -0
  89. package/src/components/Header/HeaderAppContent.tsx +120 -0
  90. package/src/components/Heading/Heading.css +40 -0
  91. package/src/components/Heading/Heading.stories.tsx +15 -5
  92. package/src/components/Heading/Heading.tsx +21 -44
  93. package/src/components/Heading/HeadingSkeleton.stories.tsx +30 -0
  94. package/src/components/Heading/HeadingSkeleton.tsx +14 -0
  95. package/src/components/Hero/Hero.css +19 -0
  96. package/src/components/Hero/Hero.stories.tsx +41 -0
  97. package/src/components/Hero/Hero.tsx +14 -0
  98. package/src/components/HintText/HintText.css +5 -0
  99. package/src/components/HintText/HintText.stories.tsx +29 -0
  100. package/src/components/HintText/HintText.tsx +13 -0
  101. package/src/components/InfoBox/InfoBox.css +38 -0
  102. package/src/components/InfoBox/InfoBox.stories.tsx +51 -403
  103. package/src/components/InfoBox/InfoBox.tsx +31 -158
  104. package/src/components/Input/Input.css +77 -0
  105. package/src/components/Input/Input.stories.tsx +123 -0
  106. package/src/components/Input/Input.tsx +74 -0
  107. package/src/components/InputGroup/InputGroup.css +140 -0
  108. package/src/components/InputGroup/InputGroup.stories.tsx +125 -0
  109. package/src/components/InputGroup/InputGroup.tsx +47 -0
  110. package/src/components/InsetText/InsetText.css +5 -0
  111. package/src/components/InsetText/InsetText.stories.tsx +30 -0
  112. package/src/components/InsetText/InsetText.tsx +9 -0
  113. package/src/components/Keyboard/Keyboard.css +9 -0
  114. package/src/components/Keyboard/Keyboard.stories.tsx +63 -0
  115. package/src/components/Keyboard/Keyboard.tsx +30 -0
  116. package/src/components/Label/Label.css +27 -0
  117. package/src/components/Label/Label.stories.tsx +40 -0
  118. package/src/components/Label/Label.tsx +17 -0
  119. package/src/components/LayerSwitcher/LayerSwitcher.css +59 -0
  120. package/src/components/LayerSwitcher/LayerSwitcher.stories.tsx +112 -0
  121. package/src/components/LayerSwitcher/LayerSwitcher.tsx +61 -0
  122. package/src/components/Layout/401/401.stories.tsx +23 -0
  123. package/src/components/Layout/401/401.tsx +1 -0
  124. package/src/components/Layout/403/403.stories.tsx +23 -0
  125. package/src/components/Layout/403/403.tsx +21 -0
  126. package/src/components/Layout/404/404.stories.tsx +23 -0
  127. package/src/components/Layout/404/404.tsx +28 -0
  128. package/src/components/Layout/500/500.stories.tsx +56 -0
  129. package/src/components/Layout/500/500.tsx +63 -0
  130. package/src/components/Layout/ButtonGroup/ButtonGroup.css +52 -0
  131. package/src/components/Layout/ButtonGroup/ButtonGroup.stories.tsx +156 -0
  132. package/src/components/Layout/ButtonGroup/ButtonGroup.tsx +50 -0
  133. package/src/components/Layout/HeadingGroup/HeadingGroup.css +21 -0
  134. package/src/components/Layout/HeadingGroup/HeadingGroup.stories.tsx +84 -0
  135. package/src/components/Layout/HeadingGroup/HeadingGroup.tsx +9 -0
  136. package/src/components/Layout/MainWrapper/MainWrapper.css +12 -0
  137. package/src/components/Layout/MainWrapper/MainWrapper.stories.tsx +36 -0
  138. package/src/components/Layout/MainWrapper/MainWrapper.tsx +14 -0
  139. package/src/components/Layout/PageShell/PageShell.css +42 -0
  140. package/src/components/Layout/PageShell/PageShell.stories.tsx +108 -0
  141. package/src/components/Layout/PageShell/PageShell.tsx +31 -0
  142. package/src/components/Layout/ParagraphGroup/ParagraphGroup.css +5 -0
  143. package/src/components/Layout/ParagraphGroup/ParagraphGroup.stories.tsx +34 -0
  144. package/src/components/Layout/ParagraphGroup/ParagraphGroup.tsx +9 -0
  145. package/src/components/Layout/Stack/Stack.css +29 -0
  146. package/src/components/Layout/Stack/Stack.stories.tsx +83 -0
  147. package/src/components/Layout/Stack/Stack.tsx +19 -0
  148. package/src/components/Layout/WidthContainer/WidthContainer.css +17 -0
  149. package/src/components/Layout/WidthContainer/WidthContainer.stories.tsx +59 -0
  150. package/src/components/Layout/WidthContainer/WidthContainer.tsx +13 -0
  151. package/src/components/Legend/Legend.css +95 -0
  152. package/src/components/Legend/Legend.stories.tsx +81 -0
  153. package/src/components/Legend/Legend.tsx +32 -0
  154. package/src/components/Legend/LegendDot.tsx +15 -0
  155. package/src/components/Legend/LegendPanel.stories.tsx +101 -0
  156. package/src/components/Legend/LegendPanel.tsx +68 -0
  157. package/src/components/Legend/LegendSkeleton.stories.tsx +87 -0
  158. package/src/components/Legend/LegendSkeleton.tsx +73 -0
  159. package/src/components/LegendSelect/LegendSelect.css +8 -0
  160. package/src/components/LegendSelect/LegendSelect.stories.tsx +80 -0
  161. package/src/components/LegendSelect/LegendSelect.tsx +26 -0
  162. package/src/components/Link/Link.css +98 -0
  163. package/src/components/Link/Link.stories.tsx +70 -0
  164. package/src/components/Link/Link.tsx +52 -0
  165. package/src/components/LinkButton/LinkButton.css +67 -0
  166. package/src/components/LinkButton/LinkButton.stories.tsx +90 -42
  167. package/src/components/LinkButton/LinkButton.tsx +50 -63
  168. package/src/components/LinkList/LinkList.css +28 -0
  169. package/src/components/LinkList/LinkList.stories.tsx +46 -0
  170. package/src/components/LinkList/LinkList.tsx +23 -0
  171. package/src/components/List/Li.tsx +9 -0
  172. package/src/components/List/List.css +9 -0
  173. package/src/components/List/OrderedList.stories.tsx +36 -0
  174. package/src/components/List/OrderedList.tsx +9 -0
  175. package/src/components/List/UnorderedList.stories.tsx +36 -0
  176. package/src/components/List/UnorderedList.tsx +9 -0
  177. package/src/components/LoadingBox/LoadingBox.css +27 -0
  178. package/src/components/LoadingBox/LoadingBox.stories.tsx +66 -0
  179. package/src/components/LoadingBox/LoadingBox.tsx +40 -0
  180. package/src/components/Modal/Modal.css +111 -0
  181. package/src/components/Modal/Modal.stories.tsx +94 -155
  182. package/src/components/Modal/Modal.tsx +141 -75
  183. package/src/components/NoScriptBanner/NoScriptBanner.css +17 -0
  184. package/src/components/NoScriptBanner/NoScriptBanner.stories.tsx +46 -0
  185. package/src/components/NoScriptBanner/NoScriptBanner.tsx +26 -0
  186. package/src/components/Paragraph/Paragraph.css +22 -0
  187. package/src/components/Paragraph/Paragraph.stories.tsx +1 -42
  188. package/src/components/Paragraph/Paragraph.tsx +4 -3
  189. package/src/components/Paragraph/ParagraphSkeleton.stories.tsx +44 -0
  190. package/src/components/Paragraph/ParagraphSkeleton.tsx +33 -0
  191. package/src/components/PasswordInput/PasswordInput.css +3 -0
  192. package/src/components/PasswordInput/PasswordInput.stories.tsx +47 -0
  193. package/src/components/PasswordInput/PasswordInput.tsx +45 -0
  194. package/src/components/PhaseBanner/PhaseBanner.css +12 -0
  195. package/src/components/PhaseBanner/PhaseBanner.stories.tsx +61 -0
  196. package/src/components/PhaseBanner/PhaseBanner.tsx +17 -0
  197. package/src/components/Popover/Popover.css +43 -0
  198. package/src/components/Popover/Popover.stories.tsx +102 -0
  199. package/src/components/Popover/Popover.tsx +40 -0
  200. package/src/components/Radio/Radio.css +69 -0
  201. package/src/components/Radio/Radio.stories.tsx +169 -0
  202. package/src/components/Radio/Radio.tsx +56 -0
  203. package/src/components/Resizable/Resizable.css +86 -0
  204. package/src/components/Resizable/Resizable.stories.tsx +123 -0
  205. package/src/components/Resizable/Resizable.tsx +86 -0
  206. package/src/components/ScrollArea/ScrollArea.css +9 -0
  207. package/src/components/ScrollArea/ScrollArea.stories.tsx +42 -0
  208. package/src/components/ScrollArea/ScrollArea.tsx +11 -0
  209. package/src/components/SearchBox/SearchBox.stories.tsx +88 -0
  210. package/src/components/SearchBox/SearchBox.tsx +36 -0
  211. package/src/components/Select/AsyncSelect.stories.tsx +84 -0
  212. package/src/components/Select/AsyncSelect.tsx +41 -0
  213. package/src/components/Select/Select.css +202 -0
  214. package/src/components/Select/Select.stories.tsx +162 -0
  215. package/src/components/Select/Select.test.tsx +36 -0
  216. package/src/components/Select/Select.tsx +296 -0
  217. package/src/components/Select/SelectSkeleton.stories.tsx +51 -0
  218. package/src/components/Select/SelectSkeleton.tsx +23 -0
  219. package/src/components/Separator/Separator.css +41 -0
  220. package/src/components/Separator/Separator.stories.tsx +63 -0
  221. package/src/components/Separator/Separator.tsx +42 -0
  222. package/src/components/Sidebar/ResizableSidebar.tsx +118 -0
  223. package/src/components/Sidebar/Sidebar.css +203 -0
  224. package/src/components/Sidebar/Sidebar.stories.tsx +438 -0
  225. package/src/components/Sidebar/Sidebar.tsx +225 -0
  226. package/src/components/Skeleton/Skeleton.css +16 -0
  227. package/src/components/Skeleton/Skeleton.stories.tsx +39 -0
  228. package/src/components/Skeleton/Skeleton.tsx +12 -0
  229. package/src/components/SkipLink/SkipLink.css +16 -0
  230. package/src/components/SkipLink/SkipLink.stories.tsx +40 -0
  231. package/src/components/SkipLink/SkipLink.tsx +15 -0
  232. package/src/components/Spinner/Spinner.css +30 -0
  233. package/src/components/Spinner/Spinner.stories.tsx +36 -0
  234. package/src/components/Spinner/Spinner.tsx +45 -0
  235. package/src/components/StartButton/StartButton.css +13 -0
  236. package/src/components/StartButton/StartButton.stories.tsx +68 -0
  237. package/src/components/StartButton/StartButton.tsx +50 -0
  238. package/src/components/SubmitButton/SubmitButton.stories.tsx +31 -0
  239. package/src/components/SubmitButton/SubmitButton.tsx +14 -0
  240. package/src/components/SummaryList/SummaryList.css +71 -0
  241. package/src/components/SummaryList/SummaryList.stories.tsx +211 -0
  242. package/src/components/SummaryList/SummaryList.tsx +41 -0
  243. package/src/components/SummaryList/SummaryListSkeleton.stories.tsx +67 -0
  244. package/src/components/SummaryList/SummaryListSkeleton.tsx +40 -0
  245. package/src/components/Tabs/Tabs.css +84 -0
  246. package/src/components/Tabs/Tabs.stories.tsx +133 -0
  247. package/src/components/Tabs/Tabs.tsx +66 -0
  248. package/src/components/Tag/Tag.css +8 -0
  249. package/src/components/Tag/Tag.stories.tsx +96 -0
  250. package/src/components/Tag/Tag.tsx +34 -0
  251. package/src/components/Textarea/Textarea.css +38 -0
  252. package/src/components/Textarea/Textarea.stories.tsx +131 -0
  253. package/src/components/Textarea/Textarea.tsx +50 -0
  254. package/src/components/TextareaCount/TextareaCount.css +17 -0
  255. package/src/components/TextareaCount/TextareaCount.stories.tsx +131 -0
  256. package/src/components/TextareaCount/TextareaCount.tsx +108 -0
  257. package/src/components/Tooltip/Tooltip.css +43 -0
  258. package/src/components/Tooltip/Tooltip.stories.tsx +110 -0
  259. package/src/components/Tooltip/Tooltip.tsx +53 -0
  260. package/src/components/WarningText/WarningText.css +20 -0
  261. package/src/components/WarningText/WarningText.stories.tsx +29 -0
  262. package/src/components/WarningText/WarningText.tsx +17 -0
  263. package/src/components/index.ts +343 -61
  264. package/src/errors/errorDisplay.ts +70 -0
  265. package/src/errors/index.ts +1 -0
  266. package/src/http/constants.ts +2 -0
  267. package/src/http/fetch.ts +7 -3
  268. package/src/map/LayerSwitcherPanel.tsx +2 -173
  269. package/src/map/LegendShim.tsx +2 -0
  270. package/src/map/MapComponent.tsx +7 -17
  271. package/src/map/MapControlsOverlay.tsx +68 -175
  272. package/src/map/Popup.tsx +71 -24
  273. package/src/map/geocoder/Geocoder.test.tsx +46 -3
  274. package/src/map/geocoder/Geocoder.tsx +155 -278
  275. package/src/map/index.ts +1 -1
  276. package/src/types/utils.ts +0 -10
  277. package/src/utils/components.ts +96 -0
  278. package/src/utils/index.ts +1 -0
  279. package/src/utils/utils.ts +0 -12
  280. package/src/assets/images/defra-logo.svg +0 -51
  281. package/src/assets/images/ea-logo.svg +0 -58
  282. package/src/assets/images/ogl.svg +0 -1
  283. package/src/components/Button/Button.test.tsx +0 -53
  284. package/src/components/ButtonLink/ButtonLink.test.tsx +0 -154
  285. package/src/components/Card/Card.test.tsx +0 -51
  286. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +0 -75
  287. package/src/components/ErrorBoundary/ErrorFallback.test.tsx +0 -107
  288. package/src/components/ErrorText/ErrorText.test.tsx +0 -33
  289. package/src/components/Heading/Heading.test.tsx +0 -22
  290. package/src/components/Hint/Hint.stories.tsx +0 -58
  291. package/src/components/Hint/Hint.test.tsx +0 -33
  292. package/src/components/Hint/Hint.tsx +0 -12
  293. package/src/components/InfoBox/InfoBox.test.tsx +0 -330
  294. package/src/components/InfoBox/types.ts +0 -6
  295. package/src/components/LinkButton/LinkButton.test.tsx +0 -177
  296. package/src/components/Modal/Modal.test.tsx +0 -246
  297. package/src/components/NotificationBanner/NotificationBanner.stories.tsx +0 -45
  298. package/src/components/NotificationBanner/NotificationBanner.test.tsx +0 -58
  299. package/src/components/NotificationBanner/NotificationBanner.tsx +0 -45
  300. package/src/components/Paragraph/Paragraph.test.tsx +0 -10
  301. package/src/components/SlidingPanel/SlidingPanel.stories.tsx +0 -129
  302. package/src/components/SlidingPanel/SlidingPanel.test.tsx +0 -83
  303. package/src/components/SlidingPanel/SlidingPanel.tsx +0 -258
  304. package/src/components/accordion/Accordion.stories.tsx +0 -234
  305. package/src/components/accordion/Accordion.test.tsx +0 -192
  306. package/src/components/accordion/Accordion.tsx +0 -73
  307. package/src/components/backToTop/BackToTop.stories.tsx +0 -407
  308. package/src/components/backToTop/BackToTop.test.tsx +0 -57
  309. package/src/components/backToTop/BackToTop.tsx +0 -134
  310. package/src/components/chip/Chip.stories.tsx +0 -61
  311. package/src/components/chip/Chip.test.tsx +0 -31
  312. package/src/components/chip/Chip.tsx +0 -23
  313. package/src/components/container/Container.tsx +0 -42
  314. package/src/components/cookieBanner/CookieBanner.stories.tsx +0 -257
  315. package/src/components/cookieBanner/CookieBanner.test.tsx +0 -68
  316. package/src/components/cookieBanner/CookieBanner.tsx +0 -73
  317. package/src/components/divider/RuleDivider.stories.tsx +0 -254
  318. package/src/components/divider/RuleDivider.test.tsx +0 -164
  319. package/src/components/divider/RuleDivider.tsx +0 -23
  320. package/src/components/dropdown/DropdownMenu.test.tsx +0 -211
  321. package/src/components/dropdown/DropdownMenu.tsx +0 -108
  322. package/src/components/dropdown/useDropdownMenu.ts +0 -249
  323. package/src/components/form/Input.stories.tsx +0 -435
  324. package/src/components/form/Input.test.tsx +0 -206
  325. package/src/components/form/Input.tsx +0 -23
  326. package/src/components/form/TextArea.stories.tsx +0 -464
  327. package/src/components/form/TextArea.test.tsx +0 -232
  328. package/src/components/form/TextArea.tsx +0 -23
  329. package/src/components/layout/footer/Footer.tsx +0 -26
  330. package/src/components/layout/footer/Licence.tsx +0 -19
  331. package/src/components/layout/footer/MetaLinks.tsx +0 -36
  332. package/src/components/layout/header/Header.test.tsx +0 -36
  333. package/src/components/layout/header/Header.tsx +0 -90
  334. package/src/components/layout/header/HeaderAuthClient.test.tsx +0 -45
  335. package/src/components/layout/header/HeaderAuthClient.tsx +0 -40
  336. package/src/components/layout/header/HeaderNavClient.test.tsx +0 -44
  337. package/src/components/layout/header/HeaderNavClient.tsx +0 -67
  338. package/src/components/link/ExternalLink.test.tsx +0 -102
  339. package/src/components/link/ExternalLink.tsx +0 -29
  340. package/src/components/link/Link.tsx +0 -26
  341. package/src/components/select/Select.stories.tsx +0 -336
  342. package/src/components/select/Select.test.tsx +0 -474
  343. package/src/components/select/Select.tsx +0 -125
  344. package/src/components/select/SelectSkeleton.stories.tsx +0 -194
  345. package/src/components/select/SelectSkeleton.test.tsx +0 -104
  346. package/src/components/select/SelectSkeleton.tsx +0 -23
  347. package/src/components/select/common.ts +0 -3
  348. package/src/components/select/index.ts +0 -18
  349. package/src/components/skipLink/SkipLink.stories.tsx +0 -346
  350. package/src/components/skipLink/SkipLink.test.tsx +0 -22
  351. package/src/components/skipLink/SkipLink.tsx +0 -49
  352. package/src/map/LegendPanel.tsx +0 -85
  353. /package/src/{components → assets}/images/DefraLogo.tsx +0 -0
  354. /package/src/{components → assets}/images/EaLogo.tsx +0 -0
  355. /package/src/{components → assets}/images/OglLogo.tsx +0 -0
  356. /package/src/components/{googleAnalytics → GoogleAnalytics}/GlobalVars.tsx +0 -0
  357. /package/src/components/{googleAnalytics → GoogleAnalytics}/GoogleAnalytics.tsx +0 -0
@@ -1,173 +1,2 @@
1
- 'use client';
2
-
3
- import { useEffect, useRef, useState } from 'react';
4
-
5
- import { FocusTrap } from 'focus-trap-react';
6
- import BaseLayer from 'ol/layer/Base';
7
- import { createPortal } from 'react-dom';
8
-
9
- import { cn } from '../utils';
10
-
11
- type LayerSwitcherPanelProps = {
12
- isOpen: boolean;
13
- onClose: () => void;
14
- layers: BaseLayer[];
15
- activeLayerName: string | null;
16
- onSelectLayer: (layerName: string) => void;
17
- buttonRect: DOMRect | null;
18
- };
19
-
20
- export const LayerSwitcherPanel = ({
21
- isOpen,
22
- onClose,
23
- layers,
24
- activeLayerName,
25
- onSelectLayer,
26
- buttonRect,
27
- }: LayerSwitcherPanelProps) => {
28
- const [isMounted, setIsMounted] = useState(false);
29
- const [isTrapActive, setIsTrapActive] = useState(false);
30
- const panelRef = useRef<HTMLDivElement>(null);
31
- const closeButtonRef = useRef<HTMLButtonElement>(null);
32
-
33
- const handleClose = () => {
34
- setIsTrapActive(false);
35
- onClose();
36
- };
37
-
38
- useEffect(() => {
39
- setIsMounted(true);
40
- }, []);
41
-
42
- useEffect(() => {
43
- if (isOpen) {
44
- setIsTrapActive(true);
45
- } else {
46
- setIsTrapActive(false);
47
- }
48
- }, [isOpen]);
49
-
50
- if (!isMounted || !isOpen) {
51
- return <></>;
52
- }
53
-
54
- // Calculate position based on button position
55
- const top = buttonRect ? buttonRect.bottom + 8 : 118;
56
- const right = 48; // 3rem = 48px
57
-
58
- // Calculate max-height to prevent panel from going off-screen
59
- // Leave 16px padding at the bottom
60
- const maxHeight = buttonRect
61
- ? `${window.innerHeight - buttonRect.bottom - 24}px`
62
- : 'calc(100vh - 150px)';
63
-
64
- const panelClasses = cn(
65
- 'fixed bg-white rounded-lg shadow-lg border border-gray-200',
66
- 'flex flex-col',
67
- 'p-2 gap-2',
68
- 'w-[180px] max-w-[250px]',
69
- 'transition-all duration-300 ease-in-out',
70
- 'z-[9999]',
71
- isOpen ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-full pointer-events-none',
72
- );
73
-
74
- const contentClasses = cn(
75
- 'grid grid-cols-1 gap-2',
76
- 'flex-1 min-h-0',
77
- 'overflow-y-auto overflow-x-hidden',
78
- );
79
-
80
- return createPortal(
81
- <FocusTrap
82
- active={isTrapActive}
83
- focusTrapOptions={{
84
- clickOutsideDeactivates: true,
85
- escapeDeactivates: true,
86
- onDeactivate: handleClose,
87
- returnFocusOnDeactivate: true,
88
- initialFocus: false, // Don't auto-focus close button
89
- fallbackFocus: () => panelRef.current ?? document.body,
90
- }}
91
- >
92
- {/* We use role="dialog" instead of <dialog> tag for better portal positioning control */}
93
- <div
94
- ref={panelRef}
95
- role="dialog"
96
- aria-modal="true"
97
- aria-label="Basemap switcher"
98
- aria-describedby="layer-switcher-description"
99
- style={{ top, right, maxHeight }}
100
- className={panelClasses}
101
- >
102
- {/* Screen reader description */}
103
- <div id="layer-switcher-description" className="sr-only">
104
- Select a basemap layer. Use arrow keys to navigate between options, Enter or Space to
105
- select. Press Escape to close.
106
- </div>
107
- {/* Header with close button */}
108
- <div className="flex justify-end flex-shrink-0">
109
- <button
110
- ref={closeButtonRef}
111
- type="button"
112
- onClick={handleClose}
113
- aria-label="Close basemap switcher"
114
- tabIndex={0}
115
- className="w-8 h-8 flex items-center justify-center rounded text-gray-600 hover:bg-focus
116
- focus:outline focus:outline-2 focus:outline-focus focus:bg-focus transition-colors"
117
- >
118
- <span className="text-xl leading-none" aria-hidden="true">
119
- &times;
120
- </span>
121
- </button>
122
- </div>
123
-
124
- {/* Content with basemap buttons */}
125
- <div className={contentClasses} role="radiogroup" aria-label="Basemap options">
126
- {layers.map((layer) => {
127
- const name = layer.get('name') as string;
128
- const image = layer.get('image') as string;
129
- const isActive = name === activeLayerName;
130
-
131
- return (
132
- <button
133
- key={name}
134
- type="button"
135
- role="radio"
136
- aria-checked={isActive}
137
- aria-label={`${name} basemap${isActive ? ', currently selected' : ''}`}
138
- onClick={() => onSelectLayer(name)}
139
- className={cn(
140
- 'flex flex-col items-center justify-start gap-2',
141
- 'w-full p-2',
142
- 'border-2 rounded',
143
- 'transition-all duration-200',
144
- 'focus:outline focus:outline-2 focus:outline-focus',
145
- isActive
146
- ? 'bg-focus text-black border-focus'
147
- : 'bg-white text-black border-gray-300 hover:bg-focus',
148
- )}
149
- >
150
- <div className="flex items-center justify-center w-full flex-shrink-0">
151
- <img
152
- src={image}
153
- alt={`Preview of ${name}`}
154
- title={name}
155
- className={cn(
156
- 'max-w-[60px] max-h-[60px] w-auto h-auto object-contain border',
157
- isActive ? 'border-black' : 'grayscale border-black',
158
- )}
159
- />
160
- </div>
161
-
162
- <div className="text-xs leading-tight text-center w-full flex-shrink-0 px-1">
163
- {name}
164
- </div>
165
- </button>
166
- );
167
- })}
168
- </div>
169
- </div>
170
- </FocusTrap>,
171
- document.body,
172
- );
173
- };
1
+ export { LayerSwitcher } from '../components/LayerSwitcher/LayerSwitcher';
2
+ export type { LayerSwitcherProps, LayerOption } from '../components/LayerSwitcher/LayerSwitcher';
@@ -0,0 +1,2 @@
1
+ export { Legend } from '../components/Legend/Legend';
2
+ export type { LegendProps, LegendOption } from '../components/Legend/Legend';
@@ -13,11 +13,13 @@ import { getPopupPositionClass, LAYER_NAMES } from './utils';
13
13
  import type { PopupDirection } from '../types/map';
14
14
 
15
15
  export type MapComponentProps = {
16
- osMapsApiKey?: string;
17
16
  basePath: string;
18
17
  children?: ReactNode;
19
18
  };
20
19
 
20
+ // Where the popup-container div itself sits relative to the clicked
21
+ // coordinate. Popup owns the matching arrow-position lookup for its own
22
+ // pointer, since that's internal to how it renders, not where it's placed.
21
23
  const positionTransforms: Record<PopupDirection, string> = {
22
24
  'bottom-left': 'top-[-2em] right-[1em]',
23
25
  'top-left': 'bottom-[-2em] right-[1em]',
@@ -25,13 +27,6 @@ const positionTransforms: Record<PopupDirection, string> = {
25
27
  'bottom-right': 'top-[-2em] left-[1em]',
26
28
  };
27
29
 
28
- const arrowStyles: Record<PopupDirection, string> = {
29
- 'bottom-left': 'right-[-.5em] top-[1em]',
30
- 'bottom-right': 'left-[-.5em] top-[1em]',
31
- 'top-left': 'right-[-.5em] bottom-[1em]',
32
- 'top-right': 'left-[-.5em] bottom-[1em]',
33
- };
34
-
35
30
  /**
36
31
  * This component manages the main map, this is where the basemap layers
37
32
  * and basic controls are added to the map. The map component encapsulates
@@ -42,7 +37,7 @@ const arrowStyles: Record<PopupDirection, string> = {
42
37
  *
43
38
  * @return {*}
44
39
  */
45
- const MapComponentBase = ({ osMapsApiKey, basePath, children }: MapComponentProps) => {
40
+ const MapComponentBase = ({ basePath, children }: MapComponentProps) => {
46
41
  const [popupCoordinate, setPopupCoordinate] = useState<number[] | null>(null);
47
42
  const [popupPositionClass, setPopupPositionClass] = useState<PopupDirection>('bottom-right');
48
43
 
@@ -65,11 +60,6 @@ const MapComponentBase = ({ osMapsApiKey, basePath, children }: MapComponentProp
65
60
  return;
66
61
  }
67
62
 
68
- if (!osMapsApiKey) {
69
- console.warn('OS Maps API key not provided. Geocoder will not be available.');
70
- // Continue with map initialization but skip geocoder
71
- }
72
-
73
63
  // Initialise map's basemap layers.
74
64
  const layers = initializeBasemapLayers(basePath);
75
65
 
@@ -160,10 +150,10 @@ const MapComponentBase = ({ osMapsApiKey, basePath, children }: MapComponentProp
160
150
  };
161
151
 
162
152
  return (
163
- <div className="flex flex-grow min-h-0">
153
+ <div className="flex grow min-h-0">
164
154
  <div
165
155
  ref={mapRef}
166
- className="flex flex-grow relative z-10"
156
+ className="flex grow relative z-10"
167
157
  id="map"
168
158
  role="application"
169
159
  aria-label="Map"
@@ -178,7 +168,7 @@ const MapComponentBase = ({ osMapsApiKey, basePath, children }: MapComponentProp
178
168
  <Popup
179
169
  onClose={closePopup}
180
170
  clickedCoord={popupCoordinate}
181
- arrowClasses={arrowStyles[popupPositionClass]}
171
+ direction={popupPositionClass}
182
172
  >
183
173
  {children}
184
174
  </Popup>
@@ -1,97 +1,52 @@
1
1
  'use client';
2
2
 
3
- import {
4
- useCallback,
5
- useEffect,
6
- useMemo,
7
- useRef,
8
- useState,
9
- type ReactNode,
10
- type RefObject,
11
- } from 'react';
3
+ import { useCallback, useEffect, useMemo, useState, type RefObject } from 'react';
12
4
 
13
5
  import BaseLayer from 'ol/layer/Base';
14
- import { LuLayers, LuList, LuMaximize2, LuMinus, LuMinimize2, LuPlus } from 'react-icons/lu';
6
+ import { LuMaximize2, LuMinimize2, LuMinus, LuPlus } from 'react-icons/lu';
15
7
 
16
- import { LayerSwitcherPanel } from './LayerSwitcherPanel';
17
- import { LegendPanel } from './LegendPanel';
18
8
  import { useMap } from './MapContext';
19
-
20
- type MapControlsOverlayProps = {
9
+ import { Button } from '../components/Button/Button';
10
+ import type { LayerOption } from '../components/LayerSwitcher/LayerSwitcher';
11
+ import { LayerSwitcher } from '../components/LayerSwitcher/LayerSwitcher';
12
+ import { ButtonGroup } from '../components/Layout/ButtonGroup/ButtonGroup';
13
+ import type { LegendOption } from '../components/Legend/Legend';
14
+ import { Legend } from '../components/Legend/Legend';
15
+
16
+ export type MapControlsOverlayProps = {
21
17
  mapContainerRef: RefObject<HTMLDivElement | null>;
22
- isLegendVisible?: boolean;
23
- isLegendEnabled?: boolean;
24
- legendContent?: ReactNode;
25
- // Whether the map is the currently visible view (e.g. the "Map" tab is
26
- // active). The legend renders via a portal to document.body, so it isn't
27
- // hidden by the normal tab-panel `hidden` mechanism when another tab is
28
- // shown — this closes it explicitly instead.
29
- isActive?: boolean;
30
- };
31
-
32
- type ControlButtonProps = {
33
- ariaLabel: string;
34
- title: string;
35
- icon: ReactNode;
36
- onClick: () => void;
37
- isPressed?: boolean;
38
- isDisabled?: boolean;
39
- hasPopup?: boolean;
40
- isExpanded?: boolean;
41
- buttonRef?: RefObject<HTMLButtonElement | null>;
18
+ legendItems?: LegendOption[];
42
19
  };
43
20
 
44
- const ControlButton = ({
45
- ariaLabel,
46
- title,
47
- icon,
48
- onClick,
49
- isPressed,
50
- isDisabled = false,
51
- hasPopup,
52
- isExpanded,
53
- buttonRef,
54
- }: ControlButtonProps) => (
55
- <button
56
- ref={buttonRef}
57
- type="button"
58
- className="ol-btn"
59
- aria-label={ariaLabel}
60
- title={title}
61
- onClick={onClick}
62
- disabled={isDisabled}
63
- aria-pressed={isPressed}
64
- aria-haspopup={hasPopup ? 'dialog' : undefined}
65
- aria-expanded={isExpanded}
66
- >
67
- <span aria-hidden="true">{icon}</span>
68
- </button>
69
- );
70
-
71
- export const MapControlsOverlay = ({
72
- mapContainerRef,
73
- isLegendVisible = false,
74
- isLegendEnabled = false,
75
- legendContent,
76
- isActive = true,
77
- }: MapControlsOverlayProps) => {
21
+ export const MapControlsOverlay = ({ mapContainerRef, legendItems }: MapControlsOverlayProps) => {
78
22
  const { map, getLayers } = useMap();
79
23
  const [isFullScreen, setIsFullScreen] = useState(false);
80
- const [isLayerSwitcherOpen, setIsLayerSwitcherOpen] = useState(false);
81
- const [layerSwitcherButtonRect, setLayerSwitcherButtonRect] = useState<DOMRect | null>(null);
82
- const layerSwitcherButtonRef = useRef<HTMLButtonElement>(null);
83
- const [isLegendOpen, setIsLegendOpen] = useState(false);
84
- const [legendButtonRect, setLegendButtonRect] = useState<DOMRect | null>(null);
85
- const legendButtonRef = useRef<HTMLButtonElement>(null);
86
24
 
87
25
  const basemapLayers = useMemo(
88
26
  () => (getLayers()?.filter((l: BaseLayer) => l.get('basemap') === true) ?? []) as BaseLayer[],
89
27
  [getLayers],
90
28
  );
91
29
 
92
- const activeLayerName =
93
- (basemapLayers.find((l: BaseLayer) => l.getVisible())?.get('name') as string | undefined) ??
94
- null;
30
+ const layerOptions = useMemo<LayerOption[]>(
31
+ () =>
32
+ basemapLayers.map((l) => ({
33
+ name: l.get('name') as string,
34
+ image: l.get('image') as string | undefined,
35
+ })),
36
+ [basemapLayers],
37
+ );
38
+
39
+ // OL layer visibility is mutated directly (setVisible below), which doesn't
40
+ // trigger a React re-render on its own, so this can't just be read during
41
+ // render like a derived value, it needs to be actual state, synced here
42
+ // once basemapLayers is available and updated directly on selection.
43
+ const [activeLayerName, setActiveLayerName] = useState<string | null>(null);
44
+
45
+ useEffect(() => {
46
+ const visible = basemapLayers.find((l: BaseLayer) => l.getVisible());
47
+
48
+ setActiveLayerName((visible?.get('name') as string | undefined) ?? null);
49
+ }, [basemapLayers]);
95
50
 
96
51
  const handleFullScreen = useCallback(() => {
97
52
  const container = mapContainerRef.current;
@@ -110,40 +65,6 @@ export const MapControlsOverlay = ({
110
65
  }
111
66
  }, [isFullScreen, map, mapContainerRef]);
112
67
 
113
- const handleLayerSwitcherToggle = useCallback(() => {
114
- const rect = layerSwitcherButtonRef.current?.getBoundingClientRect() ?? null;
115
-
116
- setLayerSwitcherButtonRect(rect);
117
- setIsLayerSwitcherOpen((prev) => !prev);
118
- }, []);
119
-
120
- const handleLayerSwitcherClose = useCallback(() => {
121
- setIsLayerSwitcherOpen(false);
122
- layerSwitcherButtonRef.current?.focus();
123
- }, []);
124
-
125
- const handleLegendToggle = useCallback(() => {
126
- const rect = legendButtonRef.current?.getBoundingClientRect() ?? null;
127
-
128
- setLegendButtonRect(rect);
129
- setIsLegendOpen((prev) => !prev);
130
- }, []);
131
-
132
- const handleLegendClose = useCallback(() => {
133
- setIsLegendOpen(false);
134
- legendButtonRef.current?.focus();
135
- }, []);
136
-
137
- // Force-close on leaving the map view rather than remembering the open
138
- // state — the legend describes what's on the map, so re-opening it after
139
- // returning is a deliberate, cheap action rather than something worth
140
- // restoring automatically.
141
- useEffect(() => {
142
- if (!isActive) {
143
- setIsLegendOpen(false);
144
- }
145
- }, [isActive]);
146
-
147
68
  const handleSelectLayer = useCallback(
148
69
  (layerName: string) => {
149
70
  const current = basemapLayers.find((l: BaseLayer) => l.getVisible());
@@ -151,6 +72,7 @@ export const MapControlsOverlay = ({
151
72
 
152
73
  current?.setVisible(false);
153
74
  next?.setVisible(true);
75
+ setActiveLayerName(layerName);
154
76
  },
155
77
  [basemapLayers],
156
78
  );
@@ -181,75 +103,46 @@ export const MapControlsOverlay = ({
181
103
  }
182
104
  }, [map]);
183
105
 
184
- const fullScreenLabel = isFullScreen ? 'Exit full screen' : 'Toggle full screen';
185
- const legendLabel = isLegendOpen ? 'Hide legend' : 'Show legend';
186
-
187
106
  return (
188
- <>
189
- <div className="flex flex-col" role="group" aria-label="Map controls">
190
- <ControlButton
191
- ariaLabel={fullScreenLabel}
192
- title={fullScreenLabel}
193
- icon={isFullScreen ? <LuMinimize2 size={20} /> : <LuMaximize2 size={20} />}
194
- onClick={handleFullScreen}
195
- />
196
-
197
- <ControlButton
198
- buttonRef={layerSwitcherButtonRef}
199
- ariaLabel="Toggle basemap selector"
200
- title="Basemap selector"
201
- icon={<LuLayers size={20} />}
202
- onClick={handleLayerSwitcherToggle}
203
- hasPopup
204
- isExpanded={isLayerSwitcherOpen}
205
- />
206
-
207
- <ControlButton
208
- ariaLabel="Zoom in"
209
- title="Zoom in"
210
- icon={<LuPlus size={20} />}
211
- onClick={handleZoomIn}
212
- />
213
-
214
- <ControlButton
215
- ariaLabel="Zoom out"
216
- title="Zoom out"
217
- icon={<LuMinus size={20} />}
218
- onClick={handleZoomOut}
219
- />
220
-
221
- {isLegendVisible ? (
222
- <ControlButton
223
- buttonRef={legendButtonRef}
224
- ariaLabel={legendLabel}
225
- title={legendLabel}
226
- icon={<LuList size={20} />}
227
- onClick={handleLegendToggle}
228
- hasPopup
229
- isExpanded={isLegendOpen}
230
- isDisabled={!isLegendEnabled}
231
- />
232
- ) : null}
233
- </div>
234
-
235
- <LayerSwitcherPanel
236
- isOpen={isLayerSwitcherOpen}
237
- onClose={handleLayerSwitcherClose}
238
- layers={basemapLayers}
107
+ <ButtonGroup orientation="vertical" compressed role="toolbar" aria-label="Map controls">
108
+ <Button
109
+ variant="icon"
110
+ appearance="panel"
111
+ aria-label={isFullScreen ? 'Exit full screen' : 'Enter full screen'}
112
+ onClick={handleFullScreen}
113
+ >
114
+ {isFullScreen ? <LuMinimize2 aria-hidden /> : <LuMaximize2 aria-hidden />}
115
+ </Button>
116
+
117
+ <LayerSwitcher
118
+ layers={layerOptions}
239
119
  activeLayerName={activeLayerName}
240
120
  onSelectLayer={handleSelectLayer}
241
- buttonRect={layerSwitcherButtonRect}
242
121
  />
243
122
 
244
- {isLegendVisible ? (
245
- <LegendPanel
246
- isOpen={isLegendOpen}
247
- onClose={handleLegendClose}
248
- buttonRect={legendButtonRect}
249
- >
250
- {legendContent}
251
- </LegendPanel>
123
+ <Button
124
+ variant="icon"
125
+ appearance="panel"
126
+ aria-label="Zoom in"
127
+ onClick={handleZoomIn}
128
+ disabled={!map}
129
+ >
130
+ <LuPlus aria-hidden />
131
+ </Button>
132
+
133
+ <Button
134
+ variant="icon"
135
+ appearance="panel"
136
+ aria-label="Zoom out"
137
+ onClick={handleZoomOut}
138
+ disabled={!map}
139
+ >
140
+ <LuMinus aria-hidden />
141
+ </Button>
142
+
143
+ {legendItems !== undefined ? (
144
+ <Legend items={legendItems} disabled={legendItems.length === 0} />
252
145
  ) : null}
253
- </>
146
+ </ButtonGroup>
254
147
  );
255
148
  };
package/src/map/Popup.tsx CHANGED
@@ -1,48 +1,95 @@
1
1
  'use client';
2
2
 
3
- import { type ReactNode } from 'react';
3
+ import type { ReactNode } from 'react';
4
4
 
5
- import { IoMdCloseCircle } from 'react-icons/io';
5
+ import { LuX } from 'react-icons/lu';
6
6
 
7
- type PopupProps = {
7
+ import { Button } from '../components/Button/Button';
8
+ import type { PopupDirection } from '../types/map';
9
+ import { cn } from '../utils/components';
10
+
11
+ // Which corner of the popup box points back at the clicked coordinate,
12
+ // matching MapComponent's own positionTransforms lookup (which decides
13
+ // where the box itself sits) so the two stay in sync.
14
+ const POPUP_ARROW_POSITIONS = {
15
+ 'bottom-left': 'right-[-.5em] top-[1em]',
16
+ 'bottom-right': 'left-[-.5em] top-[1em]',
17
+ 'top-left': 'right-[-.5em] bottom-[1em]',
18
+ 'top-right': 'left-[-.5em] bottom-[1em]',
19
+ } as const;
20
+
21
+ // The arrow only ever pokes out the left or right edge (see the offsets
22
+ // above), never top/bottom, so it only ever needs to point left or right.
23
+ // Same rotation math @radix-ui/react-popper uses for its own Arrow (see
24
+ // PopperArrow's per-side transform): the un-rotated shape points down,
25
+ // rotate(90deg) makes it point left, rotate(-90deg) makes it point right.
26
+ const POPUP_ARROW_ROTATIONS = {
27
+ 'bottom-left': '-rotate-90',
28
+ 'bottom-right': 'rotate-90',
29
+ 'top-left': '-rotate-90',
30
+ 'top-right': 'rotate-90',
31
+ } as const;
32
+
33
+ export type PopupProps = {
8
34
  children: ReactNode;
9
35
  onClose: () => void;
10
36
  clickedCoord: number[] | null;
11
- arrowClasses: string;
37
+ direction?: PopupDirection;
12
38
  };
13
39
 
40
+ // Not built on Modal: it's a non-modal panel pinned to a map coordinate
41
+ // (with a directional arrow pointing back at it) rather than a centred,
42
+ // backdrop-blocking dialog, but it borrows Modal's visual language (panel,
43
+ // no rounded corners, same close button treatment) so the two feel related.
44
+ // One padded box holding everything (close button and content both), same
45
+ // as ModalContent, rather than nesting a second, separately-padded box
46
+ // inside it, so the two actually share the exact same padding math.
14
47
  export const Popup = ({
15
48
  children,
16
49
  onClose,
17
50
  clickedCoord,
18
- arrowClasses = 'bottom-left',
51
+ direction = 'bottom-left',
19
52
  }: PopupProps) => {
20
53
  if (!clickedCoord) {
21
- return <></>;
54
+ return null;
22
55
  }
23
56
 
24
57
  return (
25
- <div className={'relative z-50 w-[300px] shadow-md'}>
26
- <button
27
- type="button"
28
- onClick={onClose}
29
- className="absolute z-4 top-[.5em] right-[1em] text-gray-500 hover:text-black text-sm"
30
- aria-label="Close popup"
31
- >
32
- <IoMdCloseCircle size={20} />
33
- </button>
58
+ <div
59
+ role="dialog"
60
+ aria-label="Feature details"
61
+ className="cn-z-top panel relative w-75 p-gds-4"
62
+ >
63
+ <div className="absolute top-gds-2 right-gds-2">
64
+ <Button variant="icon" appearance="ghost" onClick={onClose}>
65
+ <LuX />
34
66
 
35
- <div
36
- className="space-y-2 pt-4 pb-1 overflow-y-auto max-h-[300px] bg-white border border-border
37
- rounded-lg divide-y divide-gray-300"
38
- >
39
- {children}
67
+ <span className="sr-only">Close popup</span>
68
+ </Button>
40
69
  </div>
41
70
 
42
- <div
43
- className={`absolute z-[-1] w-4 h-4 bg-white border border-gray-300 rotate-45
44
- ${arrowClasses}`}
45
- />
71
+ <div className="flex max-h-75 flex-col gap-gds-2 overflow-y-auto">{children}</div>
72
+
73
+ {/* Same shape/proportions as PopoverContent/TooltipContent's own
74
+ Radix Arrow (viewBox 0 0 30 10, width 10, height 5), reimplemented
75
+ rather than reused directly: Radix's Arrow depends on its Popper
76
+ positioning context (floating-ui), which only exists inside a real
77
+ Popover.Content/Tooltip.Content, not this OpenLayers-positioned
78
+ overlay. */}
79
+ <svg
80
+ aria-hidden
81
+ width={10}
82
+ height={5}
83
+ viewBox="0 0 30 10"
84
+ preserveAspectRatio="none"
85
+ className={cn(
86
+ 'fill-panel-border absolute -z-10',
87
+ POPUP_ARROW_POSITIONS[direction],
88
+ POPUP_ARROW_ROTATIONS[direction],
89
+ )}
90
+ >
91
+ <polygon points="0,0 30,0 15,10" />
92
+ </svg>
46
93
  </div>
47
94
  );
48
95
  };