@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,15 @@
1
+ import { cn } from '../../utils/components';
2
+
3
+ export type LegendDotProps = {
4
+ color: string;
5
+ className?: string;
6
+ };
7
+
8
+ export const LegendDot = ({ color, className }: LegendDotProps) => (
9
+ <span
10
+ data-slot="legend-dot"
11
+ className={cn('cn-legend-dot', className)}
12
+ style={{ backgroundColor: color }}
13
+ aria-hidden
14
+ />
15
+ );
@@ -0,0 +1,101 @@
1
+ import { useState } from 'react';
2
+
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { LegendPanel } from './LegendPanel';
6
+ import type { LegendOption } from './LegendPanel';
7
+
8
+ const HABITAT_ITEMS: LegendOption[] = [
9
+ { value: 'bogs', label: 'Bogs', color: '#3b82f6' },
10
+ { value: 'dwarf-shrub', label: 'Dwarf shrub heath', color: '#14b8a6' },
11
+ { value: 'fens', label: 'Fens, marsh & swamp', color: '#ec4899' },
12
+ { value: 'littoral', label: 'Littoral sediments', color: '#f97316' },
13
+ { value: 'neutral', label: 'Neutral grassland', color: '#a855f7' },
14
+ ];
15
+
16
+ const meta: Meta<typeof LegendPanel> = {
17
+ title: 'Components/LegendPanel',
18
+ component: LegendPanel,
19
+ tags: ['autodocs'],
20
+ parameters: {
21
+ docs: {
22
+ description: {
23
+ component: `
24
+ Renders a list of \`LegendOption\` items (colour dot + label). This is the shared list rendering used inside \`Legend\`'s popover, and can also be used standalone.
25
+
26
+ Pass \`value\` and \`onSelectItem\` together to switch into single-select interactive mode (radio-group behaviour, one item selected at a time). Omit both for a static, display-only list.
27
+
28
+ \`LegendPanel\` itself has no border or background, that comes from whatever it's nested in (\`Legend\`'s \`PopoverContent\` already supplies it). For standalone use outside a popover, wrap it in a panel-styled container (the \`panel\` utility class, or your own bordered wrapper). \`Card\` is a different pattern now (a top-divider content tile), not a general-purpose bordered box.
29
+
30
+ **Props:**
31
+ - \`items\`: array of \`LegendOption\` objects (\`value\`, \`label\`, \`color\`)
32
+ - \`title\`: optional heading shown above the list
33
+ - \`value\` / \`onSelectItem\`: provide both to make the list selectable
34
+ `.trim(),
35
+ },
36
+ },
37
+ },
38
+ };
39
+
40
+ export default meta;
41
+
42
+ type Story = StoryObj<typeof LegendPanel>;
43
+
44
+ const InteractiveStory = () => {
45
+ const [active, setActive] = useState<string | null>('bogs');
46
+
47
+ return (
48
+ <div className="panel p-gds-3 w-52">
49
+ <LegendPanel
50
+ items={HABITAT_ITEMS}
51
+ title="Habitat types"
52
+ value={active}
53
+ onSelectItem={setActive}
54
+ />
55
+ </div>
56
+ );
57
+ };
58
+
59
+ const NoSelectionStory = () => {
60
+ const [active, setActive] = useState<string | null>(null);
61
+
62
+ return (
63
+ <div className="panel p-gds-3 w-52">
64
+ <LegendPanel
65
+ items={HABITAT_ITEMS}
66
+ title="Habitat types"
67
+ value={active}
68
+ onSelectItem={setActive}
69
+ />
70
+ </div>
71
+ );
72
+ };
73
+
74
+ export const Default: Story = {
75
+ render: () => (
76
+ <div className="panel p-gds-3 w-52">
77
+ <LegendPanel items={HABITAT_ITEMS} title="Habitat types" />
78
+ </div>
79
+ ),
80
+ };
81
+
82
+ export const Interactive: Story = {
83
+ parameters: {
84
+ docs: {
85
+ description: {
86
+ story:
87
+ 'Pass `value` and `onSelectItem` to make the list selectable, one item at a time. The same pattern as `LayerSwitcher`, without a popover.',
88
+ },
89
+ },
90
+ },
91
+ render: () => <InteractiveStory />,
92
+ };
93
+
94
+ export const NoSelection: Story = {
95
+ parameters: {
96
+ docs: {
97
+ description: { story: 'When `value` is `null` no option is shown as selected.' },
98
+ },
99
+ },
100
+ render: () => <NoSelectionStory />,
101
+ };
@@ -0,0 +1,68 @@
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { LegendDot } from './LegendDot';
4
+ import { cn } from '../../utils/components';
5
+
6
+ export type LegendOption = {
7
+ value: string;
8
+ label: string;
9
+ color: string;
10
+ };
11
+
12
+ export type LegendPanelProps = ComponentProps<'div'> & {
13
+ items: LegendOption[];
14
+ title?: string;
15
+ } & (
16
+ | { value?: never; onSelectItem?: never }
17
+ | { value: string | null; onSelectItem: (value: string) => void }
18
+ );
19
+
20
+ export const LegendPanel = ({
21
+ items,
22
+ title,
23
+ value,
24
+ onSelectItem,
25
+ className,
26
+ ...props
27
+ }: LegendPanelProps) => (
28
+ <div data-slot="legend-panel" className={cn('cn-legend-panel', className)} {...props}>
29
+ {title !== undefined ? (
30
+ <p data-slot="legend-title" className="cn-legend-title">
31
+ {title}
32
+ </p>
33
+ ) : null}
34
+
35
+ {onSelectItem !== undefined ? (
36
+ <div
37
+ data-slot="legend-items"
38
+ role="radiogroup"
39
+ aria-label={title ?? 'Legend'}
40
+ className="cn-legend-items"
41
+ >
42
+ {items.map((item) => (
43
+ <button
44
+ key={item.value}
45
+ type="button"
46
+ role="radio"
47
+ aria-checked={item.value === value}
48
+ onClick={() => onSelectItem(item.value)}
49
+ data-slot="legend-option"
50
+ className="cn-legend-option"
51
+ >
52
+ <LegendDot color={item.color} />
53
+ {item.label}
54
+ </button>
55
+ ))}
56
+ </div>
57
+ ) : (
58
+ <ul data-slot="legend-items" className="cn-legend-items">
59
+ {items.map((item) => (
60
+ <li key={item.value} data-slot="legend-item" className="cn-legend-item">
61
+ <LegendDot color={item.color} />
62
+ {item.label}
63
+ </li>
64
+ ))}
65
+ </ul>
66
+ )}
67
+ </div>
68
+ );
@@ -0,0 +1,87 @@
1
+ import { useState } from 'react';
2
+
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { LegendPanel } from './LegendPanel';
6
+ import type { LegendOption } from './LegendPanel';
7
+ import { LegendSkeleton } from './LegendSkeleton';
8
+
9
+ const HABITAT_ITEMS: LegendOption[] = [
10
+ { value: 'bogs', label: 'Bogs', color: '#3b82f6' },
11
+ { value: 'dwarf-shrub', label: 'Dwarf shrub heath', color: '#14b8a6' },
12
+ { value: 'fens', label: 'Fens, marsh & swamp', color: '#ec4899' },
13
+ { value: 'littoral', label: 'Littoral sediments', color: '#f97316' },
14
+ ];
15
+
16
+ const meta: Meta<typeof LegendSkeleton> = {
17
+ title: 'Components/Legend/Skeleton',
18
+ component: LegendSkeleton,
19
+ tags: ['autodocs'],
20
+ parameters: {
21
+ docs: {
22
+ description: {
23
+ component:
24
+ 'Placeholder for a LegendPanel while its items are loading (e.g. fetched from a WMS GetLegendGraphic call). Reuses cn-legend-panel/title/items/item directly, so it lines up exactly with the real panel once data arrives.',
25
+ },
26
+ },
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+
32
+ type Story = StoryObj<typeof LegendSkeleton>;
33
+
34
+ export const Default: Story = {
35
+ render: () => (
36
+ <div className="w-52">
37
+ <LegendSkeleton />
38
+ </div>
39
+ ),
40
+ };
41
+
42
+ export const SideBySide: Story = {
43
+ parameters: {
44
+ docs: {
45
+ description: { story: 'The skeleton and the real panel line up exactly once loaded.' },
46
+ },
47
+ },
48
+ render: () => (
49
+ <div className="flex gap-gds-4">
50
+ <div className="w-52">
51
+ <LegendSkeleton items={HABITAT_ITEMS.length} />
52
+ </div>
53
+
54
+ <div className="w-52">
55
+ <LegendPanel items={HABITAT_ITEMS} title="Legend" />
56
+ </div>
57
+ </div>
58
+ ),
59
+ };
60
+
61
+ const InteractiveSideBySideExample = () => {
62
+ const [active, setActive] = useState<string | null>(HABITAT_ITEMS[0]?.value ?? null);
63
+
64
+ return (
65
+ <div className="flex gap-gds-4">
66
+ <div className="w-52">
67
+ <LegendSkeleton items={HABITAT_ITEMS.length} interactive />
68
+ </div>
69
+
70
+ <div className="w-52">
71
+ <LegendPanel items={HABITAT_ITEMS} title="Legend" value={active} onSelectItem={setActive} />
72
+ </div>
73
+ </div>
74
+ );
75
+ };
76
+
77
+ export const InteractiveSideBySide: Story = {
78
+ parameters: {
79
+ docs: {
80
+ description: {
81
+ story:
82
+ 'Interactive LegendPanel rows (a radiogroup of padded, full-width buttons) are noticeably taller/wider than the plain display rows above, set interactive on the skeleton to match.',
83
+ },
84
+ },
85
+ },
86
+ render: () => <InteractiveSideBySideExample />,
87
+ };
@@ -0,0 +1,73 @@
1
+ import type { ComponentProps } from 'react';
2
+
3
+ import { cn } from '../../utils/components';
4
+ import { Skeleton } from '../Skeleton/Skeleton';
5
+
6
+ // Stable, fixed identifiers rather than the array index, even though these
7
+ // are decorative and never reorder, so an item's identity is never tied to
8
+ // its position at render time.
9
+ const ITEM_KEYS = [
10
+ 'item-1',
11
+ 'item-2',
12
+ 'item-3',
13
+ 'item-4',
14
+ 'item-5',
15
+ 'item-6',
16
+ 'item-7',
17
+ 'item-8',
18
+ ] as const;
19
+
20
+ export type LegendSkeletonProps = ComponentProps<'div'> & {
21
+ /** Explicitly disallow children, a skeleton is a placeholder shape, not a content wrapper. */
22
+ children?: never;
23
+ /** Number of item rows to show. Defaults to 4. */
24
+ items?: number;
25
+ /** Shows a title bar, matching LegendPanel's own optional title. Defaults to true. */
26
+ showTitle?: boolean;
27
+ /** Matches LegendPanel's interactive (radiogroup) mode: rows are padded, full-width
28
+ * cn-legend-option buttons rather than plain cn-legend-item rows, which is
29
+ * noticeably taller/wider, so the skeleton needs to reserve that same space. */
30
+ interactive?: boolean;
31
+ };
32
+
33
+ export const LegendSkeleton = ({
34
+ className,
35
+ items = 4,
36
+ showTitle = true,
37
+ interactive = false,
38
+ ...props
39
+ }: LegendSkeletonProps) => {
40
+ const itemKeys = ITEM_KEYS.slice(0, items);
41
+
42
+ return (
43
+ <div aria-hidden className={cn('cn-legend-panel', className)} {...props}>
44
+ {showTitle ? (
45
+ <div className="cn-legend-title">
46
+ <Skeleton className="cn-legend-skeleton-title" />
47
+ </div>
48
+ ) : null}
49
+
50
+ {interactive ? (
51
+ <div className="cn-legend-items">
52
+ {itemKeys.map((key) => (
53
+ <div key={key} className="cn-legend-option">
54
+ <Skeleton className="cn-legend-skeleton-dot" />
55
+
56
+ <Skeleton className="cn-legend-skeleton-label" />
57
+ </div>
58
+ ))}
59
+ </div>
60
+ ) : (
61
+ <ul className="cn-legend-items">
62
+ {itemKeys.map((key) => (
63
+ <li key={key} className="cn-legend-item">
64
+ <Skeleton className="cn-legend-skeleton-dot" />
65
+
66
+ <Skeleton className="cn-legend-skeleton-label" />
67
+ </li>
68
+ ))}
69
+ </ul>
70
+ )}
71
+ </div>
72
+ );
73
+ };
@@ -0,0 +1,8 @@
1
+ @layer components {
2
+ /* MARK: Legend dot
3
+ White ring separates the dot from the accent-coloured selected-option
4
+ background, ensuring visibility regardless of the dot colour. */
5
+ .cn-select-option-selected .cn-legend-dot {
6
+ outline: var(--focus-ring-width) solid var(--color-panel);
7
+ }
8
+ }
@@ -0,0 +1,80 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { LegendSelect, type LegendOption } from './LegendSelect';
4
+ import { ErrorText } from '../ErrorText/ErrorText';
5
+ import { FormGroup } from '../Form/FormGroup';
6
+ import { Label } from '../Label/Label';
7
+
8
+ const meta: Meta<typeof LegendSelect> = {
9
+ title: 'Components/LegendSelect',
10
+ component: LegendSelect,
11
+ tags: ['autodocs'],
12
+ parameters: {
13
+ docs: {
14
+ description: {
15
+ component:
16
+ 'A Select variant where each option carries a `color` field rendered as a filled circle before the label. Useful for map legend filters, status indicators, and any dataset where colour encodes meaning. Extends `Select` directly, so all Select props (clearable, disabled, invalid, async, etc.) work without extra wiring. Pass `formatOptionLabel` if you need to override the rendering.',
17
+ },
18
+ },
19
+ },
20
+ };
21
+
22
+ export default meta;
23
+
24
+ type Story = StoryObj<typeof LegendSelect>;
25
+
26
+ const LAND_COVER_OPTIONS: LegendOption[] = [
27
+ { value: 'woodland', label: 'Woodland', color: '#2d6a4f' },
28
+ { value: 'grassland', label: 'Grassland', color: '#95d5b2' },
29
+ { value: 'arable', label: 'Arable land', color: '#e9c46a' },
30
+ { value: 'wetland', label: 'Wetland', color: '#4895ef' },
31
+ { value: 'urban', label: 'Urban / developed', color: '#adb5bd' },
32
+ ];
33
+
34
+ export const Default: Story = {
35
+ args: {
36
+ options: LAND_COVER_OPTIONS,
37
+ placeholder: 'Select land cover type...',
38
+ },
39
+ };
40
+
41
+ export const WithDefault: Story = {
42
+ parameters: {
43
+ docs: {
44
+ description: {
45
+ story:
46
+ 'The colour dot appears in both the dropdown and the selected-value slot in the control.',
47
+ },
48
+ },
49
+ },
50
+ args: {
51
+ options: LAND_COVER_OPTIONS,
52
+ defaultValue: LAND_COVER_OPTIONS[0],
53
+ },
54
+ };
55
+
56
+ export const Clearable: Story = {
57
+ args: {
58
+ options: LAND_COVER_OPTIONS,
59
+ defaultValue: LAND_COVER_OPTIONS[2],
60
+ isClearable: true,
61
+ },
62
+ };
63
+
64
+ export const Invalid: Story = {
65
+ render: () => (
66
+ <FormGroup invalid>
67
+ <Label htmlFor="legend-invalid">Land cover type</Label>
68
+
69
+ <ErrorText id="legend-invalid-error">Select a land cover type</ErrorText>
70
+
71
+ <LegendSelect
72
+ inputId="legend-invalid"
73
+ aria-describedby="legend-invalid-error"
74
+ options={LAND_COVER_OPTIONS}
75
+ placeholder="Select land cover type…"
76
+ invalid
77
+ />
78
+ </FormGroup>
79
+ ),
80
+ };
@@ -0,0 +1,26 @@
1
+ import type { GroupBase } from 'react-select';
2
+
3
+ import { LegendDot } from '../Legend/LegendDot';
4
+ import type { LegendOption } from '../Legend/LegendPanel';
5
+ import { Select, type SelectProps } from '../Select/Select';
6
+
7
+ export type { LegendOption };
8
+
9
+ export type LegendSelectProps<IsMulti extends boolean = false> = Omit<
10
+ SelectProps<LegendOption, IsMulti, GroupBase<LegendOption>>,
11
+ 'formatOptionLabel'
12
+ >;
13
+
14
+ export const LegendSelect = <IsMulti extends boolean = false>(
15
+ props: LegendSelectProps<IsMulti>,
16
+ ) => (
17
+ <Select<LegendOption, IsMulti>
18
+ {...props}
19
+ formatOptionLabel={(option) => (
20
+ <span className="flex items-center gap-gds-1">
21
+ <LegendDot color={option.color} />
22
+ {option.label}
23
+ </span>
24
+ )}
25
+ />
26
+ );
@@ -0,0 +1,98 @@
1
+ /* MARK: Appearances
2
+ Defined as utilities so ButtonLink can @apply them without duplication. */
3
+
4
+ @utility cn-link-appearance-primary {
5
+ @apply text-link;
6
+
7
+ &:visited {
8
+ @apply text-link-visited;
9
+ }
10
+ &:hover:not(:disabled) {
11
+ @apply text-link-hover;
12
+ }
13
+ &:active:not(:disabled) {
14
+ @apply text-link-active;
15
+ }
16
+ &:focus:not(:disabled) {
17
+ @apply text-on-focus;
18
+ }
19
+ }
20
+
21
+ @utility cn-link-appearance-error {
22
+ color: var(--color-error);
23
+
24
+ &:visited {
25
+ color: var(--color-error);
26
+ }
27
+ &:hover:not(:disabled) {
28
+ color: color-mix(in srgb, var(--color-error) 80%, black);
29
+ }
30
+ &:active:not(:disabled) {
31
+ color: color-mix(in srgb, var(--color-error) 80%, black);
32
+ }
33
+ &:focus:not(:disabled) {
34
+ @apply text-on-focus;
35
+ }
36
+ }
37
+
38
+ @utility cn-link-appearance-inverse {
39
+ @apply text-white;
40
+
41
+ &:visited {
42
+ @apply text-white;
43
+ }
44
+ &:hover:not(:disabled) {
45
+ @apply text-white;
46
+ }
47
+ &:focus:not(:disabled) {
48
+ @apply text-on-focus;
49
+ }
50
+ }
51
+
52
+ @utility cn-link-appearance-contrast {
53
+ @apply text-body;
54
+
55
+ &:visited {
56
+ @apply text-body;
57
+ }
58
+ &:hover:not(:disabled),
59
+ &:active:not(:disabled) {
60
+ @apply text-body;
61
+ }
62
+ &:focus:not(:disabled) {
63
+ @apply text-on-focus;
64
+ }
65
+ }
66
+
67
+ @layer components {
68
+ /*
69
+ MARK: Link
70
+ GDS link pattern. Underline always visible, thicker on hover,
71
+ yellow highlight box on focus. Box shadow creates lines above
72
+ and below text (not a ring). Visited uses distinct purple.
73
+ */
74
+ .cn-link {
75
+ @apply w-fit cn-link-decoration;
76
+
77
+ &:hover {
78
+ @apply cn-link-hover-decoration;
79
+ }
80
+
81
+ /* GDS focus: yellow box with black underline shadow */
82
+ &:focus {
83
+ @apply text-on-focus bg-focus no-underline outline-focus outline-transparent;
84
+ /* Top shadow extends focus box above text. Bottom shadow replaces underline */
85
+ box-shadow:
86
+ 0 -2px var(--color-focus),
87
+ 0 4px var(--color-on-focus);
88
+ }
89
+ }
90
+
91
+ /* MARK: Expand variant
92
+ Overrides the base w-fit: content (e.g. a title and a trailing chevron)
93
+ spreads across the full width of whatever the link sits in, rather than
94
+ the icon sitting flush against the text. */
95
+ .cn-link-variant-expand {
96
+ @apply flex w-full items-center justify-between gap-gds-2;
97
+ }
98
+ }
@@ -0,0 +1,70 @@
1
+ import { LuChevronRight } from 'react-icons/lu';
2
+
3
+ import type { Meta, StoryObj } from '@storybook/react-vite';
4
+
5
+ import { Link } from './Link';
6
+
7
+ const meta: Meta<typeof Link> = {
8
+ title: 'Components/Link',
9
+ component: Link,
10
+ tags: ['autodocs'],
11
+ parameters: {
12
+ docs: {
13
+ description: {
14
+ component: 'Use Link for accessible anchor navigation with multiple visual variants.',
15
+ },
16
+ },
17
+ },
18
+ argTypes: {
19
+ appearance: {
20
+ control: { type: 'radio' },
21
+ options: ['primary', 'inverse', 'contrast'],
22
+ },
23
+ variant: {
24
+ control: { type: 'radio' },
25
+ options: ['default', 'icon', 'expand'],
26
+ },
27
+ href: { control: 'text' },
28
+ children: { control: 'text' },
29
+ },
30
+ args: {
31
+ appearance: 'primary',
32
+ variant: 'default',
33
+ href: '#',
34
+ children: 'Story link',
35
+ },
36
+ };
37
+
38
+ export default meta;
39
+
40
+ type Story = StoryObj<typeof Link>;
41
+
42
+ export const Primary: Story = {
43
+ args: {
44
+ appearance: 'primary',
45
+ },
46
+ };
47
+
48
+ export const Inverse: Story = {
49
+ args: {
50
+ appearance: 'inverse',
51
+ },
52
+ };
53
+
54
+ export const Contrast: Story = {
55
+ args: {
56
+ appearance: 'contrast',
57
+ },
58
+ };
59
+
60
+ export const Expanded: Story = {
61
+ args: {
62
+ variant: 'expand',
63
+ },
64
+ render: (args) => (
65
+ <Link {...args}>
66
+ Explore
67
+ <LuChevronRight aria-hidden className="size-4" strokeWidth={2.5} />
68
+ </Link>
69
+ ),
70
+ };
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+
3
+ import type { ComponentProps } from 'react';
4
+
5
+ import { Slot } from 'radix-ui';
6
+
7
+ import { cn } from '../../utils/components';
8
+
9
+ const LINK_APPEARANCES = {
10
+ primary: 'cn-link-appearance-primary',
11
+ /**
12
+ * Typically reserved for use within an `Alert` component with the error appearance.
13
+ */
14
+ error: 'cn-link-appearance-error',
15
+ inverse: 'cn-link-appearance-inverse',
16
+ contrast: 'cn-link-appearance-contrast',
17
+ } as const;
18
+
19
+ export const LINK_VARIANTS = {
20
+ default: 'cn-link-variant-default',
21
+ icon: 'cn-link-variant-icon',
22
+ /** Full width, content spread apart, e.g. a title with a trailing chevron pushed to the end. */
23
+ expand: 'cn-link-variant-expand',
24
+ };
25
+
26
+ export type LinkAppearance = keyof typeof LINK_APPEARANCES;
27
+ export type LinkVariant = keyof typeof LINK_VARIANTS;
28
+
29
+ export type LinkProps = ComponentProps<'a'> & {
30
+ asChild?: boolean;
31
+ appearance?: LinkAppearance;
32
+ variant?: LinkVariant;
33
+ };
34
+
35
+ export const Link = ({
36
+ className,
37
+ asChild = false,
38
+ appearance = 'primary',
39
+ variant = 'default',
40
+ ...props
41
+ }: LinkProps) => {
42
+ const Comp = asChild ? Slot.Root : 'a';
43
+
44
+ return (
45
+ <Comp
46
+ {...props}
47
+ data-slot="link"
48
+ data-variant={variant}
49
+ className={cn('cn-link', LINK_APPEARANCES[appearance], LINK_VARIANTS[variant], className)}
50
+ />
51
+ );
52
+ };