@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
@@ -0,0 +1,100 @@
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
+
9
+ const meta: Meta<typeof ErrorModal> = {
10
+ title: 'Components/ErrorModal',
11
+ component: ErrorModal,
12
+ tags: ['autodocs'],
13
+ parameters: {
14
+ docs: {
15
+ description: {
16
+ component:
17
+ '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.',
18
+ },
19
+ },
20
+ },
21
+ argTypes: {
22
+ children: { control: false },
23
+ },
24
+ };
25
+
26
+ export default meta;
27
+
28
+ type Story = StoryObj<typeof ErrorModal>;
29
+
30
+ export const Default: Story = {
31
+ render: () => {
32
+ const [open, setOpen] = useState(false);
33
+
34
+ return (
35
+ <>
36
+ <Button onClick={() => setOpen(true)}>Trigger error</Button>
37
+
38
+ <ErrorModal
39
+ open={open}
40
+ onOpenChange={setOpen}
41
+ error={new Error('Failed to fetch sampling points')}
42
+ >
43
+ An error occurred while fetching sampling points.
44
+ </ErrorModal>
45
+ </>
46
+ );
47
+ },
48
+ };
49
+
50
+ export const WithApiError: Story = {
51
+ parameters: {
52
+ docs: {
53
+ description: {
54
+ story:
55
+ 'ApiError additionally carries `details` and a `digest`, both shown in the technical information section and included in the copied text.',
56
+ },
57
+ },
58
+ },
59
+ render: () => {
60
+ const [open, setOpen] = useState(false);
61
+
62
+ return (
63
+ <>
64
+ <Button onClick={() => setOpen(true)}>Trigger error</Button>
65
+
66
+ <ErrorModal
67
+ open={open}
68
+ onOpenChange={setOpen}
69
+ error={ApiError.internalServerError('The upstream sampling points service timed out')}
70
+ >
71
+ An error occurred while fetching sampling points.
72
+ </ErrorModal>
73
+ </>
74
+ );
75
+ },
76
+ };
77
+
78
+ export const WithoutError: Story = {
79
+ parameters: {
80
+ docs: {
81
+ description: {
82
+ story:
83
+ 'No `error` prop, no technical information section is rendered, just the message and a close button.',
84
+ },
85
+ },
86
+ },
87
+ render: () => {
88
+ const [open, setOpen] = useState(false);
89
+
90
+ return (
91
+ <>
92
+ <Button onClick={() => setOpen(true)}>Trigger error</Button>
93
+
94
+ <ErrorModal open={open} onOpenChange={setOpen}>
95
+ Something went wrong. Try again in a few minutes.
96
+ </ErrorModal>
97
+ </>
98
+ );
99
+ },
100
+ };
@@ -0,0 +1,123 @@
1
+ 'use client';
2
+
3
+ import type { ComponentProps, ReactNode } from 'react';
4
+
5
+ import { LuCircleAlert } from 'react-icons/lu';
6
+
7
+ import { formatErrorForClipboard, getErrorDisplayInfo } from '../../errors/errorDisplay';
8
+ import { Button } from '../Button/Button';
9
+ import { CopyButton } from '../CopyButton/CopyButton';
10
+ import { Details, DetailsContent, DetailsTrigger } from '../Details/Details';
11
+ import { HintText } from '../HintText/HintText';
12
+ import { Stack } from '../Layout/Stack/Stack';
13
+ import {
14
+ Modal,
15
+ ModalContent,
16
+ ModalDescription,
17
+ ModalFooter,
18
+ ModalHeader,
19
+ ModalTitle,
20
+ } from '../Modal/Modal';
21
+ import {
22
+ SummaryList,
23
+ SummaryListKey,
24
+ SummaryListRow,
25
+ SummaryListValue,
26
+ } from '../SummaryList/SummaryList';
27
+
28
+ export type ErrorModalProps = Omit<ComponentProps<typeof Modal>, 'children'> & {
29
+ /** The error that occurred. When set, an expandable "Technical information"
30
+ * section shows its message/details/digest, with a button to copy all of
31
+ * it (including the stack trace, when available) to the clipboard. */
32
+ error?: unknown;
33
+ title?: ReactNode;
34
+ children?: ReactNode;
35
+ closeLabel?: ReactNode;
36
+ };
37
+
38
+ export const ErrorModal = ({
39
+ error,
40
+ title = 'An error occurred',
41
+ children,
42
+ closeLabel = 'Close',
43
+ onOpenChange,
44
+ ...props
45
+ }: ErrorModalProps) => {
46
+ const info = error !== undefined ? getErrorDisplayInfo(error) : null;
47
+
48
+ return (
49
+ <Modal onOpenChange={onOpenChange} {...props}>
50
+ <ModalContent className="cn-error-modal">
51
+ <div className="cn-error-modal-icon">
52
+ <LuCircleAlert aria-hidden className="size-8" />
53
+ </div>
54
+
55
+ <Stack gap="sm" className="cn-error-modal-body">
56
+ <ModalHeader>
57
+ <ModalTitle>{title}</ModalTitle>
58
+ </ModalHeader>
59
+
60
+ {/* ModalDescription renders a <p> (it's the target of the
61
+ dialog's aria-describedby), which can only contain phrasing
62
+ content. children is assumed to be plain descriptive text for
63
+ that reason, Details below is a sibling instead, not nested
64
+ inside it, both to stay valid HTML (a browser silently closes
65
+ a <p> early upon hitting a nested block element, which desyncs
66
+ SSR/client markup into a hydration error, exactly like nesting
67
+ a <dl> in one here did) and because interactive elements like
68
+ the copy button below don't make sense as part of an
69
+ aria-describedby'd description in the first place. */}
70
+ <ModalDescription>{children}</ModalDescription>
71
+
72
+ {info ? (
73
+ <Details>
74
+ <DetailsTrigger>Technical information</DetailsTrigger>
75
+
76
+ <DetailsContent>
77
+ <Stack gap="sm">
78
+ <div className="cn-error-modal-details">
79
+ <SummaryList>
80
+ <SummaryListRow>
81
+ <SummaryListKey>Reason</SummaryListKey>
82
+
83
+ <SummaryListValue>{info.message}</SummaryListValue>
84
+ </SummaryListRow>
85
+
86
+ {info.details ? (
87
+ <SummaryListRow>
88
+ <SummaryListKey>Details</SummaryListKey>
89
+
90
+ <SummaryListValue>{info.details}</SummaryListValue>
91
+ </SummaryListRow>
92
+ ) : null}
93
+
94
+ {info.digest ? (
95
+ <SummaryListRow>
96
+ <SummaryListKey>Digest</SummaryListKey>
97
+
98
+ <SummaryListValue>{info.digest}</SummaryListValue>
99
+ </SummaryListRow>
100
+ ) : null}
101
+ </SummaryList>
102
+ </div>
103
+
104
+ <div className="flex flex-col gap-gds-1">
105
+ <CopyButton appearance="secondary" content={formatErrorForClipboard(info)} />
106
+
107
+ <HintText className="text-sm">
108
+ Copy to share error details with the app administrator.
109
+ </HintText>
110
+ </div>
111
+ </Stack>
112
+ </DetailsContent>
113
+ </Details>
114
+ ) : null}
115
+
116
+ <ModalFooter className="sm:justify-start">
117
+ <Button onClick={() => onOpenChange?.(false)}>{closeLabel}</Button>
118
+ </ModalFooter>
119
+ </Stack>
120
+ </ModalContent>
121
+ </Modal>
122
+ );
123
+ };
@@ -0,0 +1,22 @@
1
+ @layer components {
2
+ /* Meant to stay mounted at all times (even with no error, as empty
3
+ children), rather than being conditionally rendered, so it's a stable
4
+ role="alert" live region screen readers can reliably announce content
5
+ changes on. A live region needs to already exist in the DOM before its
6
+ content changes for that announcement to be reliable, inserting a
7
+ brand new role="alert" node after the fact is a recognised trigger too,
8
+ but is markedly less consistent across AT/browser combinations.
9
+ Consumers that always render a fixed, non-empty message (e.g.
10
+ ErrorFallback) are unaffected by the rule below, it only ever matches
11
+ when there's genuinely no content. */
12
+ .cn-error-text {
13
+ @apply text-error font-bold;
14
+
15
+ /* Cancels out the gap-gds-1 that FormGroup/FieldSet's flex layout would
16
+ otherwise still apply around this element even while it's empty,
17
+ leaving no permanent dead space when there's no error to show. */
18
+ &:empty {
19
+ margin-top: calc(var(--spacing-gds-1) * -1);
20
+ }
21
+ }
22
+ }
@@ -1,27 +1,30 @@
1
+ import { useState } from 'react';
2
+
1
3
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
4
 
3
5
  import { ErrorText } from './ErrorText';
4
- import { ExternalLink } from '../link/ExternalLink';
6
+ import { Button } from '../Button/Button';
7
+ import { FormGroup } from '../Form/FormGroup';
8
+ import { Input } from '../Input/Input';
9
+ import { Label } from '../Label/Label';
5
10
 
6
11
  const meta: Meta<typeof ErrorText> = {
7
12
  title: 'Components/ErrorText',
8
13
  component: ErrorText,
14
+ tags: ['autodocs'],
9
15
  parameters: {
10
- layout: 'padded',
16
+ docs: {
17
+ description: {
18
+ component:
19
+ 'Show an error message next to the field and in the Error summary component when there is a validation error. Renders role="alert" (plus explicit aria-live/aria-atomic). Always render ErrorText, even with no error (empty children), rather than conditionally mounting it: a live region needs to already exist in the DOM before its content changes for screen readers to reliably announce that change, inserting a brand new role="alert" node after the fact is a recognised trigger too, but is markedly less consistent. The empty state collapses to zero height and cancels out FormGroup/FieldSet\'s own gap, so it never leaves a permanent gap when there\'s nothing to show.',
20
+ },
21
+ },
11
22
  },
12
- tags: ['autodocs'],
13
23
  argTypes: {
14
- children: {
15
- description: 'The content to display for the error',
16
- control: false,
17
- },
18
- className: {
19
- description: 'Additional TailwindCSS classes to apply',
20
- control: 'text',
21
- },
24
+ children: { control: 'text' },
22
25
  },
23
26
  args: {
24
- children: 'Error message',
27
+ children: 'The date your passport was issued must be in the past',
25
28
  },
26
29
  };
27
30
 
@@ -31,22 +34,34 @@ type Story = StoryObj<typeof ErrorText>;
31
34
 
32
35
  export const Default: Story = {};
33
36
 
34
- export const CustomStyling: Story = {
35
- args: {
36
- className: 'text-3xl',
37
- children: 'Custom styled error message',
38
- },
37
+ const ToggleableExample = () => {
38
+ const [hasError, setHasError] = useState(false);
39
+
40
+ return (
41
+ <div className="flex flex-col gap-gds-4">
42
+ <FormGroup invalid={hasError}>
43
+ <Label htmlFor="full-name">Full name</Label>
44
+
45
+ <Input id="full-name" aria-invalid={hasError} aria-describedby="full-name-error" />
46
+
47
+ <ErrorText id="full-name-error">{hasError ? 'Enter your full name' : ''}</ErrorText>
48
+ </FormGroup>
49
+
50
+ <Button onClick={() => setHasError((current) => !current)} className="self-start">
51
+ Toggle error
52
+ </Button>
53
+ </div>
54
+ );
39
55
  };
40
56
 
41
- export const ComplexChildren: Story = {
42
- args: {
43
- children: (
44
- <div>
45
- Error message with{' '}
46
- <ExternalLink className="underline text-link" href="/">
47
- Link
48
- </ExternalLink>
49
- </div>
50
- ),
57
+ export const AlwaysMounted: Story = {
58
+ parameters: {
59
+ docs: {
60
+ description: {
61
+ story:
62
+ "ErrorText stays mounted the whole time here, only its children toggle between empty and the message, click the button to see there's no gap left behind when it's empty.",
63
+ },
64
+ },
51
65
  },
66
+ render: () => <ToggleableExample />,
52
67
  };
@@ -1,18 +1,20 @@
1
- import type { ExtendProps } from '../../types/utils';
2
- import { cn } from '../../utils';
1
+ import type { ComponentProps } from 'react';
3
2
 
4
- export type ErrorTextProps = ExtendProps<'p'>;
3
+ import { cn } from '../../utils/components';
4
+ import { Paragraph } from '../Paragraph/Paragraph';
5
5
 
6
- export const ErrorText = ({ className, children, ...props }: ErrorTextProps) => {
7
- const Component = typeof children === 'string' ? 'p' : 'div';
6
+ export type ErrorTextProps = ComponentProps<typeof Paragraph>;
8
7
 
8
+ export const ErrorText = ({ className, children, ...props }: ErrorTextProps) => {
9
9
  return (
10
- <Component
10
+ <Paragraph
11
11
  role="alert"
12
- className={cn('mb-3 text-base text-error font-bold', className)}
12
+ aria-live="assertive"
13
+ aria-atomic="true"
14
+ className={cn('cn-error-text', className)}
13
15
  {...props}
14
16
  >
15
17
  {children}
16
- </Component>
18
+ </Paragraph>
17
19
  );
18
20
  };
@@ -0,0 +1,12 @@
1
+ @layer components {
2
+ .cn-external-link-icon-wrapper {
3
+ @apply whitespace-nowrap;
4
+ }
5
+
6
+ .cn-external-link-icon {
7
+ @apply inline ml-0.5;
8
+ width: 1em;
9
+ height: 1em;
10
+ vertical-align: -0.15em;
11
+ }
12
+ }
@@ -0,0 +1,43 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { ExternalLink } from './ExternalLink';
4
+
5
+ const meta: Meta<typeof ExternalLink> = {
6
+ title: 'Components/ExternalLink',
7
+ component: ExternalLink,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'Use ExternalLink for navigation that should open in a new tab. Generally avoid opening links in a new tab or window, however, if needed, aim to include the words `opens in new tab` in the link. This is the default variant of this component.',
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ variant: {
19
+ control: { type: 'radio' },
20
+ options: ['default', 'icon', 'text'],
21
+ },
22
+ href: { control: 'text' },
23
+ children: { control: 'text' },
24
+ },
25
+ args: {
26
+ href: '#',
27
+ children: 'Story link',
28
+ },
29
+ };
30
+
31
+ export default meta;
32
+
33
+ type Story = StoryObj<typeof ExternalLink>;
34
+
35
+ export const Default: Story = {};
36
+
37
+ export const WithIcon: Story = {
38
+ args: { variant: 'icon' },
39
+ };
40
+
41
+ export const WithNothing: Story = {
42
+ args: { variant: 'plain' },
43
+ };
@@ -0,0 +1,43 @@
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { LuExternalLink } from 'react-icons/lu';
4
+
5
+ import { cn } from '../../utils/components';
6
+ import { Link } from '../Link/Link';
7
+
8
+ export type ExternalLinkVariants = 'plain' | 'icon' | 'text';
9
+
10
+ export type ExternalLinkProps = Omit<ComponentProps<typeof Link>, 'target' | 'rel' | 'variant'> & {
11
+ variant?: ExternalLinkVariants;
12
+ };
13
+
14
+ export const ExternalLink = ({
15
+ children,
16
+ variant = 'text',
17
+ className,
18
+ ...props
19
+ }: ExternalLinkProps) => {
20
+ const content = {
21
+ plain: null,
22
+ icon: (
23
+ <span className="cn-external-link-icon-wrapper">
24
+ <span className="sr-only">(opens in new tab)</span>
25
+ &nbsp;
26
+ <LuExternalLink aria-hidden className="cn-external-link-icon" />
27
+ </span>
28
+ ),
29
+ text: <span> (opens in new tab)</span>,
30
+ } as const;
31
+
32
+ return (
33
+ <Link
34
+ {...props}
35
+ className={cn('cn-external-link', className)}
36
+ rel="noopener noreferrer"
37
+ target="_blank"
38
+ >
39
+ {children}
40
+ {content[variant]}
41
+ </Link>
42
+ );
43
+ };
@@ -0,0 +1,103 @@
1
+ @layer components {
2
+ /* MARK: Fieldset
3
+ Reset browser fieldset defaults. Browsers apply borders,
4
+ padding, and an unusual min-width that breaks flex layouts. */
5
+ .cn-field-set {
6
+ @apply min-w-0 m-0 p-0 border-0 flex flex-col gap-gds-2;
7
+ }
8
+
9
+ /* MARK: Legend
10
+ Plain body text by default, matching GDS's own unmodified
11
+ govuk-fieldset__legend (and govuk-label): no bold, same size as a
12
+ label (govuk-fieldset__legend and govuk-label both use govuk-font(19)
13
+ in real GDS, this project's text-lg), not automatically a heading. For
14
+ a heading-like look, nest a real Heading component inside FieldLegend
15
+ instead, mirroring GDS's own documented pattern of nesting an <h1>
16
+ inside <legend> on question-per-page forms, rather than baking
17
+ heading-mimicking size variants into the legend itself. */
18
+ /* margin-bottom (not the fieldset's own gap-gds-2): a <legend> is exempt
19
+ from its fieldset's flex gap. Per spec, a flex/grid fieldset lays out
20
+ its OTHER children inside an anonymous wrapper box, legend sits
21
+ outside it as its own block box, so gap, which only bridges children
22
+ within the same flex/grid formatting context, never reaches the
23
+ space between legend and whatever follows it, unlike FormGroup's
24
+ Label, an ordinary element with no such carve-out, which does get its
25
+ gap-gds-1 for free. Matches gap-gds-2 so a legend reads the same as
26
+ any other item-to-item gap in the set. */
27
+ .cn-field-legend {
28
+ @apply block box-border max-w-full p-0 text-lg text-body whitespace-normal;
29
+ margin-bottom: var(--spacing-gds-2);
30
+ }
31
+
32
+ /* GDS tightens the gap specifically when a group-level hint sits directly
33
+ under a plain legend (margin-top: govuk-spacing(-1) on the hint). This
34
+ stacks with cn-field-legend's own margin-bottom above (flex item
35
+ margins never collapse), landing on a net gds-1 (5px) rather than the
36
+ usual gds-2 (10px) between legend and controls. */
37
+ .cn-field-legend + .cn-hint-text {
38
+ margin-top: calc(var(--spacing-gds-1) * -1);
39
+ }
40
+
41
+ /* MARK: Divider
42
+ Appears between groups of controls, typically contains "or".
43
+ Width matches GDS (40px). Sits naturally in the item flex-col gap. */
44
+ .cn-field-divider {
45
+ @apply w-10 text-center text-base font-normal;
46
+ }
47
+
48
+ /* MARK: Item
49
+ Mirrors GDS's govuk-radios__item/govuk-checkboxes__item exactly: flex
50
+ with wrap, not grid. Control and label sit side by side on the first
51
+ line; the hint (if any) is forced onto its own wrapped line by w-full,
52
+ a flex item at 100% width can't share a line with anything else. Unlike
53
+ grid, flex has no shared column to auto-align the hint into under the
54
+ label, so its start padding has to repeat the control's width plus the
55
+ item's own gap-x by hand, same as GDS hardcodes touch-target + label
56
+ padding rather than measuring anything. margin-top is negative for the
57
+ same reason GDS's own hint margin-top is: it sits closer to the label
58
+ above it than a hint normally would on its own.
59
+
60
+ No align-items override here, same as GDS's own item wrapper: the
61
+ control (Radio/Checkbox) has an explicit, non-auto height, so the
62
+ default stretch alignment has no size to stretch it into and falls
63
+ back to flex-start, top-aligning it for free. The label gets its own
64
+ align-self: center instead (see below), matching GDS's own
65
+ govuk-radios__label/govuk-checkboxes__label. Centering only the label,
66
+ not the row, is what keeps both cases correct at once: a short label
67
+ centers against the control's full height same as before, and a
68
+ wrapping label, being the row's own tallest item once it wraps,
69
+ already fills the full row height, so centering it is a no-op and it
70
+ stays flush with the control's top instead of floating down between
71
+ the lines the way row-level centering would. */
72
+ .cn-field-item {
73
+ @apply flex flex-wrap gap-x-gds-2;
74
+ }
75
+
76
+ /* min-w-0 flex-1: without this, a long label wraps as a whole flex item
77
+ instead of wrapping its own text, a flex item's default min-width is
78
+ auto, not 0, so it won't shrink below its label text's full unwrapped
79
+ width, and once that doesn't fit next to the control on the row,
80
+ flex-wrap moves the entire label below the control rather than
81
+ letting the label's own text wrap while staying beside it. flex-1
82
+ (not just min-w-0) is what makes the label actually claim the row's
83
+ remaining width to wrap within, rather than shrinking to its own
84
+ content and only wrapping once genuinely out of room.
85
+
86
+ self-center: see the item comment above. */
87
+ .cn-field-item > .cn-label {
88
+ @apply min-w-0 flex-1 self-center;
89
+ }
90
+
91
+ .cn-field-item > .cn-hint-text {
92
+ @apply w-full;
93
+ margin-top: calc(var(--spacing-gds-1) * -1);
94
+ padding-inline-start: calc(2.5rem + var(--spacing-gds-2));
95
+ padding-inline-end: var(--spacing-gds-3);
96
+ }
97
+
98
+ /* Small Radio/Checkbox (cn-radio-size-sm/cn-checkbox-size-sm) are 1.5rem,
99
+ not the base 2.5rem, so the hint's offset needs to match. */
100
+ .cn-field-item:has(.cn-radio-size-sm, .cn-checkbox-size-sm) > .cn-hint-text {
101
+ padding-inline-start: calc(1.5rem + var(--spacing-gds-2));
102
+ }
103
+ }