@tpzdsp/next-toolkit 2.5.0 → 3.0.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 (355) hide show
  1. package/package.json +22 -13
  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 +32 -0
  62. package/src/components/ErrorModal/ErrorModal.stories.tsx +101 -0
  63. package/src/components/ErrorModal/ErrorModal.tsx +120 -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 +4 -2
  77. package/src/components/Footer/Footer.stories.tsx +24 -0
  78. package/src/components/Footer/Footer.tsx +41 -0
  79. package/src/components/Footer/Licence.tsx +18 -0
  80. package/src/components/{layout/footer → Footer}/MetaLinks.tsx +8 -3
  81. package/src/components/Footer/Small/SmallFooter.tsx +43 -42
  82. package/src/components/Form/FormGroup.css +18 -0
  83. package/src/components/Form/FormGroup.stories.tsx +87 -0
  84. package/src/components/Form/FormGroup.tsx +16 -0
  85. package/src/components/{layout/header → Header}/Header.stories.tsx +26 -12
  86. package/src/components/Header/Header.test.tsx +87 -0
  87. package/src/components/Header/Header.tsx +303 -0
  88. package/src/components/Header/HeaderAppContent.tsx +120 -0
  89. package/src/components/Heading/Heading.css +40 -0
  90. package/src/components/Heading/Heading.stories.tsx +15 -5
  91. package/src/components/Heading/Heading.tsx +21 -44
  92. package/src/components/Heading/HeadingSkeleton.stories.tsx +30 -0
  93. package/src/components/Heading/HeadingSkeleton.tsx +14 -0
  94. package/src/components/Hero/Hero.css +19 -0
  95. package/src/components/Hero/Hero.stories.tsx +41 -0
  96. package/src/components/Hero/Hero.tsx +14 -0
  97. package/src/components/HintText/HintText.css +5 -0
  98. package/src/components/HintText/HintText.stories.tsx +29 -0
  99. package/src/components/HintText/HintText.tsx +13 -0
  100. package/src/components/InfoBox/InfoBox.css +38 -0
  101. package/src/components/InfoBox/InfoBox.stories.tsx +51 -403
  102. package/src/components/InfoBox/InfoBox.tsx +31 -158
  103. package/src/components/Input/Input.css +77 -0
  104. package/src/components/Input/Input.stories.tsx +123 -0
  105. package/src/components/Input/Input.tsx +74 -0
  106. package/src/components/InputGroup/InputGroup.css +140 -0
  107. package/src/components/InputGroup/InputGroup.stories.tsx +125 -0
  108. package/src/components/InputGroup/InputGroup.tsx +47 -0
  109. package/src/components/InsetText/InsetText.css +5 -0
  110. package/src/components/InsetText/InsetText.stories.tsx +30 -0
  111. package/src/components/InsetText/InsetText.tsx +9 -0
  112. package/src/components/Keyboard/Keyboard.css +9 -0
  113. package/src/components/Keyboard/Keyboard.stories.tsx +63 -0
  114. package/src/components/Keyboard/Keyboard.tsx +30 -0
  115. package/src/components/Label/Label.css +27 -0
  116. package/src/components/Label/Label.stories.tsx +40 -0
  117. package/src/components/Label/Label.tsx +17 -0
  118. package/src/components/LayerSwitcher/LayerSwitcher.css +59 -0
  119. package/src/components/LayerSwitcher/LayerSwitcher.stories.tsx +112 -0
  120. package/src/components/LayerSwitcher/LayerSwitcher.tsx +61 -0
  121. package/src/components/Layout/401/401.stories.tsx +23 -0
  122. package/src/components/Layout/401/401.tsx +1 -0
  123. package/src/components/Layout/403/403.stories.tsx +23 -0
  124. package/src/components/Layout/403/403.tsx +21 -0
  125. package/src/components/Layout/404/404.stories.tsx +23 -0
  126. package/src/components/Layout/404/404.tsx +28 -0
  127. package/src/components/Layout/500/500.stories.tsx +56 -0
  128. package/src/components/Layout/500/500.tsx +63 -0
  129. package/src/components/Layout/ButtonGroup/ButtonGroup.css +52 -0
  130. package/src/components/Layout/ButtonGroup/ButtonGroup.stories.tsx +156 -0
  131. package/src/components/Layout/ButtonGroup/ButtonGroup.tsx +50 -0
  132. package/src/components/Layout/HeadingGroup/HeadingGroup.css +21 -0
  133. package/src/components/Layout/HeadingGroup/HeadingGroup.stories.tsx +84 -0
  134. package/src/components/Layout/HeadingGroup/HeadingGroup.tsx +9 -0
  135. package/src/components/Layout/MainWrapper/MainWrapper.css +12 -0
  136. package/src/components/Layout/MainWrapper/MainWrapper.stories.tsx +36 -0
  137. package/src/components/Layout/MainWrapper/MainWrapper.tsx +14 -0
  138. package/src/components/Layout/PageShell/PageShell.css +42 -0
  139. package/src/components/Layout/PageShell/PageShell.stories.tsx +108 -0
  140. package/src/components/Layout/PageShell/PageShell.tsx +31 -0
  141. package/src/components/Layout/ParagraphGroup/ParagraphGroup.css +5 -0
  142. package/src/components/Layout/ParagraphGroup/ParagraphGroup.stories.tsx +34 -0
  143. package/src/components/Layout/ParagraphGroup/ParagraphGroup.tsx +9 -0
  144. package/src/components/Layout/Stack/Stack.css +29 -0
  145. package/src/components/Layout/Stack/Stack.stories.tsx +83 -0
  146. package/src/components/Layout/Stack/Stack.tsx +19 -0
  147. package/src/components/Layout/WidthContainer/WidthContainer.css +17 -0
  148. package/src/components/Layout/WidthContainer/WidthContainer.stories.tsx +59 -0
  149. package/src/components/Layout/WidthContainer/WidthContainer.tsx +13 -0
  150. package/src/components/Legend/Legend.css +95 -0
  151. package/src/components/Legend/Legend.stories.tsx +81 -0
  152. package/src/components/Legend/Legend.tsx +32 -0
  153. package/src/components/Legend/LegendDot.tsx +15 -0
  154. package/src/components/Legend/LegendPanel.stories.tsx +101 -0
  155. package/src/components/Legend/LegendPanel.tsx +68 -0
  156. package/src/components/Legend/LegendSkeleton.stories.tsx +87 -0
  157. package/src/components/Legend/LegendSkeleton.tsx +73 -0
  158. package/src/components/LegendSelect/LegendSelect.css +8 -0
  159. package/src/components/LegendSelect/LegendSelect.stories.tsx +80 -0
  160. package/src/components/LegendSelect/LegendSelect.tsx +26 -0
  161. package/src/components/Link/Link.css +98 -0
  162. package/src/components/Link/Link.stories.tsx +70 -0
  163. package/src/components/Link/Link.tsx +52 -0
  164. package/src/components/LinkButton/LinkButton.css +67 -0
  165. package/src/components/LinkButton/LinkButton.stories.tsx +90 -42
  166. package/src/components/LinkButton/LinkButton.tsx +50 -63
  167. package/src/components/LinkList/LinkList.css +28 -0
  168. package/src/components/LinkList/LinkList.stories.tsx +46 -0
  169. package/src/components/LinkList/LinkList.tsx +23 -0
  170. package/src/components/List/Li.tsx +9 -0
  171. package/src/components/List/List.css +9 -0
  172. package/src/components/List/OrderedList.stories.tsx +36 -0
  173. package/src/components/List/OrderedList.tsx +9 -0
  174. package/src/components/List/UnorderedList.stories.tsx +36 -0
  175. package/src/components/List/UnorderedList.tsx +9 -0
  176. package/src/components/LoadingBox/LoadingBox.css +27 -0
  177. package/src/components/LoadingBox/LoadingBox.stories.tsx +66 -0
  178. package/src/components/LoadingBox/LoadingBox.tsx +40 -0
  179. package/src/components/Modal/Modal.css +114 -0
  180. package/src/components/Modal/Modal.stories.tsx +41 -160
  181. package/src/components/Modal/Modal.tsx +132 -75
  182. package/src/components/NoScriptBanner/NoScriptBanner.css +17 -0
  183. package/src/components/NoScriptBanner/NoScriptBanner.stories.tsx +46 -0
  184. package/src/components/NoScriptBanner/NoScriptBanner.tsx +26 -0
  185. package/src/components/Paragraph/Paragraph.css +22 -0
  186. package/src/components/Paragraph/Paragraph.stories.tsx +1 -42
  187. package/src/components/Paragraph/Paragraph.tsx +4 -3
  188. package/src/components/Paragraph/ParagraphSkeleton.stories.tsx +44 -0
  189. package/src/components/Paragraph/ParagraphSkeleton.tsx +33 -0
  190. package/src/components/PasswordInput/PasswordInput.css +3 -0
  191. package/src/components/PasswordInput/PasswordInput.stories.tsx +47 -0
  192. package/src/components/PasswordInput/PasswordInput.tsx +45 -0
  193. package/src/components/PhaseBanner/PhaseBanner.css +12 -0
  194. package/src/components/PhaseBanner/PhaseBanner.stories.tsx +61 -0
  195. package/src/components/PhaseBanner/PhaseBanner.tsx +17 -0
  196. package/src/components/Popover/Popover.css +43 -0
  197. package/src/components/Popover/Popover.stories.tsx +102 -0
  198. package/src/components/Popover/Popover.tsx +40 -0
  199. package/src/components/Radio/Radio.css +69 -0
  200. package/src/components/Radio/Radio.stories.tsx +169 -0
  201. package/src/components/Radio/Radio.tsx +56 -0
  202. package/src/components/Resizable/Resizable.css +86 -0
  203. package/src/components/Resizable/Resizable.stories.tsx +123 -0
  204. package/src/components/Resizable/Resizable.tsx +86 -0
  205. package/src/components/ScrollArea/ScrollArea.css +9 -0
  206. package/src/components/ScrollArea/ScrollArea.stories.tsx +42 -0
  207. package/src/components/ScrollArea/ScrollArea.tsx +11 -0
  208. package/src/components/SearchBox/SearchBox.stories.tsx +88 -0
  209. package/src/components/SearchBox/SearchBox.tsx +36 -0
  210. package/src/components/Select/AsyncSelect.stories.tsx +84 -0
  211. package/src/components/Select/AsyncSelect.tsx +41 -0
  212. package/src/components/Select/Select.css +194 -0
  213. package/src/components/Select/Select.stories.tsx +162 -0
  214. package/src/components/Select/Select.test.tsx +36 -0
  215. package/src/components/Select/Select.tsx +291 -0
  216. package/src/components/Select/SelectSkeleton.stories.tsx +51 -0
  217. package/src/components/Select/SelectSkeleton.tsx +23 -0
  218. package/src/components/Separator/Separator.css +41 -0
  219. package/src/components/Separator/Separator.stories.tsx +63 -0
  220. package/src/components/Separator/Separator.tsx +42 -0
  221. package/src/components/Sidebar/ResizableSidebar.tsx +118 -0
  222. package/src/components/Sidebar/Sidebar.css +203 -0
  223. package/src/components/Sidebar/Sidebar.stories.tsx +438 -0
  224. package/src/components/Sidebar/Sidebar.tsx +225 -0
  225. package/src/components/Skeleton/Skeleton.css +16 -0
  226. package/src/components/Skeleton/Skeleton.stories.tsx +39 -0
  227. package/src/components/Skeleton/Skeleton.tsx +12 -0
  228. package/src/components/SkipLink/SkipLink.css +16 -0
  229. package/src/components/SkipLink/SkipLink.stories.tsx +40 -0
  230. package/src/components/SkipLink/SkipLink.tsx +15 -0
  231. package/src/components/Spinner/Spinner.css +30 -0
  232. package/src/components/Spinner/Spinner.stories.tsx +36 -0
  233. package/src/components/Spinner/Spinner.tsx +45 -0
  234. package/src/components/StartButton/StartButton.css +13 -0
  235. package/src/components/StartButton/StartButton.stories.tsx +68 -0
  236. package/src/components/StartButton/StartButton.tsx +50 -0
  237. package/src/components/SubmitButton/SubmitButton.stories.tsx +31 -0
  238. package/src/components/SubmitButton/SubmitButton.tsx +14 -0
  239. package/src/components/SummaryList/SummaryList.css +71 -0
  240. package/src/components/SummaryList/SummaryList.stories.tsx +211 -0
  241. package/src/components/SummaryList/SummaryList.tsx +41 -0
  242. package/src/components/SummaryList/SummaryListSkeleton.stories.tsx +67 -0
  243. package/src/components/SummaryList/SummaryListSkeleton.tsx +40 -0
  244. package/src/components/Tabs/Tabs.css +84 -0
  245. package/src/components/Tabs/Tabs.stories.tsx +133 -0
  246. package/src/components/Tabs/Tabs.tsx +66 -0
  247. package/src/components/Tag/Tag.css +8 -0
  248. package/src/components/Tag/Tag.stories.tsx +96 -0
  249. package/src/components/Tag/Tag.tsx +34 -0
  250. package/src/components/Textarea/Textarea.css +38 -0
  251. package/src/components/Textarea/Textarea.stories.tsx +131 -0
  252. package/src/components/Textarea/Textarea.tsx +50 -0
  253. package/src/components/TextareaCount/TextareaCount.css +17 -0
  254. package/src/components/TextareaCount/TextareaCount.stories.tsx +131 -0
  255. package/src/components/TextareaCount/TextareaCount.tsx +108 -0
  256. package/src/components/Tooltip/Tooltip.css +43 -0
  257. package/src/components/Tooltip/Tooltip.stories.tsx +110 -0
  258. package/src/components/Tooltip/Tooltip.tsx +53 -0
  259. package/src/components/WarningText/WarningText.css +20 -0
  260. package/src/components/WarningText/WarningText.stories.tsx +29 -0
  261. package/src/components/WarningText/WarningText.tsx +17 -0
  262. package/src/components/index.ts +342 -61
  263. package/src/errors/errorDisplay.ts +70 -0
  264. package/src/errors/index.ts +1 -0
  265. package/src/http/constants.ts +1 -0
  266. package/src/http/fetch.ts +7 -3
  267. package/src/map/LayerSwitcherPanel.tsx +2 -173
  268. package/src/map/LegendShim.tsx +2 -0
  269. package/src/map/MapComponent.tsx +7 -17
  270. package/src/map/MapControlsOverlay.tsx +68 -175
  271. package/src/map/Popup.tsx +71 -24
  272. package/src/map/geocoder/Geocoder.test.tsx +46 -3
  273. package/src/map/geocoder/Geocoder.tsx +155 -278
  274. package/src/map/index.ts +1 -1
  275. package/src/types/utils.ts +0 -10
  276. package/src/utils/components.ts +96 -0
  277. package/src/utils/index.ts +1 -0
  278. package/src/utils/utils.ts +0 -12
  279. package/src/assets/images/defra-logo.svg +0 -51
  280. package/src/assets/images/ea-logo.svg +0 -58
  281. package/src/assets/images/ogl.svg +0 -1
  282. package/src/components/Button/Button.test.tsx +0 -53
  283. package/src/components/ButtonLink/ButtonLink.test.tsx +0 -154
  284. package/src/components/Card/Card.test.tsx +0 -51
  285. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +0 -75
  286. package/src/components/ErrorBoundary/ErrorFallback.test.tsx +0 -107
  287. package/src/components/ErrorText/ErrorText.test.tsx +0 -33
  288. package/src/components/Heading/Heading.test.tsx +0 -22
  289. package/src/components/Hint/Hint.stories.tsx +0 -58
  290. package/src/components/Hint/Hint.test.tsx +0 -33
  291. package/src/components/Hint/Hint.tsx +0 -12
  292. package/src/components/InfoBox/InfoBox.test.tsx +0 -330
  293. package/src/components/InfoBox/types.ts +0 -6
  294. package/src/components/LinkButton/LinkButton.test.tsx +0 -177
  295. package/src/components/Modal/Modal.test.tsx +0 -246
  296. package/src/components/NotificationBanner/NotificationBanner.stories.tsx +0 -45
  297. package/src/components/NotificationBanner/NotificationBanner.test.tsx +0 -58
  298. package/src/components/NotificationBanner/NotificationBanner.tsx +0 -45
  299. package/src/components/Paragraph/Paragraph.test.tsx +0 -10
  300. package/src/components/SlidingPanel/SlidingPanel.stories.tsx +0 -129
  301. package/src/components/SlidingPanel/SlidingPanel.test.tsx +0 -83
  302. package/src/components/SlidingPanel/SlidingPanel.tsx +0 -258
  303. package/src/components/accordion/Accordion.stories.tsx +0 -234
  304. package/src/components/accordion/Accordion.test.tsx +0 -192
  305. package/src/components/accordion/Accordion.tsx +0 -73
  306. package/src/components/backToTop/BackToTop.stories.tsx +0 -407
  307. package/src/components/backToTop/BackToTop.test.tsx +0 -57
  308. package/src/components/backToTop/BackToTop.tsx +0 -134
  309. package/src/components/chip/Chip.stories.tsx +0 -61
  310. package/src/components/chip/Chip.test.tsx +0 -31
  311. package/src/components/chip/Chip.tsx +0 -23
  312. package/src/components/container/Container.tsx +0 -42
  313. package/src/components/cookieBanner/CookieBanner.stories.tsx +0 -257
  314. package/src/components/cookieBanner/CookieBanner.test.tsx +0 -68
  315. package/src/components/cookieBanner/CookieBanner.tsx +0 -73
  316. package/src/components/divider/RuleDivider.stories.tsx +0 -254
  317. package/src/components/divider/RuleDivider.test.tsx +0 -164
  318. package/src/components/divider/RuleDivider.tsx +0 -23
  319. package/src/components/dropdown/DropdownMenu.test.tsx +0 -211
  320. package/src/components/dropdown/DropdownMenu.tsx +0 -108
  321. package/src/components/dropdown/useDropdownMenu.ts +0 -249
  322. package/src/components/form/Input.stories.tsx +0 -435
  323. package/src/components/form/Input.test.tsx +0 -206
  324. package/src/components/form/Input.tsx +0 -23
  325. package/src/components/form/TextArea.stories.tsx +0 -464
  326. package/src/components/form/TextArea.test.tsx +0 -232
  327. package/src/components/form/TextArea.tsx +0 -23
  328. package/src/components/layout/footer/Footer.tsx +0 -26
  329. package/src/components/layout/footer/Licence.tsx +0 -19
  330. package/src/components/layout/header/Header.test.tsx +0 -36
  331. package/src/components/layout/header/Header.tsx +0 -90
  332. package/src/components/layout/header/HeaderAuthClient.test.tsx +0 -45
  333. package/src/components/layout/header/HeaderAuthClient.tsx +0 -40
  334. package/src/components/layout/header/HeaderNavClient.test.tsx +0 -44
  335. package/src/components/layout/header/HeaderNavClient.tsx +0 -67
  336. package/src/components/link/ExternalLink.test.tsx +0 -102
  337. package/src/components/link/ExternalLink.tsx +0 -29
  338. package/src/components/link/Link.tsx +0 -26
  339. package/src/components/select/Select.stories.tsx +0 -336
  340. package/src/components/select/Select.test.tsx +0 -474
  341. package/src/components/select/Select.tsx +0 -125
  342. package/src/components/select/SelectSkeleton.stories.tsx +0 -194
  343. package/src/components/select/SelectSkeleton.test.tsx +0 -104
  344. package/src/components/select/SelectSkeleton.tsx +0 -23
  345. package/src/components/select/common.ts +0 -3
  346. package/src/components/select/index.ts +0 -18
  347. package/src/components/skipLink/SkipLink.stories.tsx +0 -346
  348. package/src/components/skipLink/SkipLink.test.tsx +0 -22
  349. package/src/components/skipLink/SkipLink.tsx +0 -49
  350. package/src/map/LegendPanel.tsx +0 -85
  351. /package/src/{components → assets}/images/DefraLogo.tsx +0 -0
  352. /package/src/{components → assets}/images/EaLogo.tsx +0 -0
  353. /package/src/{components → assets}/images/OglLogo.tsx +0 -0
  354. /package/src/components/{googleAnalytics → GoogleAnalytics}/GlobalVars.tsx +0 -0
  355. /package/src/components/{googleAnalytics → GoogleAnalytics}/GoogleAnalytics.tsx +0 -0
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import type { ComponentProps } from 'react';
4
+
5
+ import { Collapsible as CollapsiblePrimitive } from 'radix-ui';
6
+ import { LuChevronRight } from 'react-icons/lu';
7
+
8
+ import { cn } from '../../utils';
9
+
10
+ export type DetailsProps = ComponentProps<typeof CollapsiblePrimitive.Root>;
11
+
12
+ export const Details = ({ className, ...props }: DetailsProps) => (
13
+ <CollapsiblePrimitive.Root
14
+ data-slot="details"
15
+ className={cn('cn-details', className)}
16
+ {...props}
17
+ />
18
+ );
19
+
20
+ export type DetailsTriggerProps = ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>;
21
+
22
+ export const DetailsTrigger = ({ className, children, ...props }: DetailsTriggerProps) => (
23
+ <CollapsiblePrimitive.CollapsibleTrigger
24
+ data-slot="details-trigger"
25
+ className={cn('cn-details-trigger', className)}
26
+ {...props}
27
+ >
28
+ <LuChevronRight aria-hidden className="cn-details-trigger-icon" />
29
+ {children}
30
+ </CollapsiblePrimitive.CollapsibleTrigger>
31
+ );
32
+
33
+ export type DetailsContentProps = ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>;
34
+
35
+ export const DetailsContent = ({ className, ...props }: DetailsContentProps) => (
36
+ <CollapsiblePrimitive.CollapsibleContent
37
+ data-slot="details-content"
38
+ className={cn('cn-details-content', className)}
39
+ {...props}
40
+ />
41
+ );
@@ -0,0 +1,81 @@
1
+ @layer components {
2
+ /* MARK: Content
3
+ Same panel treatment as Popover/Select's own menu (bg-panel + border),
4
+ portal-rendered so DOM order is always safe above page content, but
5
+ cn-z-overlay still matters against other elements with their own high
6
+ z-index (e.g. a resizable handle's cn-z-top). */
7
+ .cn-dropdown-menu-content {
8
+ @apply panel cn-z-overlay min-w-40 overflow-hidden;
9
+
10
+ &[data-state='open'] {
11
+ animation: cn-dropdown-menu-in 0.1s ease-out;
12
+ }
13
+
14
+ &[data-state='closed'] {
15
+ animation: cn-dropdown-menu-out 0.075s ease-in;
16
+ }
17
+ }
18
+
19
+ @keyframes cn-dropdown-menu-in {
20
+ from {
21
+ opacity: 0;
22
+ transform: scale(0.97);
23
+ }
24
+ to {
25
+ opacity: 1;
26
+ transform: scale(1);
27
+ }
28
+ }
29
+
30
+ @keyframes cn-dropdown-menu-out {
31
+ from {
32
+ opacity: 1;
33
+ }
34
+ to {
35
+ opacity: 0;
36
+ }
37
+ }
38
+
39
+ /* MARK: Item
40
+ data-highlighted is Radix's own state for whichever item is currently
41
+ hovered or keyboard-focused (it also moves real DOM focus there when
42
+ keyboard-navigated, which is what lets a real Link's own :focus/:hover
43
+ apply at all, asChild renders this class directly onto whatever's
44
+ passed as a child, e.g. HeaderAppContentNavLink, there's no wrapper,
45
+ so a Link IS this element). Every item gets the same washed background
46
+ on highlight regardless of content, same as cn-select-option-focused,
47
+ so the menu reads consistently row to row.
48
+
49
+ border-none/shadow-none on top of that specifically neutralises a
50
+ Link's own :focus box-shadow (the GDS yellow-bar treatment), which
51
+ would otherwise show alongside the washed background rather than the
52
+ two picking one look. A highlighted link still reads as a link
53
+ though: :is(a) applies cn-link-hover-decoration, the same
54
+ thicker-underline Link.css itself uses on :hover, layered on top of
55
+ the shared background rather than replacing it. */
56
+ .cn-dropdown-menu-item {
57
+ @apply block w-full cursor-default px-gds-2 text-left text-base py-gds-1;
58
+
59
+ &:hover,
60
+ &:focus {
61
+ @apply outline-none;
62
+ }
63
+
64
+ &[data-highlighted] {
65
+ @apply bg-wash border-none shadow-none;
66
+ }
67
+
68
+ &[data-highlighted]:is(a) {
69
+ @apply cn-link-hover-decoration;
70
+ }
71
+
72
+ &[data-disabled] {
73
+ @apply cursor-not-allowed opacity-50;
74
+ }
75
+ }
76
+
77
+ /* MARK: Separator */
78
+ .cn-dropdown-menu-separator {
79
+ @apply my-gds-1 h-px bg-divider;
80
+ }
81
+ }
@@ -0,0 +1,131 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import {
4
+ DropdownMenu,
5
+ DropdownMenuContent,
6
+ DropdownMenuItem,
7
+ DropdownMenuSeparator,
8
+ DropdownMenuTrigger,
9
+ } from './Dropdown';
10
+ import { Button } from '../Button/Button';
11
+ import { ExternalLink } from '../ExternalLink/ExternalLink';
12
+ import { Link } from '../Link/Link';
13
+
14
+ const meta: Meta<typeof DropdownMenu> = {
15
+ title: 'Components/Dropdown',
16
+ component: DropdownMenu,
17
+ tags: ['autodocs'],
18
+ parameters: {
19
+ docs: {
20
+ description: {
21
+ component:
22
+ "A floating menu of actions or links anchored to a trigger. Built for a flat list of nav links, not a full command menu, no checkbox/radio items, submenus, or keyboard shortcuts. Use DropdownMenuItem with asChild and a real Link (this library's own, a plain `<a>`, or a framework-aware wrapper like one using asChild internally with next/link). Every item, Link or not, gets the same washed background on highlight; a highlighted Link also keeps its own thicker-underline hover cue on top of that, its focus box-shadow is neutralised so the two don't compete. Whatever component you pass needs to forward extra props through to its own rendered element (via a rest spread), asChild relies on that to merge role=\"menuitem\" and keyboard handling onto it, see HeaderAppContent's own HeaderAppContentNavLink for a real example.",
23
+ },
24
+ },
25
+ },
26
+ };
27
+
28
+ export default meta;
29
+
30
+ type Story = StoryObj<typeof DropdownMenu>;
31
+
32
+ export const Default: Story = {
33
+ parameters: {
34
+ docs: {
35
+ description: {
36
+ story:
37
+ "Using this library's own Link/ExternalLink with asChild, appearance=\"contrast\" for plain body-coloured text on the menu's white background. Highlighting an item (hover or arrow-key nav) shows the same washed background as any other item, plus the link's own thicker-underline hover cue.",
38
+ },
39
+ },
40
+ },
41
+ render: () => (
42
+ <div className="flex min-h-40 items-start justify-center">
43
+ <DropdownMenu>
44
+ <DropdownMenuTrigger asChild>
45
+ <Button appearance="secondary">Account</Button>
46
+ </DropdownMenuTrigger>
47
+
48
+ <DropdownMenuContent>
49
+ <DropdownMenuItem asChild>
50
+ <ExternalLink variant="icon" href="#profile" appearance="contrast">
51
+ Profile
52
+ </ExternalLink>
53
+ </DropdownMenuItem>
54
+
55
+ <DropdownMenuItem asChild>
56
+ <ExternalLink variant="icon" href="#settings" appearance="contrast">
57
+ Settings
58
+ </ExternalLink>
59
+ </DropdownMenuItem>
60
+
61
+ <DropdownMenuItem asChild>
62
+ <Link href="#sign-out" appearance="contrast">
63
+ Sign out
64
+ </Link>
65
+ </DropdownMenuItem>
66
+ </DropdownMenuContent>
67
+ </DropdownMenu>
68
+ </div>
69
+ ),
70
+ };
71
+
72
+ export const WithSeparator: Story = {
73
+ parameters: {
74
+ docs: {
75
+ description: {
76
+ story: 'DropdownMenuSeparator splits related links from an unrelated one, e.g. sign out.',
77
+ },
78
+ },
79
+ },
80
+ render: () => (
81
+ <div className="flex min-h-40 items-start justify-center">
82
+ <DropdownMenu>
83
+ <DropdownMenuTrigger asChild>
84
+ <Button appearance="secondary">Account</Button>
85
+ </DropdownMenuTrigger>
86
+
87
+ <DropdownMenuContent>
88
+ <DropdownMenuItem asChild>
89
+ <ExternalLink variant="icon" href="#profile" appearance="contrast">
90
+ Profile
91
+ </ExternalLink>
92
+ </DropdownMenuItem>
93
+
94
+ <DropdownMenuItem asChild>
95
+ <ExternalLink variant="icon" href="#settings" appearance="contrast">
96
+ Settings
97
+ </ExternalLink>
98
+ </DropdownMenuItem>
99
+
100
+ <DropdownMenuSeparator />
101
+
102
+ <DropdownMenuItem asChild>
103
+ <Link href="#sign-out" appearance="contrast">
104
+ Sign out
105
+ </Link>
106
+ </DropdownMenuItem>
107
+ </DropdownMenuContent>
108
+ </DropdownMenu>
109
+ </div>
110
+ ),
111
+ };
112
+
113
+ export const WithDisabledItem: Story = {
114
+ render: () => (
115
+ <div className="flex min-h-40 items-start justify-center">
116
+ <DropdownMenu>
117
+ <DropdownMenuTrigger asChild>
118
+ <Button appearance="secondary">Sort</Button>
119
+ </DropdownMenuTrigger>
120
+
121
+ <DropdownMenuContent>
122
+ <DropdownMenuItem>Date</DropdownMenuItem>
123
+
124
+ <DropdownMenuItem disabled>Name (unavailable)</DropdownMenuItem>
125
+
126
+ <DropdownMenuItem>Type</DropdownMenuItem>
127
+ </DropdownMenuContent>
128
+ </DropdownMenu>
129
+ </div>
130
+ ),
131
+ };
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ import type { ComponentProps } from 'react';
4
+
5
+ import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
6
+
7
+ import { cn } from '../../utils/components';
8
+
9
+ export const DropdownMenu = ({ ...props }: ComponentProps<typeof DropdownMenuPrimitive.Root>) => (
10
+ <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
11
+ );
12
+
13
+ export const DropdownMenuTrigger = ({
14
+ ...props
15
+ }: ComponentProps<typeof DropdownMenuPrimitive.Trigger>) => (
16
+ <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
17
+ );
18
+
19
+ export const DropdownMenuContent = ({
20
+ className,
21
+ align = 'start',
22
+ sideOffset = 6,
23
+ ...props
24
+ }: ComponentProps<typeof DropdownMenuPrimitive.Content>) => (
25
+ <DropdownMenuPrimitive.Portal>
26
+ <DropdownMenuPrimitive.Content
27
+ data-slot="dropdown-menu-content"
28
+ align={align}
29
+ sideOffset={sideOffset}
30
+ className={cn('cn-dropdown-menu-content', className)}
31
+ {...props}
32
+ />
33
+ </DropdownMenuPrimitive.Portal>
34
+ );
35
+
36
+ // asChild + a real Link for nav links (this library's own, or a
37
+ // framework-aware wrapper e.g. one using asChild internally with next/link).
38
+ // cn-dropdown-menu-item gives every item (Link or not) the same washed
39
+ // background on highlight, same as Select's own menu, rather than leaving
40
+ // a Link's own :focus box-shadow to fight that for the same row (which it
41
+ // specifically neutralises), a highlighted Link still gets its own cue on
42
+ // top, the same thicker-underline Link.css itself uses on :hover, see
43
+ // Dropdown.css for the exact rules. Whatever's passed here needs to
44
+ // forward arbitrary extra props through to its own rendered element
45
+ // (asChild relies on that to merge role="menuitem", the roving-tabindex
46
+ // keyboard handlers, and this className onto it), a component that only
47
+ // destructures its own known props without a rest spread will silently
48
+ // drop all of that.
49
+ export const DropdownMenuItem = ({
50
+ className,
51
+ ...props
52
+ }: ComponentProps<typeof DropdownMenuPrimitive.Item>) => (
53
+ <DropdownMenuPrimitive.Item
54
+ data-slot="dropdown-menu-item"
55
+ className={cn('cn-dropdown-menu-item', className)}
56
+ {...props}
57
+ />
58
+ );
59
+
60
+ export const DropdownMenuSeparator = ({
61
+ className,
62
+ ...props
63
+ }: ComponentProps<typeof DropdownMenuPrimitive.Separator>) => (
64
+ <DropdownMenuPrimitive.Separator
65
+ data-slot="dropdown-menu-separator"
66
+ className={cn('cn-dropdown-menu-separator', className)}
67
+ {...props}
68
+ />
69
+ );
@@ -1,33 +1,17 @@
1
- import type { FallbackProps } from 'react-error-boundary';
2
-
3
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
4
2
 
5
3
  import { ErrorBoundary } from './ErrorBoundary';
4
+ import { Paragraph } from '../Paragraph/Paragraph';
6
5
 
7
6
  // Component that intentionally throws an error
8
7
  const Bomb = () => {
9
8
  throw new Error('Boom!');
10
9
  };
11
10
 
12
- // Simple custom fallback for demonstration
13
- const CustomFallback = ({ error, resetErrorBoundary }: FallbackProps) => (
14
- <div role="alert" className="p-4 border border-red-300 rounded-lg bg-red-50">
15
- <p className="font-medium text-red-700">Custom Fallback:</p>
16
-
17
- <p className="text-red-600">{error instanceof Error ? error.message : 'Unknown'}</p>
18
-
19
- <button
20
- className="px-3 py-1 mt-2 text-white bg-red-600 rounded hover:bg-red-700"
21
- onClick={resetErrorBoundary}
22
- >
23
- Try Again
24
- </button>
25
- </div>
26
- );
27
-
28
11
  const meta = {
29
12
  title: 'Components/ErrorBoundary',
30
13
  component: ErrorBoundary,
14
+ tags: ['autodocs'],
31
15
  parameters: {
32
16
  layout: 'centered',
33
17
  docs: {
@@ -37,7 +21,6 @@ const meta = {
37
21
  },
38
22
  },
39
23
  },
40
- tags: ['autodocs'],
41
24
  } satisfies Meta<typeof ErrorBoundary>;
42
25
 
43
26
  export default meta;
@@ -45,16 +28,12 @@ type Story = StoryObj<typeof meta>;
45
28
 
46
29
  export const Default: Story = {
47
30
  args: {
48
- children: (
49
- <div className="p-4 text-green-800 border rounded-md bg-green-50">
50
- <p>No errors here — everything renders normally.</p>
51
- </div>
52
- ),
31
+ children: <Paragraph>No errors here — everything renders normally.</Paragraph>,
53
32
  },
54
33
  parameters: {
55
34
  docs: {
56
35
  description: {
57
- story: 'Default behavior children render normally when no error is thrown.',
36
+ story: 'Default behavior. Children render normally when no error is thrown.',
58
37
  },
59
38
  },
60
39
  },
@@ -72,17 +51,3 @@ export const WithError: Story = {
72
51
  },
73
52
  },
74
53
  };
75
-
76
- export const WithCustomFallback: Story = {
77
- args: {
78
- children: <Bomb />,
79
- fallback: CustomFallback,
80
- },
81
- parameters: {
82
- docs: {
83
- description: {
84
- story: 'ErrorBoundary can render a custom fallback component when errors occur.',
85
- },
86
- },
87
- },
88
- };
@@ -1,13 +1,16 @@
1
- import type { ComponentType, ErrorInfo, ReactNode } from 'react';
1
+ 'use client';
2
+
3
+ import type { ComponentProps, ComponentType, ErrorInfo } from 'react';
2
4
 
3
5
  import { ErrorBoundary as ReactErrorBoundary, type FallbackProps } from 'react-error-boundary';
4
6
 
5
7
  import { ErrorFallback } from './ErrorFallback';
6
8
 
7
- type ErrorBoundaryProps = {
8
- children: ReactNode;
9
+ export type ErrorBoundaryProps = Omit<
10
+ ComponentProps<typeof ReactErrorBoundary>,
11
+ 'onError' | 'FallbackComponent'
12
+ > & {
9
13
  fallback?: ComponentType<FallbackProps>;
10
- onReset?: () => void;
11
14
  };
12
15
 
13
16
  export const ErrorBoundary = ({ children, fallback, onReset }: ErrorBoundaryProps) => {
@@ -1,18 +1,21 @@
1
1
  'use client';
2
2
 
3
- import { useId } from 'react';
3
+ import { useId, type ComponentProps } from 'react';
4
4
 
5
5
  import { type FallbackProps } from 'react-error-boundary';
6
6
 
7
- import { ApiError } from '../../errors/ApiError';
8
- import type { ExtendProps } from '../../types';
7
+ import { formatErrorForClipboard, getErrorDisplayInfo } from '../../errors/errorDisplay';
9
8
  import { cn } from '../../utils';
10
- import { Accordion } from '../accordion/Accordion';
11
9
  import { Button } from '../Button/Button';
12
- import { Heading } from '../Heading/Heading';
10
+ import { CopyButton } from '../CopyButton/CopyButton';
11
+ import { ErrorText } from '../ErrorText/ErrorText';
12
+ import { HintText } from '../HintText/HintText';
13
+ import { InfoBox } from '../InfoBox/InfoBox';
14
+ import { ButtonGroup } from '../Layout/ButtonGroup/ButtonGroup';
13
15
  import { Paragraph } from '../Paragraph/Paragraph';
16
+ import { ScrollArea } from '../ScrollArea/ScrollArea';
14
17
 
15
- export type ErrorFallbackProps = ExtendProps<'div', FallbackProps>;
18
+ export type ErrorFallbackProps = ComponentProps<'div'> & FallbackProps;
16
19
 
17
20
  export const ErrorFallback = ({
18
21
  resetErrorBoundary,
@@ -22,78 +25,41 @@ export const ErrorFallback = ({
22
25
  }: ErrorFallbackProps) => {
23
26
  const id = useId();
24
27
 
25
- let message;
26
- let details = undefined;
27
- let digest = undefined;
28
-
29
- if (error instanceof ApiError) {
30
- message = error.message;
31
- details = error.details;
32
- digest = error.digest;
33
- } else if (error instanceof Error) {
34
- message = error.message;
35
- details = error.cause?.toString();
36
- } else {
37
- message = 'Unknown error';
38
- }
39
-
40
- const copyToClipboard = () => {
41
- navigator.clipboard.writeText(
42
- // eslint-disable-next-line sonarjs/no-nested-template-literals
43
- `App Error: ${message}, ${details ? `Details: ${details}, ` : ''}${digest ? `Digest: ${digest}` : ''}`,
44
- );
45
- };
28
+ const info = getErrorDisplayInfo(error);
29
+ const clipboardContent = formatErrorForClipboard(info);
46
30
 
47
31
  return (
48
32
  <div
49
33
  role="alert"
50
34
  aria-labelledby={id}
51
- className={cn(
52
- 'grid gap-2 border-form border-transparent border-l-error max-w-full pl-2',
53
- className,
54
- )}
55
- style={{ gridTemplateColumns: '1fr auto' }}
35
+ className={cn('border-l-standard pl-gds-2 border-error', className)}
56
36
  {...props}
57
37
  >
58
- <div className="flex flex-col gap-0.5">
59
- <Heading id={id} type="h3" className="text-error text-base font-semibold py-0">
60
- Something went wrong
61
- </Heading>
38
+ <div className="flex items-center gap-gds-1">
39
+ <ErrorText id={id}>Something went wrong</ErrorText>
62
40
 
63
- <Paragraph className="leading-snug pb-0">
64
- <strong>Reason:</strong> {message}
65
- </Paragraph>
66
- </div>
41
+ <InfoBox>
42
+ <div className="flex flex-col gap-gds-2">
43
+ <ScrollArea className="max-h-32 overflow-y-auto overflow-x-hidden">
44
+ <Paragraph className="leading-snug pb-0">
45
+ <strong>Reason:</strong> {info.message}
46
+ </Paragraph>
47
+ </ScrollArea>
67
48
 
68
- <div className="flex items-start gap-1">
69
- <Button onClick={copyToClipboard} variant="secondary" className="shrink-0">
70
- Copy
71
- </Button>
49
+ <div className="flex flex-col gap-gds-1">
50
+ <ButtonGroup>
51
+ <CopyButton appearance="secondary" content={clipboardContent} />
72
52
 
73
- <Button onClick={resetErrorBoundary} variant="secondary" className="shrink-0">
74
- Retry
75
- </Button>
76
- </div>
53
+ <Button onClick={resetErrorBoundary}>Retry</Button>
54
+ </ButtonGroup>
77
55
 
78
- {details && (
79
- <div className="col-span-2">
80
- <Accordion title="Show details">
81
- <div className="flex gap-0.5 flex-col">
82
- {digest && (
83
- <Paragraph className="leading-snug pb-0">
84
- <strong>Error ID:</strong> {digest}
85
- </Paragraph>
86
- )}
87
- <pre
88
- className="text-base bg-white border border-gray-200 p-2 overflow-y-auto max-h-32
89
- whitespace-pre-wrap"
90
- >
91
- {details}
92
- </pre>
56
+ <HintText className="text-sm">
57
+ Copy to share error details with the app administrator.
58
+ </HintText>
93
59
  </div>
94
- </Accordion>
95
- </div>
96
- )}
60
+ </div>
61
+ </InfoBox>
62
+ </div>
97
63
  </div>
98
64
  );
99
65
  };
@@ -0,0 +1,32 @@
1
+ @layer components {
2
+ /* MARK: Error modal
3
+ Row layout: icon column beside a content column. cn-modal-content stays
4
+ a plain block by default (see Modal.css), so it's safe to turn into a
5
+ row here without fighting a hardcoded flex-direction. */
6
+ .cn-error-modal {
7
+ @apply flex flex-row gap-gds-3;
8
+ }
9
+
10
+ /* items-start keeps the icon pinned to the top of the row rather than
11
+ stretching/centering across the full modal height (including the
12
+ footer), justify-center centres it within its own column. */
13
+ .cn-error-modal-icon {
14
+ @apply flex shrink-0 items-start justify-center text-error;
15
+ }
16
+
17
+ /* min-w-0 lets this column shrink below its content's natural width
18
+ within the flex row, same reasoning as elsewhere in this project:
19
+ without it, long unbroken content (e.g. a digest UUID) could force the
20
+ row wider than the modal itself instead of wrapping/scrolling. */
21
+ .cn-error-modal-body {
22
+ @apply flex min-w-0 flex-col;
23
+ }
24
+
25
+ /* Scoped to the summary list only (not the copy button/hint below it), an
26
+ arbitrary-length details string (e.g. a dumped API error body) or a long
27
+ digest can otherwise push the whole modal to an unreasonable height,
28
+ rather than just scrolling internally. */
29
+ .cn-error-modal-details {
30
+ @apply max-h-60 overflow-y-auto;
31
+ }
32
+ }
@@ -0,0 +1,101 @@
1
+ import { useState } from 'react';
2
+
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { ErrorModal } from './ErrorModal';
6
+ import { ApiError } from '../../errors/ApiError';
7
+ import { Button } from '../Button/Button';
8
+ import { Paragraph } from '../Paragraph/Paragraph';
9
+
10
+ const meta: Meta<typeof ErrorModal> = {
11
+ title: 'Components/ErrorModal',
12
+ component: ErrorModal,
13
+ tags: ['autodocs'],
14
+ parameters: {
15
+ docs: {
16
+ description: {
17
+ component:
18
+ 'A Modal preset for reporting an error: an alert icon beside the message, with an optional expandable "Technical information" section (message, details, digest, and a copy-to-clipboard button that includes the stack trace when available) driven entirely by the `error` prop. Shares its error-describing logic with ErrorFallback via `getErrorDisplayInfo`/`formatErrorForClipboard`, so both surfaces describe the same error the same way.',
19
+ },
20
+ },
21
+ },
22
+ argTypes: {
23
+ children: { control: false },
24
+ },
25
+ };
26
+
27
+ export default meta;
28
+
29
+ type Story = StoryObj<typeof ErrorModal>;
30
+
31
+ export const Default: Story = {
32
+ render: () => {
33
+ const [open, setOpen] = useState(false);
34
+
35
+ return (
36
+ <>
37
+ <Button onClick={() => setOpen(true)}>Trigger error</Button>
38
+
39
+ <ErrorModal
40
+ open={open}
41
+ onOpenChange={setOpen}
42
+ error={new Error('Failed to fetch sampling points')}
43
+ >
44
+ <Paragraph>An error occurred while fetching sampling points.</Paragraph>
45
+ </ErrorModal>
46
+ </>
47
+ );
48
+ },
49
+ };
50
+
51
+ export const WithApiError: Story = {
52
+ parameters: {
53
+ docs: {
54
+ description: {
55
+ story:
56
+ 'ApiError additionally carries `details` and a `digest`, both shown in the technical information section and included in the copied text.',
57
+ },
58
+ },
59
+ },
60
+ render: () => {
61
+ const [open, setOpen] = useState(false);
62
+
63
+ return (
64
+ <>
65
+ <Button onClick={() => setOpen(true)}>Trigger error</Button>
66
+
67
+ <ErrorModal
68
+ open={open}
69
+ onOpenChange={setOpen}
70
+ error={ApiError.internalServerError('The upstream sampling points service timed out')}
71
+ >
72
+ <Paragraph>An error occurred while fetching sampling points.</Paragraph>
73
+ </ErrorModal>
74
+ </>
75
+ );
76
+ },
77
+ };
78
+
79
+ export const WithoutError: Story = {
80
+ parameters: {
81
+ docs: {
82
+ description: {
83
+ story:
84
+ 'No `error` prop, no technical information section is rendered, just the message and a close button.',
85
+ },
86
+ },
87
+ },
88
+ render: () => {
89
+ const [open, setOpen] = useState(false);
90
+
91
+ return (
92
+ <>
93
+ <Button onClick={() => setOpen(true)}>Trigger error</Button>
94
+
95
+ <ErrorModal open={open} onOpenChange={setOpen}>
96
+ <Paragraph>Something went wrong. Try again in a few minutes.</Paragraph>
97
+ </ErrorModal>
98
+ </>
99
+ );
100
+ },
101
+ };