@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,69 @@
1
+ @layer components {
2
+ /* MARK: Radio
3
+ Radix RadioGroup.Item renders a <button role="radio">.
4
+ Border is explicit (not @apply cn-form-border) because appearance:none on
5
+ <button> can override utility-layer border styles. */
6
+ .cn-radio {
7
+ @apply relative rounded-full appearance-none cursor-pointer flex-none box-border bg-white p-0
8
+ outline-none;
9
+ border: var(--border-width-form) solid var(--color-divider-input);
10
+
11
+ &:focus:not(:disabled) {
12
+ @apply outline-focus border-4 outline-offset-1;
13
+ box-shadow: 0 0 0 var(--outline-width-focus) var(--color-focus);
14
+ }
15
+
16
+ &:disabled {
17
+ @apply opacity-50 cursor-not-allowed;
18
+ }
19
+ }
20
+
21
+ /* MARK: Indicator
22
+ RadioGroup.Indicator only mounts when checked, styled as a black dot.
23
+ Absolutely positioned + translate(-50%, -50%) rather than the parent's
24
+ flex items-center/justify-center: flex centering resolves (container -
25
+ item) / 2 during layout, which has to snap to a whole device pixel, so
26
+ a fractional remainder rounds asymmetrically (one side up, one down) at
27
+ 1x DPR, visible as the dot sitting a pixel off-centre, while the exact
28
+ same fraction is imperceptible at 2x/3x DPR where it still lands on a
29
+ clean device pixel either way. translate is resolved in the compositor
30
+ rather than layout, so it isn't pixel-snapped the same way. This also
31
+ matches real GDS: govuk-radios positions its own dot with absolute
32
+ top/left, not flexbox. */
33
+ .cn-radio-indicator {
34
+ @apply absolute top-1/2 left-1/2 -translate-1/2 block rounded-full bg-body;
35
+ }
36
+
37
+ /* MARK: Sizes */
38
+ .cn-radio-size-sm {
39
+ @apply w-6 h-6;
40
+
41
+ & .cn-radio-indicator {
42
+ @apply w-3 h-3;
43
+ }
44
+ }
45
+
46
+ .cn-radio-size-base {
47
+ @apply w-10 h-10;
48
+
49
+ & .cn-radio-indicator {
50
+ @apply w-5 h-5;
51
+ }
52
+ }
53
+
54
+ /* MARK: Group
55
+ gap-gds-2 matches GDS's own govuk-radios__item margin-bottom (10px).
56
+ Radix reflects the orientation prop as aria-orientation on the group's
57
+ own root, but doesn't do anything visual with it itself (it's only used
58
+ internally for arrow-key roving focus direction), so the row layout for
59
+ orientation="horizontal" has to be applied here. flex-wrap + the wider
60
+ gap-x match GDS's own govuk-radios--inline (wraps rather than
61
+ overflowing, govuk-spacing(4) between items horizontally). */
62
+ .cn-radio-group {
63
+ @apply flex flex-col gap-gds-2;
64
+
65
+ &[aria-orientation='horizontal'] {
66
+ @apply flex-row flex-wrap items-center gap-x-gds-4 gap-y-gds-2;
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,169 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { Radio, RadioGroup } from './Radio';
4
+ import { FieldItem } from '../Fieldset/Fieldset';
5
+ import { Label } from '../Label/Label';
6
+
7
+ const meta: Meta<typeof Radio> = {
8
+ title: 'Components/Radio',
9
+ component: Radio,
10
+ tags: ['autodocs'],
11
+ parameters: {
12
+ docs: {
13
+ description: {
14
+ component: 'Use the radios component when users can only select one option from a list.',
15
+ },
16
+ },
17
+ },
18
+ argTypes: {
19
+ size: {
20
+ control: { type: 'radio' },
21
+ options: ['sm', 'base'],
22
+ },
23
+ disabled: { control: 'boolean' },
24
+ },
25
+ args: {
26
+ size: 'base',
27
+ value: 'default',
28
+ },
29
+ };
30
+
31
+ export default meta;
32
+
33
+ type Story = StoryObj<typeof Radio>;
34
+
35
+ export const Default: Story = {
36
+ render: (args) => (
37
+ <RadioGroup>
38
+ <Radio {...args} />
39
+ </RadioGroup>
40
+ ),
41
+ };
42
+
43
+ export const Sizes: Story = {
44
+ render: () => (
45
+ <div className="flex items-center gap-gds-4">
46
+ <div className="flex flex-col items-center gap-gds-1">
47
+ <RadioGroup>
48
+ <Radio size="sm" value="sm" id="radio-size-sm" />
49
+ </RadioGroup>
50
+
51
+ <Label htmlFor="radio-size-sm">sm</Label>
52
+ </div>
53
+
54
+ <div className="flex flex-col items-center gap-gds-1">
55
+ <RadioGroup>
56
+ <Radio size="base" value="base" id="radio-size-base" />
57
+ </RadioGroup>
58
+
59
+ <Label htmlFor="radio-size-base">base</Label>
60
+ </div>
61
+ </div>
62
+ ),
63
+ };
64
+
65
+ export const WithLabels: Story = {
66
+ parameters: {
67
+ docs: {
68
+ description: {
69
+ story:
70
+ 'Standard radio group with labels. Arrow keys move between options, `RadioGroup` manages focus and selection.',
71
+ },
72
+ },
73
+ },
74
+ render: () => (
75
+ <RadioGroup defaultValue="yes">
76
+ <div className="flex flex-col gap-gds-2">
77
+ <FieldItem>
78
+ <Radio id="yes" value="yes" />
79
+
80
+ <Label htmlFor="yes">Yes</Label>
81
+ </FieldItem>
82
+
83
+ <FieldItem>
84
+ <Radio id="no" value="no" />
85
+
86
+ <Label htmlFor="no">No</Label>
87
+ </FieldItem>
88
+
89
+ <FieldItem>
90
+ <Radio id="maybe" value="maybe" />
91
+
92
+ <Label htmlFor="maybe">Maybe</Label>
93
+ </FieldItem>
94
+ </div>
95
+ </RadioGroup>
96
+ ),
97
+ };
98
+
99
+ export const Horizontal: Story = {
100
+ parameters: {
101
+ docs: {
102
+ description: {
103
+ story:
104
+ 'Pass orientation="horizontal" for an inline row of options (GDS\'s "inline" radios), it wraps onto more than one line rather than overflowing if there isn\'t room for all of them.',
105
+ },
106
+ },
107
+ },
108
+ render: () => (
109
+ <RadioGroup defaultValue="yes" orientation="horizontal">
110
+ <FieldItem>
111
+ <Radio id="h-yes" value="yes" />
112
+
113
+ <Label htmlFor="h-yes">Yes</Label>
114
+ </FieldItem>
115
+
116
+ <FieldItem>
117
+ <Radio id="h-no" value="no" />
118
+
119
+ <Label htmlFor="h-no">No</Label>
120
+ </FieldItem>
121
+
122
+ <FieldItem>
123
+ <Radio id="h-maybe" value="maybe" />
124
+
125
+ <Label htmlFor="h-maybe">Maybe</Label>
126
+ </FieldItem>
127
+ </RadioGroup>
128
+ ),
129
+ };
130
+
131
+ export const Disabled: Story = {
132
+ render: () => (
133
+ <RadioGroup defaultValue="yes">
134
+ <div className="flex flex-col gap-gds-2">
135
+ <FieldItem>
136
+ <Radio id="d-yes" value="yes" disabled />
137
+
138
+ <Label htmlFor="d-yes">Yes (disabled, selected)</Label>
139
+ </FieldItem>
140
+
141
+ <FieldItem>
142
+ <Radio id="d-no" value="no" disabled />
143
+
144
+ <Label htmlFor="d-no">No (disabled)</Label>
145
+ </FieldItem>
146
+ </div>
147
+ </RadioGroup>
148
+ ),
149
+ };
150
+
151
+ export const Small: Story = {
152
+ render: () => (
153
+ <RadioGroup defaultValue="one">
154
+ <div className="flex flex-col gap-gds-1">
155
+ <FieldItem>
156
+ <Radio id="sm-one" value="one" size="sm" />
157
+
158
+ <Label htmlFor="sm-one">Option one</Label>
159
+ </FieldItem>
160
+
161
+ <FieldItem>
162
+ <Radio id="sm-two" value="two" size="sm" />
163
+
164
+ <Label htmlFor="sm-two">Option two</Label>
165
+ </FieldItem>
166
+ </div>
167
+ </RadioGroup>
168
+ ),
169
+ };
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+
3
+ import type { ComponentProps } from 'react';
4
+
5
+ import { RadioGroup as RadioGroupPrimitive } from 'radix-ui';
6
+
7
+ import { cn } from '../../utils/components';
8
+
9
+ const RADIO_SIZES = {
10
+ sm: 'cn-radio-size-sm',
11
+ base: 'cn-radio-size-base',
12
+ } as const;
13
+
14
+ export type RadioSize = keyof typeof RADIO_SIZES;
15
+
16
+ export type RadioGroupProps<T extends string> = Omit<
17
+ ComponentProps<typeof RadioGroupPrimitive.Root>,
18
+ 'defaultValue' | 'value' | 'onValueChange'
19
+ > & {
20
+ defaultValue?: T;
21
+ value?: T;
22
+ onValueChange?: (value: T) => void;
23
+ };
24
+
25
+ export type RadioProps = ComponentProps<typeof RadioGroupPrimitive.Item> & {
26
+ size?: RadioSize;
27
+ };
28
+
29
+ export const RadioGroup = <T extends string = string>({
30
+ defaultValue,
31
+ value,
32
+ onValueChange,
33
+ className,
34
+ ...props
35
+ }: RadioGroupProps<T>) => (
36
+ <RadioGroupPrimitive.Root
37
+ {...props}
38
+ data-slot="radio-group"
39
+ className={cn('cn-radio-group', className)}
40
+ defaultValue={defaultValue as string}
41
+ value={value as string}
42
+ onValueChange={(value) => onValueChange?.(value as T)}
43
+ />
44
+ );
45
+
46
+ export const Radio = ({ className, size = 'base', ...props }: RadioProps) => (
47
+ <RadioGroupPrimitive.Item
48
+ {...props}
49
+
50
+ data-slot="radio"
51
+ data-size={size}
52
+ className={cn('cn-radio', RADIO_SIZES[size], className)}
53
+ >
54
+ <RadioGroupPrimitive.Indicator data-slot="radio-indicator" className="cn-radio-indicator" />
55
+ </RadioGroupPrimitive.Item>
56
+ );
@@ -0,0 +1,86 @@
1
+ @layer components {
2
+ /* MARK: Resizing
3
+ Applied to <body> for the duration of a drag (see ResizableHandle), so
4
+ the pointer moving during the drag can't select surrounding page text. */
5
+ body.cn-resizing {
6
+ @apply select-none;
7
+ }
8
+
9
+ /* MARK: Panel group
10
+ Flex container, direction driven by the aria-orientation attribute set
11
+ by react-resizable-panels. */
12
+ .cn-resizable-panel-group {
13
+ @apply flex h-full w-full;
14
+
15
+ &[aria-orientation='vertical'] {
16
+ @apply flex-col;
17
+ }
18
+ }
19
+
20
+ /* MARK: Panel
21
+ react-resizable-panels hardcodes overflow: visible as an inline style
22
+ on every panel's own [data-panel] div (confirmed directly in its
23
+ source), regardless of props, so any content that visually extends
24
+ even slightly past a panel's own edge (a map control, a tooltip, a
25
+ canvas that doesn't perfectly match its container) isn't clipped
26
+ there, it keeps going and can inflate the whole page's scrollable
27
+ area instead of staying contained to the panel, even though every
28
+ statically-sized ancestor up to <html> still measures a perfectly
29
+ normal, viewport-matching size. !important is the only way to win
30
+ against an inline style. */
31
+ [data-panel] {
32
+ overflow: hidden !important;
33
+ }
34
+
35
+ /* MARK: Handle
36
+ The drag separator between panels. The ::after expands the hit area
37
+ without changing the visual size. Horizontal orientation flips the
38
+ layout so the line and hit target run across the full width. cn-z-top
39
+ keeps the handle reachable regardless of DOM order relative to whatever
40
+ else is rendered in the panels either side of it. */
41
+ .cn-resizable-handle {
42
+ @apply relative cn-z-top flex items-center justify-center bg-divider-input cursor-col-resize;
43
+ width: var(--border-width-form);
44
+
45
+ &::after {
46
+ @apply content-[''] absolute inset-y-0 left-1/2 w-gds-2 -translate-x-1/2;
47
+ }
48
+
49
+ &[aria-orientation='horizontal'] {
50
+ @apply w-full cursor-row-resize;
51
+ height: var(--border-width-form);
52
+
53
+ &::after {
54
+ /* translate-x-0 resets the base -translate-x-1/2 */
55
+ @apply inset-x-0 top-1/2 w-full h-gds-2 translate-x-0 -translate-y-1/2;
56
+ }
57
+
58
+ > .cn-resizable-handle-icon {
59
+ @apply rotate-90;
60
+ }
61
+ }
62
+
63
+ &:focus-visible {
64
+ @apply outline-none;
65
+
66
+ > .cn-resizable-handle-icon {
67
+ @apply bg-focus border-focus text-on-focus;
68
+ }
69
+ }
70
+
71
+ /* Hides a disabled handle entirely rather than showing a greyed-out,
72
+ non-functional line. visibility (not display: none) so it keeps
73
+ occupying its normal thin width, react-resizable-panels tracks its
74
+ Separators by React mount/unmount, not CSS, so this never touches
75
+ that, only how the already-mounted disabled Separator looks. */
76
+ &[data-separator='disabled'] {
77
+ @apply invisible pointer-events-none;
78
+ }
79
+ }
80
+
81
+ /* MARK: Grip icon
82
+ Optional visible drag affordance centred on the handle line. */
83
+ .cn-resizable-handle-icon {
84
+ @apply z-10 flex shrink-0 items-center justify-center panel text-body size-6;
85
+ }
86
+ }
@@ -0,0 +1,123 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from './Resizable';
4
+
5
+ const meta: Meta<typeof ResizablePanelGroup> = {
6
+ title: 'Components/Resizable',
7
+ component: ResizablePanelGroup,
8
+ tags: ['autodocs'],
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component: `
13
+ Drag-to-resize panel layout powered by [react-resizable-panels](https://github.com/bvaughn/react-resizable-panels).
14
+
15
+ Install the peer dependency to use this component:
16
+
17
+ \`\`\`bash
18
+ npm install react-resizable-panels
19
+ \`\`\`
20
+
21
+ - **\`ResizablePanelGroup\`**: the flex container. Set \`direction="horizontal"\` (default) or \`direction="vertical"\`.
22
+ - **\`ResizablePanel\`**: an individual panel. Accepts \`defaultSize\` (percentage), \`minSize\`, \`maxSize\`.
23
+ - **\`ResizableHandle\`**: the drag separator. Disable \`withHandle\` to hide the visible grip icon.
24
+ `.trim(),
25
+ },
26
+ },
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+ type Story = StoryObj<typeof ResizablePanelGroup>;
32
+
33
+ export const Horizontal: Story = {
34
+ parameters: {
35
+ docs: {
36
+ description: { story: 'Default horizontal layout. Drag the handle to resize.' },
37
+ },
38
+ },
39
+ render: () => (
40
+ <ResizablePanelGroup
41
+ orientation="horizontal"
42
+ className="min-h-48 border-form border-solid border-divider-input"
43
+ >
44
+ <ResizablePanel defaultSize={50}>
45
+ <div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
46
+ Panel A
47
+ </div>
48
+ </ResizablePanel>
49
+
50
+ <ResizableHandle />
51
+
52
+ <ResizablePanel defaultSize={50}>
53
+ <div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
54
+ Panel B
55
+ </div>
56
+ </ResizablePanel>
57
+ </ResizablePanelGroup>
58
+ ),
59
+ };
60
+
61
+ export const Vertical: Story = {
62
+ parameters: {
63
+ docs: {
64
+ description: { story: 'Vertical layout, panels stack top to bottom.' },
65
+ },
66
+ },
67
+ render: () => (
68
+ <ResizablePanelGroup
69
+ orientation="vertical"
70
+ className="min-h-64 border-form border-solid border-divider-input"
71
+ >
72
+ <ResizablePanel defaultSize={50}>
73
+ <div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
74
+ Top
75
+ </div>
76
+ </ResizablePanel>
77
+
78
+ <ResizableHandle />
79
+
80
+ <ResizablePanel defaultSize={50}>
81
+ <div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
82
+ Bottom
83
+ </div>
84
+ </ResizablePanel>
85
+ </ResizablePanelGroup>
86
+ ),
87
+ };
88
+
89
+ export const ThreePanels: Story = {
90
+ parameters: {
91
+ docs: {
92
+ description: { story: 'Multiple panels with independent size constraints.' },
93
+ },
94
+ },
95
+ render: () => (
96
+ <ResizablePanelGroup
97
+ orientation="horizontal"
98
+ className="min-h-48 border-form border-solid border-divider-input"
99
+ >
100
+ <ResizablePanel defaultSize={20} minSize={10}>
101
+ <div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
102
+ Sidebar
103
+ </div>
104
+ </ResizablePanel>
105
+
106
+ <ResizableHandle />
107
+
108
+ <ResizablePanel defaultSize={60}>
109
+ <div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
110
+ Main
111
+ </div>
112
+ </ResizablePanel>
113
+
114
+ <ResizableHandle />
115
+
116
+ <ResizablePanel defaultSize={20} minSize={10}>
117
+ <div className="flex h-full items-center justify-center p-gds-4 text-body-secondary text-sm">
118
+ Detail
119
+ </div>
120
+ </ResizablePanel>
121
+ </ResizablePanelGroup>
122
+ ),
123
+ };
@@ -0,0 +1,86 @@
1
+ 'use client';
2
+
3
+ import { type ComponentProps, type PointerEventHandler } from 'react';
4
+
5
+ import { LuGripVertical } from 'react-icons/lu';
6
+ import { Group, Panel, Separator } from 'react-resizable-panels';
7
+
8
+ import { cn } from '../../utils/components';
9
+
10
+ export type ResizablePanelGroupProps = ComponentProps<typeof Group>;
11
+
12
+ // react-resizable-panels pads a separator's drag hit region up to this size
13
+ // (centred on the actual gap between panels) if it's smaller, but defaults to
14
+ // only 10px for a mouse/10px pointer and 20px for touch. Our handle's visible
15
+ // grip icon (ResizableHandle, size-6 = 24px) is bigger than that default, so
16
+ // clicking near its edges misses the real hit region even though it looks
17
+ // like one big button. 24px also matches WCAG 2.5.5/2.5.8's minimum target
18
+ // size guidance, so raise the default for every pointer type, not just to
19
+ // match the icon.
20
+ const RESIZE_TARGET_MINIMUM_SIZE = { coarse: 24, fine: 24 };
21
+
22
+ export const ResizablePanelGroup = ({
23
+ className,
24
+ resizeTargetMinimumSize = RESIZE_TARGET_MINIMUM_SIZE,
25
+ ...props
26
+ }: ResizablePanelGroupProps) => (
27
+ <Group
28
+ data-slot="resizable-panel-group"
29
+ className={cn('cn-resizable-panel-group', className)}
30
+ resizeTargetMinimumSize={resizeTargetMinimumSize}
31
+ {...props}
32
+ />
33
+ );
34
+
35
+ export type ResizablePanelProps = ComponentProps<typeof Panel>;
36
+
37
+ export const ResizablePanel = (props: ResizablePanelProps) => (
38
+ <Panel data-slot="resizable-panel" {...props} />
39
+ );
40
+
41
+ export type ResizableHandleProps = ComponentProps<typeof Separator> & {
42
+ withHandle?: boolean;
43
+ };
44
+
45
+ export const ResizableHandle = ({
46
+ withHandle = true,
47
+ className,
48
+ onPointerDown,
49
+ ...props
50
+ }: ResizableHandleProps) => {
51
+ // Dragging the handle can otherwise select surrounding page text, the
52
+ // pointer doesn't stay perfectly still while dragging, and text selection
53
+ // is a normal side effect of that movement. There's no public drag-state
54
+ // event from react-resizable-panels to hook into, so track it directly:
55
+ // add on pointerdown, remove on pointerup/pointercancel wherever the
56
+ // pointer ends up, dragging routinely moves it off the handle itself.
57
+ const handlePointerDown: PointerEventHandler<HTMLDivElement> = (event) => {
58
+ document.body.classList.add('cn-resizing');
59
+
60
+ const stopResizing = () => {
61
+ document.body.classList.remove('cn-resizing');
62
+ window.removeEventListener('pointerup', stopResizing);
63
+ window.removeEventListener('pointercancel', stopResizing);
64
+ };
65
+
66
+ window.addEventListener('pointerup', stopResizing);
67
+ window.addEventListener('pointercancel', stopResizing);
68
+
69
+ onPointerDown?.(event);
70
+ };
71
+
72
+ return (
73
+ <Separator
74
+ data-slot="resizable-handle"
75
+ className={cn('cn-resizable-handle', className)}
76
+ onPointerDown={handlePointerDown}
77
+ {...props}
78
+ >
79
+ {withHandle ? (
80
+ <div className="cn-resizable-handle-icon">
81
+ <LuGripVertical aria-hidden />
82
+ </div>
83
+ ) : null}
84
+ </Separator>
85
+ );
86
+ };
@@ -0,0 +1,9 @@
1
+ @layer components {
2
+ /* MARK: Scroll area
3
+ Native scrollbars in both directions. min-height: 0 prevents the default
4
+ flex min-height: auto from stopping the area from shrinking inside a flex
5
+ parent. Without it, the area grows to fit content instead of scrolling. */
6
+ .cn-scroll-area {
7
+ @apply overflow-scroll min-h-0;
8
+ }
9
+ }
@@ -0,0 +1,42 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { ScrollArea } from './ScrollArea';
4
+ import { Heading } from '../Heading/Heading';
5
+ import { Separator } from '../Separator/Separator';
6
+
7
+ const meta: Meta<typeof ScrollArea> = {
8
+ title: 'Components/ScrollArea',
9
+ component: ScrollArea,
10
+ tags: ['autodocs'],
11
+ parameters: {
12
+ docs: {
13
+ description: {
14
+ component:
15
+ "A div wrapper that enables native scrollbars when content overflows. Set a constrained `height` or `max-height` via `className`, the area scrolls within those bounds using the browser's native scrollbar, consistent with the rest of the page.",
16
+ },
17
+ },
18
+ },
19
+ };
20
+
21
+ export default meta;
22
+
23
+ type Story = StoryObj<typeof ScrollArea>;
24
+
25
+ const TAGS = Array.from({ length: 50 }, (_, i) => `v1.2.0-beta.${50 - i}`);
26
+
27
+ export const Default: Story = {
28
+ render: () => (
29
+ <ScrollArea className="h-72 w-48 panel">
30
+ <div className="p-gds-2">
31
+ <Heading level="h4">Tags</Heading>
32
+ {TAGS.map((tag) => (
33
+ <div key={tag}>
34
+ <div>{tag}</div>
35
+
36
+ <Separator className="my-2" />
37
+ </div>
38
+ ))}
39
+ </div>
40
+ </ScrollArea>
41
+ ),
42
+ };
@@ -0,0 +1,11 @@
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { cn } from '../../utils/components';
4
+
5
+ export type ScrollAreaProps = ComponentProps<'div'>;
6
+
7
+ export const ScrollArea = ({ className, children, ...props }: ScrollAreaProps) => (
8
+ <div data-slot="scroll-area" className={cn('cn-scroll-area', className)} {...props}>
9
+ {children}
10
+ </div>
11
+ );