@scottish-government/designsystem-react 0.13.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/.storybook/sgdsArgTypes.ts +23 -0
  2. package/CHANGELOG.md +23 -0
  3. package/LICENSE.txt +21 -0
  4. package/README.md +3 -0
  5. package/dist/common/AbstractNotificationBanner/AbstractNotificationBanner.d.ts +3 -2
  6. package/dist/common/AbstractNotificationBanner/AbstractNotificationBanner.jsx +8 -7
  7. package/dist/common/AbstractNotificationBanner/types.d.ts +13 -0
  8. package/dist/common/AbstractNotificationBanner/types.js +2 -0
  9. package/dist/common/ActionLink/ActionLink.d.ts +2 -1
  10. package/dist/common/ActionLink/types.d.ts +7 -0
  11. package/dist/common/ActionLink/types.js +2 -0
  12. package/dist/common/ConditionalWrapper/ConditionalWrapper.d.ts +2 -1
  13. package/dist/common/ConditionalWrapper/ConditionalWrapper.jsx +4 -2
  14. package/dist/common/ConditionalWrapper/types.d.ts +4 -0
  15. package/dist/common/ConditionalWrapper/types.js +2 -0
  16. package/dist/common/FileIcon/FileIcon.d.ts +2 -1
  17. package/dist/common/FileIcon/types.d.ts +6 -0
  18. package/dist/common/FileIcon/types.js +2 -0
  19. package/dist/common/HintText/HintText.d.ts +2 -1
  20. package/dist/common/HintText/HintText.jsx +3 -3
  21. package/dist/common/HintText/types.d.ts +4 -0
  22. package/dist/common/HintText/types.js +2 -0
  23. package/dist/common/Icon/Icon.d.ts +2 -1
  24. package/dist/common/Icon/Icon.jsx +3 -2
  25. package/dist/common/Icon/types.d.ts +8 -0
  26. package/dist/common/Icon/types.js +2 -0
  27. package/dist/common/ScreenReaderText/ScreenReaderText.d.ts +1 -1
  28. package/dist/common/WrapperTag/WrapperTag.d.ts +385 -1
  29. package/dist/common/WrapperTag/WrapperTag.jsx +5 -2
  30. package/dist/common/WrapperTag/types.d.ts +3 -0
  31. package/dist/common/WrapperTag/types.js +2 -0
  32. package/dist/components/Accordion/Accordion.d.ts +3 -2
  33. package/dist/components/Accordion/Accordion.jsx +12 -11
  34. package/dist/components/Accordion/types.d.ts +11 -0
  35. package/dist/components/Accordion/types.js +2 -0
  36. package/dist/components/AspectBox/AspectBox.d.ts +2 -1
  37. package/dist/components/AspectBox/AspectBox.jsx +3 -9
  38. package/dist/components/AspectBox/types.d.ts +3 -0
  39. package/dist/components/AspectBox/types.js +2 -0
  40. package/dist/components/BackToTop/BackToTop.d.ts +2 -1
  41. package/dist/components/BackToTop/BackToTop.jsx +4 -3
  42. package/dist/components/BackToTop/types.d.ts +3 -0
  43. package/dist/components/BackToTop/types.js +2 -0
  44. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +3 -2
  45. package/dist/components/Breadcrumbs/Breadcrumbs.jsx +6 -2
  46. package/dist/components/Breadcrumbs/types.d.ts +5 -0
  47. package/dist/components/Breadcrumbs/types.js +2 -0
  48. package/dist/components/Button/Button.d.ts +2 -1
  49. package/dist/components/Button/Button.jsx +4 -3
  50. package/dist/components/Button/ButtonGroup.d.ts +2 -1
  51. package/dist/components/Button/ButtonGroup.jsx +9 -4
  52. package/dist/components/Button/types.d.ts +20 -0
  53. package/dist/components/Button/types.js +2 -0
  54. package/dist/components/Card/Card.d.ts +34 -0
  55. package/dist/components/Card/Card.jsx +118 -0
  56. package/dist/components/Card/index.d.ts +1 -0
  57. package/dist/components/Card/index.js +8 -0
  58. package/dist/components/Card/types.d.ts +26 -0
  59. package/dist/components/Card/types.js +2 -0
  60. package/dist/components/CategoryItem/CategoryItem.d.ts +2 -1
  61. package/dist/components/CategoryItem/CategoryItem.jsx +8 -5
  62. package/dist/components/CategoryItem/types.d.ts +9 -0
  63. package/dist/components/CategoryItem/types.js +2 -0
  64. package/dist/components/CategoryList/CategoryList.d.ts +2 -1
  65. package/dist/components/CategoryList/CategoryList.jsx +3 -2
  66. package/dist/components/CategoryList/types.d.ts +5 -0
  67. package/dist/components/CategoryList/types.js +2 -0
  68. package/dist/components/Checkbox/Checkbox.d.ts +2 -1
  69. package/dist/components/Checkbox/Checkbox.jsx +4 -3
  70. package/dist/components/Checkbox/CheckboxGroup.d.ts +2 -1
  71. package/dist/components/Checkbox/CheckboxGroup.jsx +5 -4
  72. package/dist/components/Checkbox/types.d.ts +7 -0
  73. package/dist/components/Checkbox/types.js +2 -0
  74. package/dist/components/ConfirmationMessage/ConfirmationMessage.d.ts +2 -1
  75. package/dist/components/ConfirmationMessage/ConfirmationMessage.jsx +3 -2
  76. package/dist/components/ConfirmationMessage/types.d.ts +6 -0
  77. package/dist/components/ConfirmationMessage/types.js +2 -0
  78. package/dist/components/ContentsNav/ContentsNav.d.ts +3 -2
  79. package/dist/components/ContentsNav/ContentsNav.jsx +7 -5
  80. package/dist/components/ContentsNav/types.d.ts +9 -0
  81. package/dist/components/ContentsNav/types.js +2 -0
  82. package/dist/components/CookieBanner/CookieBanner.d.ts +3 -2
  83. package/dist/components/CookieBanner/CookieBanner.jsx +13 -3
  84. package/dist/components/DatePicker/DatePicker.d.ts +2 -1
  85. package/dist/components/DatePicker/DatePicker.jsx +7 -7
  86. package/dist/components/DatePicker/types.d.ts +18 -0
  87. package/dist/components/DatePicker/types.js +2 -0
  88. package/dist/components/Details/Details.d.ts +2 -1
  89. package/dist/components/Details/Details.jsx +6 -2
  90. package/dist/components/Details/types.d.ts +4 -0
  91. package/dist/components/Details/types.js +2 -0
  92. package/dist/components/ErrorMessage/ErrorMessage.d.ts +2 -1
  93. package/dist/components/ErrorMessage/ErrorMessage.jsx +6 -2
  94. package/dist/components/ErrorMessage/types.d.ts +3 -0
  95. package/dist/components/ErrorMessage/types.js +2 -0
  96. package/dist/components/ErrorSummary/ErrorSummary.d.ts +3 -2
  97. package/dist/components/ErrorSummary/ErrorSummary.jsx +6 -2
  98. package/dist/components/ErrorSummary/types.d.ts +8 -0
  99. package/dist/components/ErrorSummary/types.js +2 -0
  100. package/dist/components/FeatureHeader/FeatureHeader.d.ts +7 -5
  101. package/dist/components/FeatureHeader/FeatureHeader.jsx +87 -50
  102. package/dist/components/FeatureHeader/types.d.ts +12 -0
  103. package/dist/components/FeatureHeader/types.js +2 -0
  104. package/dist/components/FileDownload/FileDownload.d.ts +2 -1
  105. package/dist/components/FileDownload/FileDownload.jsx +4 -3
  106. package/dist/components/FileDownload/types.d.ts +10 -0
  107. package/dist/components/FileDownload/types.js +2 -0
  108. package/dist/components/HideThisPage/HideThisPage.d.ts +2 -1
  109. package/dist/components/HideThisPage/HideThisPage.jsx +5 -4
  110. package/dist/components/HideThisPage/types.d.ts +3 -0
  111. package/dist/components/HideThisPage/types.js +2 -0
  112. package/dist/components/InsetText/InsetText.d.ts +1 -1
  113. package/dist/components/InsetText/InsetText.jsx +6 -2
  114. package/dist/components/NotificationBanner/NotificationBanner.d.ts +3 -2
  115. package/dist/components/NotificationBanner/NotificationBanner.jsx +5 -4
  116. package/dist/components/NotificationPanel/NotificationPanel.d.ts +2 -1
  117. package/dist/components/NotificationPanel/NotificationPanel.jsx +3 -2
  118. package/dist/components/NotificationPanel/types.d.ts +6 -0
  119. package/dist/components/NotificationPanel/types.js +2 -0
  120. package/dist/components/NotificationTag/NotificationTag.d.ts +6 -0
  121. package/dist/components/NotificationTag/NotificationTag.jsx +20 -0
  122. package/dist/components/NotificationTag/index.d.ts +1 -0
  123. package/dist/components/NotificationTag/index.js +8 -0
  124. package/dist/components/NotificationTag/types.d.ts +5 -0
  125. package/dist/components/NotificationTag/types.js +2 -0
  126. package/dist/components/PageHeader/PageHeader.d.ts +2 -1
  127. package/dist/components/PageHeader/PageHeader.jsx +6 -2
  128. package/dist/components/PageHeader/types.d.ts +5 -0
  129. package/dist/components/PageHeader/types.js +2 -0
  130. package/dist/components/PageMetadata/PageMetadata.d.ts +3 -2
  131. package/dist/components/PageMetadata/PageMetadata.jsx +8 -4
  132. package/dist/components/PageMetadata/types.d.ts +6 -0
  133. package/dist/components/PageMetadata/types.js +2 -0
  134. package/dist/components/Pagination/Pagination.d.ts +3 -2
  135. package/dist/components/Pagination/Pagination.jsx +7 -5
  136. package/dist/components/Pagination/types.d.ts +18 -0
  137. package/dist/components/Pagination/types.js +2 -0
  138. package/dist/components/PhaseBanner/PhaseBanner.d.ts +2 -1
  139. package/dist/components/PhaseBanner/PhaseBanner.jsx +3 -2
  140. package/dist/components/PhaseBanner/types.d.ts +3 -0
  141. package/dist/components/PhaseBanner/types.js +2 -0
  142. package/dist/components/Question/Question.d.ts +2 -1
  143. package/dist/components/Question/Question.jsx +4 -3
  144. package/dist/components/Question/types.d.ts +9 -0
  145. package/dist/components/Question/types.js +2 -0
  146. package/dist/components/RadioButton/RadioButton.d.ts +2 -1
  147. package/dist/components/RadioButton/RadioButton.jsx +4 -3
  148. package/dist/components/RadioButton/RadioGroup.d.ts +2 -1
  149. package/dist/components/RadioButton/RadioGroup.jsx +3 -2
  150. package/dist/components/RadioButton/types.d.ts +10 -0
  151. package/dist/components/RadioButton/types.js +2 -0
  152. package/dist/components/SearchFacets/SearchFacets.d.ts +4 -3
  153. package/dist/components/SearchFacets/SearchFacets.jsx +3 -2
  154. package/dist/components/SearchFacets/types.d.ts +12 -0
  155. package/dist/components/SearchFacets/types.js +2 -0
  156. package/dist/components/SearchFilters/SearchFilters.d.ts +5 -4
  157. package/dist/components/SearchFilters/types.d.ts +12 -0
  158. package/dist/components/SearchFilters/types.js +2 -0
  159. package/dist/components/SearchResult/SearchResult.d.ts +8 -11
  160. package/dist/components/SearchResult/SearchResult.jsx +6 -7
  161. package/dist/components/SearchResult/types.d.ts +11 -0
  162. package/dist/components/SearchResult/types.js +2 -0
  163. package/dist/components/SearchSort/SearchSort.d.ts +2 -1
  164. package/dist/components/SearchSort/SearchSort.jsx +3 -2
  165. package/dist/components/SearchSort/types.d.ts +7 -0
  166. package/dist/components/SearchSort/types.js +2 -0
  167. package/dist/components/Select/Select.d.ts +2 -1
  168. package/dist/components/Select/Select.jsx +4 -3
  169. package/dist/components/Select/types.d.ts +6 -0
  170. package/dist/components/Select/types.js +2 -0
  171. package/dist/components/SequentialNavigation/SequentialNavigation.d.ts +4 -3
  172. package/dist/components/SequentialNavigation/SequentialNavigation.jsx +10 -6
  173. package/dist/components/SequentialNavigation/types.d.ts +10 -0
  174. package/dist/components/SequentialNavigation/types.js +2 -0
  175. package/dist/components/SideNavigation/SideNavigation.d.ts +4 -3
  176. package/dist/components/SideNavigation/SideNavigation.jsx +10 -7
  177. package/dist/components/SideNavigation/types.d.ts +13 -0
  178. package/dist/components/SideNavigation/types.js +2 -0
  179. package/dist/components/SiteFooter/SiteFooter.d.ts +6 -5
  180. package/dist/components/SiteFooter/SiteFooter.jsx +4 -3
  181. package/dist/components/SiteFooter/types.d.ts +16 -0
  182. package/dist/components/SiteFooter/types.js +2 -0
  183. package/dist/components/SiteHeader/SiteHeader.d.ts +6 -5
  184. package/dist/components/SiteHeader/SiteHeader.jsx +1 -1
  185. package/dist/components/SiteHeader/types.d.ts +20 -0
  186. package/dist/components/SiteHeader/types.js +2 -0
  187. package/dist/components/SiteNavigation/SiteNavigation.d.ts +3 -2
  188. package/dist/components/SiteNavigation/SiteNavigation.jsx +12 -4
  189. package/dist/components/SiteNavigation/types.d.ts +9 -0
  190. package/dist/components/SiteNavigation/types.js +2 -0
  191. package/dist/components/SiteSearch/SiteSearch.d.ts +2 -1
  192. package/dist/components/SiteSearch/SiteSearch.jsx +12 -11
  193. package/dist/components/SiteSearch/types.d.ts +17 -0
  194. package/dist/components/SiteSearch/types.js +2 -0
  195. package/dist/components/SkipLinks/SkipLinks.d.ts +3 -2
  196. package/dist/components/SkipLinks/SkipLinks.jsx +4 -4
  197. package/dist/components/SkipLinks/types.d.ts +8 -0
  198. package/dist/components/SkipLinks/types.js +2 -0
  199. package/dist/components/SummaryCard/SummaryCard.d.ts +4 -2
  200. package/dist/components/SummaryCard/SummaryCard.jsx +5 -4
  201. package/dist/components/SummaryCard/types.d.ts +5 -0
  202. package/dist/components/SummaryCard/types.js +2 -0
  203. package/dist/components/SummaryList/SummaryList.d.ts +6 -4
  204. package/dist/components/SummaryList/SummaryList.jsx +5 -4
  205. package/dist/components/SummaryList/types.d.ts +6 -0
  206. package/dist/components/SummaryList/types.js +2 -0
  207. package/dist/components/Table/Table.d.ts +2 -1
  208. package/dist/components/Table/Table.jsx +5 -4
  209. package/dist/components/Table/types.d.ts +6 -0
  210. package/dist/components/Table/types.js +2 -0
  211. package/dist/components/Tabs/Tabs.d.ts +3 -2
  212. package/dist/components/Tabs/Tabs.jsx +6 -5
  213. package/dist/components/Tabs/types.d.ts +16 -0
  214. package/dist/components/Tabs/types.js +2 -0
  215. package/dist/components/Tag/Tag.d.ts +2 -1
  216. package/dist/components/Tag/Tag.jsx +7 -3
  217. package/dist/components/Tag/types.d.ts +4 -0
  218. package/dist/components/Tag/types.js +2 -0
  219. package/dist/components/TaskList/TaskList.d.ts +4 -3
  220. package/dist/components/TaskList/TaskList.jsx +11 -8
  221. package/dist/components/TaskList/types.d.ts +17 -0
  222. package/dist/components/TaskList/types.js +2 -0
  223. package/dist/components/TextInput/TextInput.d.ts +2 -1
  224. package/dist/components/TextInput/TextInput.jsx +10 -9
  225. package/dist/components/TextInput/types.d.ts +11 -0
  226. package/dist/components/TextInput/types.js +2 -0
  227. package/dist/components/Textarea/Textarea.d.ts +2 -1
  228. package/dist/components/Textarea/Textarea.jsx +6 -5
  229. package/dist/components/WarningText/WarningText.d.ts +1 -1
  230. package/dist/components/WarningText/WarningText.jsx +6 -2
  231. package/dist/hooks/useTracking/useTracking.js +0 -1
  232. package/package.json +5 -3
  233. package/src/common/AbstractNotificationBanner/AbstractNotificationBanner.tsx +8 -6
  234. package/src/common/Icon/Icon.tsx +3 -2
  235. package/src/components/Accordion/Accordion.test.tsx +11 -0
  236. package/src/components/Accordion/Accordion.tsx +11 -10
  237. package/src/components/AspectBox/AspectBox.tsx +4 -11
  238. package/src/components/BackToTop/BackToTop.test.tsx +21 -1
  239. package/src/components/BackToTop/BackToTop.tsx +4 -3
  240. package/src/components/Breadcrumbs/Breadcrumbs.tsx +3 -2
  241. package/src/components/Button/Button.tsx +4 -3
  242. package/src/components/Button/ButtonGroup.stories.tsx +22 -0
  243. package/src/components/Button/ButtonGroup.test.tsx +21 -0
  244. package/src/components/Button/ButtonGroup.tsx +6 -3
  245. package/src/components/Button/types.ts +1 -0
  246. package/src/components/Card/Card.stories.tsx +446 -0
  247. package/src/components/Card/Card.test.tsx +282 -0
  248. package/src/components/Card/Card.tsx +208 -0
  249. package/src/components/Card/index.ts +1 -0
  250. package/src/components/Card/types.ts +30 -0
  251. package/src/components/CategoryItem/CategoryItem.tsx +3 -2
  252. package/src/components/CategoryList/CategoryList.tsx +3 -2
  253. package/src/components/Checkbox/Checkbox.test.tsx +30 -0
  254. package/src/components/Checkbox/Checkbox.tsx +3 -3
  255. package/src/components/Checkbox/CheckboxGroup.test.tsx +10 -0
  256. package/src/components/Checkbox/CheckboxGroup.tsx +5 -4
  257. package/src/components/ConfirmationMessage/ConfirmationMessage.tsx +3 -2
  258. package/src/components/ContentsNav/ContentsNav.tsx +8 -5
  259. package/src/components/CookieBanner/CookieBanner.test.tsx +18 -1
  260. package/src/components/CookieBanner/CookieBanner.tsx +4 -3
  261. package/src/components/DatePicker/DatePicker.test.tsx +52 -14
  262. package/src/components/DatePicker/DatePicker.tsx +6 -7
  263. package/src/components/DatePicker/types.ts +2 -3
  264. package/src/components/Details/Details.tsx +3 -2
  265. package/src/components/ErrorMessage/ErrorMessage.tsx +3 -2
  266. package/src/components/ErrorSummary/ErrorSummary.tsx +3 -2
  267. package/src/components/FeatureHeader/FeatureHeader.stories.tsx +223 -0
  268. package/src/components/FeatureHeader/FeatureHeader.test.tsx +298 -0
  269. package/src/components/FeatureHeader/FeatureHeader.tsx +119 -0
  270. package/src/components/FeatureHeader/index.ts +1 -0
  271. package/src/components/FeatureHeader/types.ts +13 -0
  272. package/src/components/FileDownload/FileDownload.test.tsx +1 -5
  273. package/src/components/FileDownload/FileDownload.tsx +4 -3
  274. package/src/components/HideThisPage/HideThisPage.test.tsx +12 -1
  275. package/src/components/HideThisPage/HideThisPage.tsx +5 -4
  276. package/src/components/InsetText/InsetText.tsx +4 -2
  277. package/src/components/NotificationBanner/NotificationBanner.test.tsx +10 -0
  278. package/src/components/NotificationBanner/NotificationBanner.tsx +4 -3
  279. package/src/components/NotificationPanel/NotificationPanel.tsx +3 -2
  280. package/src/components/NotificationTag/NotificationTag.stories.tsx +68 -0
  281. package/src/components/NotificationTag/NotificationTag.test.tsx +70 -0
  282. package/src/components/NotificationTag/NotificationTag.tsx +32 -0
  283. package/src/components/NotificationTag/index.ts +1 -0
  284. package/src/components/NotificationTag/types.ts +5 -0
  285. package/src/components/PageHeader/PageHeader.tsx +3 -2
  286. package/src/components/PageMetadata/PageMetadata.tsx +5 -4
  287. package/src/components/Pagination/Pagination.test.tsx +20 -0
  288. package/src/components/Pagination/Pagination.tsx +7 -5
  289. package/src/components/PhaseBanner/PhaseBanner.tsx +3 -2
  290. package/src/components/Question/Question.tsx +3 -2
  291. package/src/components/Question/types.ts +2 -2
  292. package/src/components/RadioButton/RadioButton.test.tsx +30 -0
  293. package/src/components/RadioButton/RadioButton.tsx +3 -3
  294. package/src/components/RadioButton/RadioGroup.tsx +3 -2
  295. package/src/components/SearchFacets/SearchFacets.stories.tsx +1 -1
  296. package/src/components/SearchFacets/SearchFacets.tsx +3 -2
  297. package/src/components/SearchResult/SearchResult.stories.tsx +14 -13
  298. package/src/components/SearchResult/SearchResult.test.tsx +5 -4
  299. package/src/components/SearchResult/SearchResult.tsx +6 -7
  300. package/src/components/SearchSort/SearchSort.tsx +3 -2
  301. package/src/components/Select/Select.test.tsx +42 -0
  302. package/src/components/Select/Select.tsx +3 -2
  303. package/src/components/SequentialNavigation/SequentialNavigation.tsx +7 -6
  304. package/src/components/SideNavigation/SideNavigation.test.tsx +24 -0
  305. package/src/components/SideNavigation/SideNavigation.tsx +10 -7
  306. package/src/components/SiteFooter/SiteFooter.tsx +3 -2
  307. package/src/components/SiteHeader/SiteHeader.test.tsx +29 -16
  308. package/src/components/SiteHeader/SiteHeader.tsx +1 -1
  309. package/src/components/SiteNavigation/SiteNavigation.test.tsx +16 -1
  310. package/src/components/SiteNavigation/SiteNavigation.tsx +9 -4
  311. package/src/components/SiteSearch/SiteSearch.test.tsx +22 -0
  312. package/src/components/SiteSearch/SiteSearch.tsx +10 -9
  313. package/src/components/SiteSearch/types.ts +7 -1
  314. package/src/components/SkipLinks/SkipLinks.tsx +4 -4
  315. package/src/components/SummaryCard/SummaryCard.tsx +3 -2
  316. package/src/components/SummaryList/SummaryList.tsx +3 -3
  317. package/src/components/Table/Table.test.tsx +36 -0
  318. package/src/components/Table/Table.tsx +5 -4
  319. package/src/components/Tabs/Tabs.test.tsx +14 -0
  320. package/src/components/Tabs/Tabs.tsx +6 -5
  321. package/src/components/Tag/Tag.tsx +4 -3
  322. package/src/components/TaskList/TaskList.tsx +5 -4
  323. package/src/components/TextInput/TextInput.test.tsx +53 -1
  324. package/src/components/TextInput/TextInput.tsx +12 -7
  325. package/src/components/Textarea/Textarea.test.tsx +53 -1
  326. package/src/components/Textarea/Textarea.tsx +5 -4
  327. package/src/components/WarningText/WarningText.tsx +4 -2
  328. package/src/hooks/useTracking/useTracking.ts +0 -1
  329. package/src/shared-types.ts +3 -3
  330. package/static/images/illustration.svg +502 -0
  331. package/tsconfig.json +0 -3
  332. package/vite.config.ts +4 -0
@@ -0,0 +1,282 @@
1
+ import { describe, test, expect } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import Card from './Card';
4
+
5
+ const CARD_TITLE = 'Bananas';
6
+ const CARD_HREF = '#foo';
7
+ const CARD_CONTENT = 'A banana is an elongated, edible fruit—botanically a berry—produced by several kinds of large treelike herbaceous flowering plants in the genus Musa.';
8
+ const CARD_DESCRIPTION = 'New fruit added';
9
+ const CARD_MEDIA = 'JPEG image of a banana';
10
+
11
+ test('card renders correctly', () => {
12
+ render(
13
+ <Card href={CARD_HREF} data-testid="card">
14
+ <Card.Media isCover ratio="4:3" mediaWidth="wide">
15
+ {CARD_MEDIA}
16
+ </Card.Media>
17
+ <Card.Content>
18
+ <Card.ContentHeader title={CARD_TITLE}>
19
+ <Card.StatusTag colour="orange" isHidden>New release</Card.StatusTag>
20
+ </Card.ContentHeader>
21
+ <Card.ContentMain>
22
+ <p>{CARD_CONTENT}</p>
23
+ </Card.ContentMain>
24
+ <Card.ContentFooter>
25
+ <Card.Metadata>
26
+ </Card.Metadata>
27
+ </Card.ContentFooter>
28
+ </Card.Content>
29
+ </Card>
30
+ );
31
+
32
+ const card = screen.getByTestId('card');
33
+ const title = screen.getByRole('heading');
34
+ const link = screen.getByRole('link');
35
+ const content = card.querySelector('p');
36
+ const media = card.firstChild;
37
+ const aspectbox = media?.firstChild;
38
+ const metadata = card.querySelector('.ds_metadata');
39
+
40
+ expect(media).toHaveClass('ds_card__media', 'ds_card__media--cover', 'ds_card__media--wide');
41
+ expect(aspectbox).toHaveClass('ds_aspect-box', 'ds_aspect-box--43');
42
+
43
+ expect(card).toHaveClass('ds_card', 'ds_card--navigation');
44
+ expect(card.tagName).toEqual('DIV');
45
+
46
+ expect(title).toHaveClass('ds_card__title');
47
+ expect(title.tagName).toEqual('H2');
48
+ expect(title.textContent).toEqual(CARD_TITLE);
49
+ expect(title.parentElement).toHaveClass('ds_card__content-header');
50
+
51
+ expect(link).toHaveClass('ds_card__link', 'ds_card__link--cover');
52
+ expect(link).toHaveAttribute('href', CARD_HREF);
53
+ expect(link.textContent).toEqual(CARD_TITLE);
54
+ expect(link.parentElement).toEqual(title);
55
+
56
+ expect(content?.parentElement).toHaveClass('ds_card__content-main');
57
+ expect(content?.textContent).toEqual(CARD_CONTENT);
58
+
59
+ expect(metadata).toHaveClass('ds_metadata', 'ds_metadata--inline');
60
+ expect(metadata?.parentElement).toHaveClass('ds_card__content-footer');
61
+ });
62
+
63
+ test('container added for adaptive card', () => {
64
+ render(
65
+ <Card data-testid="card" isAdaptive>
66
+ <Card.Content>
67
+ <Card.ContentHeader
68
+ title={CARD_TITLE}>
69
+ </Card.ContentHeader>
70
+ </Card.Content>
71
+ </Card>
72
+ );
73
+
74
+ const card = screen.getByTestId('card');
75
+ const container = card.parentElement
76
+
77
+ expect(container).toHaveClass('ds_card-container');
78
+ });
79
+
80
+ test('description added', () => {
81
+ render(
82
+ <Card data-testid="card">
83
+ <Card.Content>
84
+ <Card.ContentHeader
85
+ title={CARD_TITLE}
86
+ description={CARD_DESCRIPTION}>
87
+ </Card.ContentHeader>
88
+ </Card.Content>
89
+ </Card>
90
+ );
91
+
92
+ const description = screen.getByText(', '+CARD_DESCRIPTION);
93
+
94
+ expect(description).toHaveClass('visually-hidden');
95
+ expect(description.tagName).toEqual('SPAN');
96
+ expect(description.parentElement).toHaveClass('ds_card__title');
97
+ });
98
+
99
+ test('display variant classes added', () => {
100
+ render(
101
+ <Card
102
+ data-testid="card"
103
+ hasSecondaryBackground
104
+ isHorizontal
105
+ isHorizontalSmall>
106
+ <Card.Content>
107
+ <Card.ContentHeader
108
+ title={CARD_TITLE}
109
+ >
110
+ </Card.ContentHeader>
111
+ </Card.Content>
112
+ </Card>
113
+ );
114
+
115
+ const card = screen.getByTestId('card');
116
+
117
+ expect(card).toHaveClass('ds_card', 'ds_card--background-secondary', 'ds_card--horizontal', 'ds_card--horizontal-small');
118
+ });
119
+
120
+ test('custom heading level', () => {
121
+ render(
122
+ <Card href={CARD_HREF} data-testid="card" headingLevel="h3">
123
+ <Card.Content>
124
+ <Card.ContentHeader
125
+ title={CARD_TITLE}>
126
+ </Card.ContentHeader>
127
+ </Card.Content>
128
+ </Card>
129
+ );
130
+
131
+ const title = screen.getByRole('heading');
132
+ expect(title.tagName).toEqual('H3');
133
+ });
134
+
135
+ test('custom element', () => {
136
+ render(
137
+ <Card data-testid="card" tagName="article">
138
+ <Card.Content>
139
+ <Card.ContentHeader
140
+ title={CARD_TITLE}>
141
+ </Card.ContentHeader>
142
+ </Card.Content>
143
+ </Card>
144
+ );
145
+
146
+ const card = screen.getByTestId('card');
147
+ expect(card.tagName).toEqual('ARTICLE');
148
+ });
149
+
150
+ test('custom link component', () => {
151
+ render(
152
+ <Card href={CARD_HREF} data-testid="card" linkComponent={
153
+ ({ className, ...props }) => (
154
+ <span role="link" className={className} {...props}/>
155
+ )}>
156
+ <Card.ContentHeader
157
+ title={CARD_TITLE}>
158
+ </Card.ContentHeader>
159
+ </Card>
160
+ );
161
+
162
+ const link = screen.getByRole('link');
163
+
164
+ expect(link.tagName).toEqual('SPAN');
165
+ expect(link.textContent).toEqual(CARD_TITLE);
166
+ });
167
+
168
+ describe('card status tag', () => {
169
+ const TAG_TEXT = 'Foo';
170
+
171
+ test('card status tag', () => {
172
+ render(
173
+ <Card.StatusTag data-testid="status-tag">{TAG_TEXT}</Card.StatusTag>
174
+ );
175
+
176
+ const statusTag = screen.getByTestId('status-tag');
177
+
178
+ expect(statusTag).toHaveClass('ds_tag');
179
+ expect(statusTag.tagName).toEqual('SPAN');
180
+ expect(statusTag.textContent).toEqual(TAG_TEXT);
181
+ expect(statusTag.getAttribute('aria-hidden')).not.toEqual('true');
182
+ });
183
+
184
+ test('card status tag: hidden', () => {
185
+ render(
186
+ <Card.StatusTag data-testid="status-tag" isHidden>{TAG_TEXT}</Card.StatusTag>
187
+ );
188
+
189
+ const statusTag = screen.getByTestId('status-tag');
190
+
191
+ expect(statusTag).toHaveClass('ds_tag');
192
+ expect(statusTag.getAttribute('aria-hidden')).toEqual('true');
193
+ });
194
+ });
195
+
196
+ describe('card media', () => {
197
+ test('card media', () => {
198
+ render(
199
+ <Card.Media data-testid="card-media">
200
+ {CARD_MEDIA}
201
+ </Card.Media>
202
+ );
203
+
204
+ const cardMedia = screen.getByTestId('card-media');
205
+
206
+ expect(cardMedia).toHaveClass('ds_card__media');
207
+ expect(cardMedia.tagName).toEqual('DIV');
208
+ expect(cardMedia.innerHTML).toEqual(CARD_MEDIA);
209
+ });
210
+
211
+ test('card media with isCover and ratio', () => {
212
+ render(
213
+ <Card.Media isCover ratio="4:3" data-testid="card-media">
214
+ {CARD_MEDIA}
215
+ </Card.Media>
216
+ );
217
+
218
+ const cardMedia = screen.getByTestId('card-media');
219
+ const cardMediaInner = cardMedia.querySelector('*');
220
+
221
+ expect(cardMedia).toHaveClass('ds_card__media--cover');
222
+ expect(cardMediaInner).toHaveClass('ds_aspect-box', 'ds_aspect-box--43');
223
+ });
224
+
225
+ test('card media with only isCover', () => {
226
+ render(
227
+ <Card.Media isCover data-testid="card-media">
228
+ {CARD_MEDIA}
229
+ </Card.Media>
230
+ );
231
+
232
+ const cardMedia = screen.getByTestId('card-media');
233
+ const cardMediaInner = cardMedia.querySelector('*');
234
+
235
+ expect(cardMedia).not.toHaveClass('ds_card__media--cover');
236
+ expect(cardMediaInner).not.toBeInTheDocument();
237
+ });
238
+
239
+ test('card media with only ratio', () => {
240
+ render(
241
+ <Card.Media ratio="4:3" data-testid="card-media">
242
+ {CARD_MEDIA}
243
+ </Card.Media>
244
+ );
245
+
246
+ const cardMedia = screen.getByTestId('card-media');
247
+ const cardMediaInner = cardMedia.querySelector('*');
248
+
249
+ expect(cardMedia).not.toHaveClass('ds_card__media--cover');
250
+ expect(cardMediaInner).toHaveClass('ds_aspect-box', 'ds_aspect-box--43');
251
+ });
252
+
253
+ test('card media with mediaWidth', () => {
254
+ render(
255
+ <Card.Media mediaWidth="narrow" data-testid="card-media">
256
+ {CARD_MEDIA}
257
+ </Card.Media>
258
+ );
259
+
260
+ const cardMedia = screen.getByTestId('card-media');
261
+
262
+ expect(cardMedia).toHaveClass('ds_card__media--narrow');
263
+ });
264
+ });
265
+
266
+ test('passing additional props', () => {
267
+ render(
268
+ <Card data-test="foo" data-testid="card"/>
269
+ );
270
+
271
+ const card = screen.getByTestId('card');
272
+ expect(card.dataset.test).toEqual('foo');
273
+ });
274
+
275
+ test('passing additional CSS classes', () => {
276
+ render(
277
+ <Card className="foo" data-testid="card"/>
278
+ );
279
+
280
+ const card = screen.getByTestId('card');
281
+ expect(card).toHaveClass('foo');
282
+ });
@@ -0,0 +1,208 @@
1
+ import { createContext, useContext } from 'react';
2
+ import ConditionalWrapper from "../../common/ConditionalWrapper";
3
+ import WrapperTag from "../../common/WrapperTag";
4
+ import AspectBox from '../AspectBox';
5
+ import Metadata from '../PageMetadata';
6
+ import Tag from '../Tag';
7
+ import { CardProps, CardHeaderProps, CardMediaProps, CardStatusTagProps } from "./types";
8
+ import { HeadingLevel, LinkComponent } from '@/src/shared-types';
9
+ import clsx from 'clsx';
10
+
11
+ type CardSettingsContextProps = {
12
+ headingLevel: HeadingLevel
13
+ href?: string
14
+ linkComponent?: LinkComponent
15
+ };
16
+
17
+ const CardSettingsContext = createContext<CardSettingsContextProps>({
18
+ headingLevel: 'h2',
19
+ href: undefined,
20
+ linkComponent: undefined
21
+ });
22
+
23
+ const CardContent = ({
24
+ children
25
+ }: React.AllHTMLAttributes<HTMLElement>) => {
26
+ return (
27
+ <div className="ds_card__content">
28
+ {children}
29
+ </div>
30
+ )
31
+ };
32
+
33
+ const CardContentHeader = ({
34
+ children,
35
+ description,
36
+ title
37
+ }: CardHeaderProps) => {
38
+ const headingLevel = useContext(CardSettingsContext).headingLevel;
39
+ const href = useContext(CardSettingsContext).href;
40
+ const linkComponent = useContext(CardSettingsContext).linkComponent;
41
+ const LINK_CLASS = 'ds_card__link ds_card__link--cover';
42
+
43
+ function getLinkElement(children: React.ReactNode) {
44
+ let linkElement;
45
+ if (linkComponent) {
46
+ linkElement = linkComponent({ className: LINK_CLASS, href, children });
47
+ } else {
48
+ linkElement = <a href={href} className={LINK_CLASS}>{children}</a>;
49
+ }
50
+ return linkElement as React.JSX.Element;
51
+ }
52
+
53
+ return (
54
+ <div className="ds_card__content-header">
55
+ {children}
56
+ <WrapperTag
57
+ className="ds_card__title"
58
+ tagName={headingLevel}
59
+ >
60
+ <ConditionalWrapper
61
+ condition={typeof href !== 'undefined'}
62
+ wrapper={(children: React.JSX.Element) => getLinkElement(children)}
63
+ >
64
+ {title}
65
+ {description && <span className="visually-hidden">, {description}</span>}
66
+ </ConditionalWrapper>
67
+ </WrapperTag>
68
+ </div>
69
+ )
70
+ };
71
+
72
+ const CardStatusTag = ({
73
+ children,
74
+ colour,
75
+ isHidden,
76
+ ...props
77
+ }: CardStatusTagProps) => {
78
+ return (
79
+ <Tag
80
+ aria-hidden={isHidden ? "true" : undefined}
81
+ colour={colour}
82
+ {...props}
83
+ >
84
+ {children}
85
+ </Tag>
86
+ )
87
+ };
88
+
89
+ const CardContentMain = ({
90
+ children
91
+ }: React.AllHTMLAttributes<HTMLElement>) => {
92
+ return (
93
+ <div className="ds_card__content-main">
94
+ {children}
95
+ </div>
96
+ )
97
+ };
98
+
99
+ const CardContentFooter = ({
100
+ children
101
+ }: React.AllHTMLAttributes<HTMLElement>) => {
102
+ return (
103
+ <div className="ds_card__content-footer">
104
+ {children}
105
+ </div>
106
+ )
107
+ };
108
+
109
+ const CardMetadata = ({
110
+ children
111
+ }: React.AllHTMLAttributes<HTMLElement>) => {
112
+ return (
113
+ <Metadata isInline>
114
+ {children}
115
+ </Metadata>
116
+ )
117
+ };
118
+
119
+ const CardMedia = ({
120
+ children,
121
+ isCover,
122
+ mediaWidth,
123
+ ratio,
124
+ ...props
125
+ }: CardMediaProps) => {
126
+ return (
127
+ <div
128
+ className={clsx([
129
+ 'ds_card__media',
130
+ isCover && ratio && 'ds_card__media--cover',
131
+ (mediaWidth && (mediaWidth === 'narrow' || mediaWidth === 'wide')) && 'ds_card__media--' + mediaWidth
132
+ ])}
133
+ {...props}
134
+ >
135
+ <ConditionalWrapper
136
+ condition={ratio !== undefined}
137
+ wrapper={(children: React.JSX.Element) => <AspectBox ratio={ratio}>
138
+ {children}
139
+ </AspectBox>}
140
+ >
141
+ {children}
142
+ </ConditionalWrapper>
143
+ </div>
144
+ )
145
+ };
146
+
147
+ const Card = ({
148
+ children,
149
+ className,
150
+ hasSecondaryBackground = false,
151
+ headingLevel,
152
+ href,
153
+ isAdaptive = false,
154
+ isHorizontal = false,
155
+ isHorizontalSmall = false,
156
+ linkComponent,
157
+ tagName = 'div',
158
+ ...props
159
+ }: CardProps) => {
160
+ return (
161
+ <CardSettingsContext value={{
162
+ headingLevel: headingLevel || 'h2',
163
+ href: href,
164
+ linkComponent: linkComponent
165
+ }}>
166
+ <ConditionalWrapper
167
+ condition={isAdaptive}
168
+ wrapper={(children: React.JSX.Element) => <div className="ds_card-container">
169
+ {children}
170
+ </div>}
171
+ >
172
+ <WrapperTag
173
+ tagName={tagName}
174
+ className={clsx([
175
+ 'ds_card',
176
+ href && 'ds_card--navigation',
177
+ hasSecondaryBackground && 'ds_card--background-secondary',
178
+ isHorizontal && 'ds_card--horizontal',
179
+ isHorizontalSmall && 'ds_card--horizontal-small',
180
+ className
181
+ ])}
182
+ {...props}
183
+ >
184
+ {children}
185
+ </WrapperTag>
186
+ </ConditionalWrapper>
187
+ </CardSettingsContext>
188
+ );
189
+ };
190
+
191
+ Card.Content = CardContent;
192
+ Card.ContentFooter = CardContentFooter;
193
+ Card.ContentHeader = CardContentHeader;
194
+ Card.ContentMain = CardContentMain;
195
+ Card.Metadata = CardMetadata;
196
+ Card.Media = CardMedia;
197
+ Card.StatusTag = CardStatusTag;
198
+
199
+ Card.displayName = 'Card';
200
+ CardContent.displayName = 'Card.Content';
201
+ CardContentFooter.displayName = 'Card.ContentFooter';
202
+ CardContentHeader.displayName = 'Card.ContentHeader';
203
+ CardContentMain.displayName = 'Card.ContentMain';
204
+ CardMetadata.displayName = 'Card.Metadata';
205
+ CardMedia.displayName = 'Card.Media';
206
+ CardStatusTag.displayName = 'Card.StatusTag';
207
+
208
+ export default Card;
@@ -0,0 +1 @@
1
+ export { default } from './Card';
@@ -0,0 +1,30 @@
1
+ import { HeadingLevel, LinkComponent } from '../../shared-types';
2
+ import { TagProps } from '../Tag/types';
3
+
4
+ export interface CardProps extends React.AllHTMLAttributes<HTMLElement> {
5
+ className?: string
6
+ hasSecondaryBackground?: boolean
7
+ headingLevel?: HeadingLevel
8
+ href?: string
9
+ isAdaptive?: boolean
10
+ isHorizontal?: boolean
11
+ isHorizontalSmall?: boolean
12
+ linkComponent?: LinkComponent
13
+ tagName?: string
14
+ }
15
+
16
+ export interface CardHeaderProps extends React.AllHTMLAttributes<HTMLElement> {
17
+ className?: string
18
+ description?: string
19
+ title: string
20
+ }
21
+
22
+ export interface CardMediaProps extends React.AllHTMLAttributes<HTMLElement> {
23
+ isCover?: boolean
24
+ mediaWidth?: 'narrow' | 'wide'
25
+ ratio?: 'square' | '1:1' | '4:3' | '16:9' | '21:9'
26
+ }
27
+
28
+ export interface CardStatusTagProps extends TagProps {
29
+ isHidden?: boolean
30
+ }
@@ -1,6 +1,7 @@
1
1
  import ConditionalWrapper from "../../common/ConditionalWrapper";
2
2
  import WrapperTag from "../../common/WrapperTag";
3
3
  import { CategoryItemProps } from "./types";
4
+ import clsx from 'clsx';
4
5
 
5
6
  const CategoryItem = ({
6
7
  children,
@@ -27,10 +28,10 @@ const CategoryItem = ({
27
28
  return (
28
29
  <WrapperTag
29
30
  tagName={tagName}
30
- className={[
31
+ className={clsx([
31
32
  'ds_category-item',
32
33
  className
33
- ].join(' ')}
34
+ ])}
34
35
  {...props}
35
36
  >
36
37
  <WrapperTag
@@ -2,6 +2,7 @@ import React, { Children } from 'react';
2
2
  import WrapperTag from "../../common/WrapperTag";
3
3
  import { CategoryListProps } from './types';
4
4
  import { CategoryItemProps } from '../CategoryItem/types';
5
+ import clsx from 'clsx';
5
6
 
6
7
  const CategoryList = ({
7
8
  children,
@@ -18,11 +19,11 @@ const CategoryList = ({
18
19
  return (
19
20
  <WrapperTag
20
21
  tagName={isOrdered ? 'ol' : 'ul'}
21
- className={[
22
+ className={clsx([
22
23
  'ds_category-list',
23
24
  isGrid && 'ds_category-list--grid',
24
25
  className
25
- ].join(' ')}
26
+ ])}
26
27
  {...props}
27
28
  >
28
29
  {Children.map(children, child => processChild(child))}
@@ -74,6 +74,36 @@ test('checkbox with change fn', () => {
74
74
  expect(ONCHANGE_FUNCTION).toHaveBeenCalled();
75
75
  });
76
76
 
77
+ test('checkbox with onBlur that is not a function', () => {
78
+ render(
79
+ // @ts-expect-error onBlur is not a function
80
+ <Checkbox onBlur='foo' label="Pension Credit" id="pensioncredit" />
81
+ );
82
+
83
+ const checkbox = screen.getByRole('checkbox');
84
+
85
+ fireEvent.blur(checkbox);
86
+
87
+ // todo: assertion
88
+ // success indicated by no errors thrown
89
+ // error would be thrown on an untestable thread
90
+ });
91
+
92
+ test('checkbox with onChange that is not a function', () => {
93
+ render(
94
+ // @ts-expect-error onChange is not a function
95
+ <Checkbox onChange='foo' label="Pension Credit" id="pensioncredit" />
96
+ );
97
+
98
+ const checkbox = screen.getByRole('checkbox');
99
+
100
+ fireEvent.click(checkbox);
101
+
102
+ // todo: assertion
103
+ // success indicated by no errors thrown
104
+ // error would be thrown on an untestable thread
105
+ });
106
+
77
107
  test('checkbox with hint text', () => {
78
108
  render(
79
109
  <Checkbox hintText="hint text" label="Pension Credit" id="pensioncredit" />
@@ -1,8 +1,8 @@
1
1
  import { useContext } from 'react';
2
2
  import { CheckboxRadioContext } from '../../utils/context';
3
-
4
3
  import HintText from '../../common/HintText';
5
4
  import { CheckboxProps } from './types';
5
+ import clsx from 'clsx';
6
6
 
7
7
  const Checkbox = ({
8
8
  checked,
@@ -36,10 +36,10 @@ const Checkbox = ({
36
36
  <>
37
37
  {isExclusive && <p className="ds_checkbox-separator">or</p>}
38
38
  <div
39
- className={[
39
+ className={clsx([
40
40
  'ds_checkbox',
41
41
  isSmall && 'ds_checkbox--small'
42
- ].join(' ')}>
42
+ ])}>
43
43
 
44
44
  <input
45
45
  aria-describedby={hintText ? hintTextId : undefined}
@@ -18,6 +18,16 @@ test('checkbox group renders correctly', () => {
18
18
  expect(checkboxContainer).not.toHaveClass('ds_checkbox--small');
19
19
  });
20
20
 
21
+ test('instantiating/initialising DS component script', () => {
22
+ render(
23
+ <CheckboxGroup data-testid="checkboxgroup" />
24
+ );
25
+
26
+ const checkboxGroup = screen.getByTestId('checkboxgroup');
27
+ expect(checkboxGroup).toHaveClass('js-initialised');
28
+ expect(checkboxGroup).toHaveClass('js-instantiated');
29
+ });
30
+
21
31
  test('passing additional props', () => {
22
32
  render(
23
33
  <CheckboxGroup data-testid="checkboxgroup" data-test="foo" />
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import { CheckboxRadioContext } from '../../utils/context';
3
- // @ts-expect-error no types from core SGDS
4
- import DSCheckboxes from '@scottish-government/design-system/src/forms/checkbox/checkboxes'
3
+ import DSCheckboxes from '@scottish-government/design-system/src/components/checkbox/checkboxes'
5
4
  import { CheckboxGroupProps } from './types';
5
+ import clsx from 'clsx';
6
6
 
7
7
  export const CheckboxGroup = ({
8
8
  children,
@@ -13,6 +13,7 @@ export const CheckboxGroup = ({
13
13
  const ref = useRef(null);
14
14
 
15
15
  useEffect(() => {
16
+ /* istanbul ignore else */
16
17
  if (ref.current) {
17
18
  new DSCheckboxes(ref.current).init();
18
19
  }
@@ -20,11 +21,11 @@ export const CheckboxGroup = ({
20
21
 
21
22
  return (
22
23
  <div
23
- className={[
24
+ className={clsx([
24
25
  'ds_checkboxes',
25
26
  'ds_field-group',
26
27
  className
27
- ].join(' ')}
28
+ ])}
28
29
  data-module="ds-checkboxes"
29
30
  ref={ref}
30
31
  {...props}