uikit-react-public 0.14.21 → 0.21.8

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 (392) hide show
  1. package/README.md +2 -2
  2. package/dist/components/Accordion/Accordion.Heading.d.ts +5 -4
  3. package/dist/components/Accordion/Accordion.Panel.d.ts +2 -2
  4. package/dist/components/Accordion/Accordion.d.ts +1 -1
  5. package/dist/components/Accordion/Accordion.stories.d.ts +57 -0
  6. package/dist/components/Accordion/index.d.ts +2 -0
  7. package/dist/components/AppHeader/AppHeader.d.ts +1 -1
  8. package/dist/components/AppHeader/AppHeaderBottom.d.ts +1 -1
  9. package/dist/components/AppHeader/AppHeaderNav.d.ts +1 -1
  10. package/dist/components/AppHeader/AppHeaderTop.d.ts +1 -1
  11. package/dist/components/Avatar/Avatar.stories.d.ts +107 -1
  12. package/dist/components/Breadcrumbs/Breadcrumb.d.ts +3 -4
  13. package/dist/components/Breadcrumbs/Breadcrumbs.d.ts +1 -1
  14. package/dist/components/Breadcrumbs/Breadcrumbs.stories.d.ts +1 -1
  15. package/dist/components/Button/Button.d.ts +8 -3
  16. package/dist/components/Button/Button.stories.d.ts +17 -7
  17. package/dist/components/Button/style/buttonAccentStyle.d.ts +4 -0
  18. package/dist/components/Button/style/buttonPrimaryDestructiveStyle.d.ts +4 -0
  19. package/dist/components/Button/style/buttonPrimaryStyle.d.ts +4 -0
  20. package/dist/components/Button/style/buttonPrimarySubtleStyle.d.ts +4 -0
  21. package/dist/components/Button/style/buttonPrimaryWarningStyle.d.ts +4 -0
  22. package/dist/components/Button/style/buttonSecondaryDestructiveStyle.d.ts +4 -0
  23. package/dist/components/Button/style/buttonSecondaryStyle.d.ts +4 -0
  24. package/dist/components/Button/style/buttonSecondarySubtleStyle.d.ts +4 -0
  25. package/dist/components/Button/style/buttonTertiaryDestructiveStyle.d.ts +4 -0
  26. package/dist/components/Button/style/buttonTertiaryNoPaddingStyle.d.ts +4 -0
  27. package/dist/components/Button/style/buttonTertiaryStyle.d.ts +4 -0
  28. package/dist/components/Calendar/index.d.ts +1 -1
  29. package/dist/components/Checkbox/Checkbox.d.ts +1 -0
  30. package/dist/components/Datepicker/Datepicker.d.ts +1 -1
  31. package/dist/components/Datepicker/Datepicker.stories.d.ts +4 -3
  32. package/dist/components/Datepicker/Datepicker.types.d.ts +4 -5
  33. package/dist/components/Datepicker/subcomponents/CustomDatepicker.d.ts +4 -1
  34. package/dist/components/Datepicker/subcomponents/DatepickerInput.d.ts +15 -2
  35. package/dist/components/Datepicker/subcomponents/Panel.d.ts +1 -1
  36. package/dist/components/Datepicker/subcomponents/VisibleField.d.ts +6 -1
  37. package/dist/components/Datepicker/subcomponents/index.d.ts +0 -1
  38. package/dist/components/Datepicker/utils/index.d.ts +0 -1
  39. package/dist/components/Dialog/BaseDialog.d.ts +2 -1
  40. package/dist/components/Dialog/Dialog.d.ts +2 -0
  41. package/dist/components/FooterNew/BackToTop.d.ts +8 -0
  42. package/dist/components/FooterNew/Footer.d.ts +23 -0
  43. package/dist/components/FooterNew/FooterColumn.d.ts +8 -0
  44. package/dist/components/FooterNew/FooterLinks.d.ts +7 -0
  45. package/dist/components/FooterNew/FooterNavLink.d.ts +8 -0
  46. package/dist/components/FooterNew/LegalAndCopyright.d.ts +14 -0
  47. package/dist/components/FooterNew/LogoAddressAndSocial.d.ts +10 -0
  48. package/dist/components/FooterNew/SocialLink.d.ts +8 -0
  49. package/dist/components/FooterNew/index.d.ts +2 -0
  50. package/dist/components/Header/Header.d.ts +4 -1
  51. package/dist/components/Header/Header.stories.d.ts +40 -0
  52. package/dist/components/HeaderNew/Header.d.ts +18 -0
  53. package/dist/components/HeaderNew/HeaderBorder.d.ts +7 -0
  54. package/dist/components/HeaderNew/HeaderLogo.d.ts +9 -0
  55. package/dist/components/HeaderNew/HeaderMenuContainer.d.ts +7 -0
  56. package/dist/components/HeaderNew/HeaderTitle.d.ts +9 -0
  57. package/dist/components/HeaderNew/__tests__/Header.test.d.ts +1 -0
  58. package/dist/components/HeaderNew/constants.d.ts +3 -0
  59. package/dist/components/HeaderNew/index.d.ts +3 -0
  60. package/dist/components/HeadingNew/Heading.d.ts +13 -0
  61. package/dist/components/HeadingNew/index.d.ts +2 -0
  62. package/dist/components/Icon/svgImports.d.ts +7 -881
  63. package/dist/components/Link/BaseLink.d.ts +14 -5
  64. package/dist/components/Link/Link.d.ts +8 -3
  65. package/dist/components/Link/Link.stories.d.ts +3 -1
  66. package/dist/components/Main/Main.d.ts +21 -0
  67. package/dist/components/Main/Main.stories.d.ts +15 -0
  68. package/dist/components/Main/__tests__/Main.test.d.ts +1 -0
  69. package/dist/components/Main/index.d.ts +2 -0
  70. package/dist/components/MenuNew/Menu.context.d.ts +14 -0
  71. package/dist/components/MenuNew/Menu.d.ts +20 -0
  72. package/dist/components/MenuNew/MenuContent.d.ts +9 -0
  73. package/dist/components/MenuNew/MenuItem.d.ts +10 -0
  74. package/dist/components/MenuNew/MenuSection.d.ts +7 -0
  75. package/dist/components/MenuNew/index.d.ts +6 -0
  76. package/dist/components/MenuNew/trigger/ButtonMenuTrigger.d.ts +8 -0
  77. package/dist/components/MenuNew/trigger/IconMenuTrigger.d.ts +8 -0
  78. package/dist/components/NativeDatepicker/NativeDatepicker.d.ts +3 -0
  79. package/dist/components/NativeDatepicker/NativeDatepicker.stories.d.ts +36 -0
  80. package/dist/components/NativeDatepicker/NativeDatepicker.types.d.ts +10 -0
  81. package/dist/components/NativeDatepicker/index.d.ts +2 -0
  82. package/dist/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.d.ts +1 -1
  83. package/dist/components/NativeDatepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts +1 -0
  84. package/dist/components/NativeDatepicker/utils/index.d.ts +1 -0
  85. package/dist/components/Overlay/Overlay.stories.d.ts +12 -12
  86. package/dist/components/ParagraphNew/Paragraph.d.ts +13 -0
  87. package/dist/components/ParagraphNew/index.d.ts +4 -0
  88. package/dist/components/Select/Select.d.ts +2 -1
  89. package/dist/components/Select/Select.stories.d.ts +167 -3
  90. package/dist/components/Select/Select.types.d.ts +75 -19
  91. package/dist/components/Select/subcomponents/CustomOption.d.ts +1 -1
  92. package/dist/components/Select/subcomponents/CustomSelect.d.ts +3 -2
  93. package/dist/components/Select/subcomponents/FilterInput.d.ts +16 -0
  94. package/dist/components/Select/subcomponents/NativeSelect.d.ts +5 -1
  95. package/dist/components/Select/subcomponents/VisibleField.d.ts +6 -1
  96. package/dist/components/Select/subcomponents/index.d.ts +1 -0
  97. package/dist/components/Spinner/Spinner.d.ts +2 -0
  98. package/dist/components/StandaloneLink/StandaloneLink.d.ts +8 -5
  99. package/dist/components/StandaloneLink/StandaloneLink.stories.d.ts +3 -1
  100. package/dist/components/Table/Table.d.ts +3 -3
  101. package/dist/components/Table/Table.stories.d.ts +3 -3
  102. package/dist/components/Table/Table.types.d.ts +1 -0
  103. package/dist/components/Table/subcomponents/Cell/Cell.d.ts +5 -1
  104. package/dist/components/Table/subcomponents/Cell/Cell.stories.d.ts +15 -13
  105. package/dist/components/Table/subcomponents/Cell/CellContent.d.ts +5 -1
  106. package/dist/components/Table/subcomponents/HeadCell/HeadCell.d.ts +2 -1
  107. package/dist/components/Table/subcomponents/HeadCell/HeadCell.stories.d.ts +14 -13
  108. package/dist/components/Table/subcomponents/HeadCell/HeadCellContent.d.ts +2 -1
  109. package/dist/components/Table/subcomponents/__tests__/Row.test.d.ts +1 -0
  110. package/dist/components/UclLogoNew/UclLogo.d.ts +8 -0
  111. package/dist/components/UclLogoNew/index.d.ts +2 -0
  112. package/dist/components/WeekPicker/WeekPicker.d.ts +2 -2
  113. package/dist/components/WeekPicker/WeekPicker.stories.d.ts +41 -0
  114. package/dist/components/WeekPicker/WeekPicker.types.d.ts +16 -0
  115. package/dist/components/WeekPicker/index.d.ts +1 -0
  116. package/dist/components/WeekPicker/subcomponents/CustomDatepicker.d.ts +1 -1
  117. package/dist/components/index.d.ts +20 -0
  118. package/dist/hooks/useFocusTrap.d.ts +2 -1
  119. package/dist/index.d.ts +1 -0
  120. package/dist/index.js +22204 -16719
  121. package/dist/theme/__tests__/fonts.test.d.ts +1 -0
  122. package/dist/theme/common/themeCommon.d.ts +904 -0
  123. package/dist/theme/fonts.d.ts +18 -0
  124. package/dist/theme/index.d.ts +6 -3
  125. package/dist/theme/light/lightColour.d.ts +126 -0
  126. package/dist/theme/light/lightTheme.d.ts +3 -0
  127. package/dist/theme/original/color.d.ts +166 -0
  128. package/dist/theme/original/defaultTheme.d.ts +1340 -0
  129. package/dist/theme/original/originalColourNewStructure.d.ts +126 -0
  130. package/dist/theme/useTheme.d.ts +2174 -0
  131. package/dist/utils/__tests__/announce.test.d.ts +1 -0
  132. package/dist/utils/addAlphaToHex.d.ts +5 -0
  133. package/dist/utils/announce.d.ts +6 -0
  134. package/dist/utils/index.d.ts +1 -0
  135. package/dist/utils/scrollToTop.d.ts +2 -0
  136. package/lib/components/Accordion/Accordion.Heading.tsx +65 -34
  137. package/lib/components/Accordion/Accordion.Panel.tsx +11 -7
  138. package/lib/components/Accordion/Accordion.stories.tsx +139 -0
  139. package/lib/components/Accordion/Accordion.tsx +39 -31
  140. package/lib/components/Accordion/__tests__/__snapshots__/Accordion.test.tsx.snap +15 -13
  141. package/lib/components/Accordion/index.ts +2 -0
  142. package/lib/components/Alert/Alert.stories.tsx +1 -1
  143. package/lib/components/Alert/Alert.tsx +12 -12
  144. package/lib/components/Alert/__tests__/__snapshots__/Alert.test.tsx.snap +13 -39
  145. package/lib/components/AppHeader/AppHeader.tsx +6 -11
  146. package/lib/components/AppHeader/AppHeaderBottom.tsx +2 -3
  147. package/lib/components/AppHeader/AppHeaderNav.tsx +2 -3
  148. package/lib/components/AppHeader/AppHeaderTop.tsx +1 -1
  149. package/lib/components/AppHeader/__tests__/__snapshots__/AppHeader.test.tsx.snap +2 -2
  150. package/lib/components/AppMenu/__tests__/__snapshots__/AppMenu.test.tsx.snap +6 -19
  151. package/lib/components/Avatar/Avatar.mdx +117 -0
  152. package/lib/components/Avatar/Avatar.stories.tsx +110 -2
  153. package/lib/components/Badge/Badge.stories.tsx +1 -1
  154. package/lib/components/Blanket/Blanket.stories.tsx +1 -1
  155. package/lib/components/Breadcrumbs/Breadcrumb.tsx +26 -12
  156. package/lib/components/Breadcrumbs/Breadcrumbs.tsx +1 -1
  157. package/lib/components/Breadcrumbs/__tests__/Breadcrumbs.test.tsx +9 -27
  158. package/lib/components/Breadcrumbs/__tests__/__snapshots__/Breadcrumbs.test.tsx.snap +24 -20
  159. package/lib/components/Button/Button.mdx +32 -279
  160. package/lib/components/Button/Button.stories.tsx +44 -51
  161. package/lib/components/Button/Button.tsx +166 -25
  162. package/lib/components/Button/__tests__/Button.test.tsx +49 -15
  163. package/lib/components/Button/__tests__/__snapshots__/Button.test.tsx.snap +80 -73
  164. package/lib/components/Button/style/buttonAccentStyle.ts +53 -0
  165. package/lib/components/Button/style/buttonPrimaryDestructiveStyle.ts +55 -0
  166. package/lib/components/Button/style/buttonPrimaryStyle.ts +53 -0
  167. package/lib/components/Button/style/buttonPrimarySubtleStyle.ts +64 -0
  168. package/lib/components/Button/style/buttonPrimaryWarningStyle.ts +56 -0
  169. package/lib/components/Button/style/buttonSecondaryDestructiveStyle.ts +63 -0
  170. package/lib/components/Button/style/buttonSecondaryStyle.ts +62 -0
  171. package/lib/components/Button/style/buttonSecondarySubtleStyle.ts +72 -0
  172. package/lib/components/Button/style/buttonTertiaryDestructiveStyle.ts +65 -0
  173. package/lib/components/Button/style/buttonTertiaryNoPaddingStyle.ts +52 -0
  174. package/lib/components/Button/style/buttonTertiaryStyle.ts +62 -0
  175. package/lib/components/Calendar/Calendar.stories.tsx +1 -1
  176. package/lib/components/Calendar/Calendar.tsx +2 -2
  177. package/lib/components/Calendar/__tests__/Calendar.test.tsx +23 -15
  178. package/lib/components/Calendar/__tests__/__snapshots__/Calendar.test.tsx.snap +99 -95
  179. package/lib/components/Calendar/index.ts +1 -5
  180. package/lib/components/Calendar/subcomponents/AcademicWeek.tsx +2 -1
  181. package/lib/components/Calendar/subcomponents/AcademicWeeks.tsx +1 -1
  182. package/lib/components/Calendar/subcomponents/ColumnHeading.tsx +2 -2
  183. package/lib/components/Calendar/subcomponents/Controls.tsx +1 -1
  184. package/lib/components/Calendar/subcomponents/Day.stories.tsx +1 -1
  185. package/lib/components/Calendar/subcomponents/Day.tsx +7 -9
  186. package/lib/components/Calendar/subcomponents/EventDot.tsx +3 -6
  187. package/lib/components/Calendar/subcomponents/index.ts +1 -1
  188. package/lib/components/Calendar/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.ts +43 -11
  189. package/lib/components/Calendar/utils/normaliseMonth/normaliseMonth.test.ts +5 -5
  190. package/lib/components/Checkbox/Checkbox.stories.tsx +1 -1
  191. package/lib/components/Checkbox/Checkbox.tsx +12 -10
  192. package/lib/components/Checkbox/__tests__/Checkbox.test.tsx +29 -0
  193. package/lib/components/Checkbox/__tests__/__snapshots__/Checkbox.test.tsx.snap +4 -4
  194. package/lib/components/Datepicker/Datepicker.lld.md +108 -0
  195. package/lib/components/Datepicker/Datepicker.stories.tsx +44 -5
  196. package/lib/components/Datepicker/Datepicker.tsx +14 -36
  197. package/lib/components/Datepicker/Datepicker.types.ts +5 -14
  198. package/lib/components/Datepicker/__tests__/Datepicker.test.tsx +267 -8
  199. package/lib/components/Datepicker/__tests__/__snapshots__/Datepicker.test.tsx.snap +20 -42
  200. package/lib/components/Datepicker/subcomponents/CustomDatepicker.tsx +48 -5
  201. package/lib/components/Datepicker/subcomponents/DatepickerInput.tsx +30 -17
  202. package/lib/components/Datepicker/subcomponents/Panel.tsx +6 -2
  203. package/lib/components/Datepicker/subcomponents/VisibleField.tsx +46 -8
  204. package/lib/components/Datepicker/subcomponents/index.ts +0 -1
  205. package/lib/components/Datepicker/utils/index.ts +0 -1
  206. package/lib/components/Dialog/BaseDialog.tsx +13 -2
  207. package/lib/components/Dialog/Dialog.stories.tsx +1 -1
  208. package/lib/components/Dialog/Dialog.tsx +8 -1
  209. package/lib/components/Dialog/DialogBody.tsx +5 -1
  210. package/lib/components/Dialog/DialogHeader.tsx +2 -1
  211. package/lib/components/Divider/Divider.stories.tsx +1 -1
  212. package/lib/components/Divider/__tests__/__snapshots__/Breadcrumbs.test.tsx.snap +12 -12
  213. package/lib/components/FeedbackDialog/FeedbackDialog.stories.tsx +1 -1
  214. package/lib/components/FeedbackDialog/FeedbackDialog.tsx +4 -6
  215. package/lib/components/Field/CharacterCount.tsx +2 -2
  216. package/lib/components/Field/ErrorText.tsx +2 -1
  217. package/lib/components/Field/Field.stories.tsx +1 -1
  218. package/lib/components/Field/Field.tsx +1 -1
  219. package/lib/components/Field/HelperText.tsx +3 -1
  220. package/lib/components/Field/__tests__/Field.test.tsx +13 -0
  221. package/lib/components/FileInput/FileInput.stories.tsx +1 -1
  222. package/lib/components/FileInput/__tests__/__snapshots__/FileInput.test.tsx.snap +4 -20
  223. package/lib/components/Footer/Footer.stories.tsx +1 -1
  224. package/lib/components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap +73 -82
  225. package/lib/components/FooterNew/BackToTop.tsx +83 -0
  226. package/lib/components/FooterNew/Footer.tsx +110 -0
  227. package/lib/components/FooterNew/FooterColumn.tsx +79 -0
  228. package/lib/components/FooterNew/FooterLinks.tsx +44 -0
  229. package/lib/components/FooterNew/FooterNavLink.tsx +63 -0
  230. package/lib/components/FooterNew/LegalAndCopyright.tsx +150 -0
  231. package/lib/components/FooterNew/LogoAddressAndSocial.tsx +154 -0
  232. package/lib/components/FooterNew/SocialLink.tsx +108 -0
  233. package/lib/components/FooterNew/__tests__/Footer.test.tsx +51 -0
  234. package/lib/components/FooterNew/__tests__/__snapshots__/Footer.test.tsx.snap +1107 -0
  235. package/lib/components/FooterNew/index.ts +2 -0
  236. package/lib/components/Header/Header.mdx +52 -0
  237. package/lib/components/Header/Header.stories.tsx +98 -0
  238. package/lib/components/Header/Header.tsx +51 -6
  239. package/lib/components/Header/__tests__/Header.test.tsx +17 -1
  240. package/lib/components/HeaderDraft/__tests__/__snapshots__/Header.test.tsx.snap +3 -2
  241. package/lib/components/HeaderNew/Header.tsx +93 -0
  242. package/lib/components/HeaderNew/HeaderBorder.tsx +55 -0
  243. package/lib/components/HeaderNew/HeaderLogo.tsx +70 -0
  244. package/lib/components/HeaderNew/HeaderMenuContainer.tsx +35 -0
  245. package/lib/components/HeaderNew/HeaderTitle.tsx +53 -0
  246. package/lib/components/HeaderNew/__tests__/Header.test.tsx +42 -0
  247. package/lib/components/HeaderNew/__tests__/__snapshots__/Header.test.tsx.snap +79 -0
  248. package/lib/components/HeaderNew/constants.ts +3 -0
  249. package/lib/components/HeaderNew/index.ts +7 -0
  250. package/lib/components/Heading/Heading.stories.tsx +1 -1
  251. package/lib/components/HeadingNew/Heading.tsx +208 -0
  252. package/lib/components/HeadingNew/index.ts +2 -0
  253. package/lib/components/Icon/Icon.stories.tsx +1 -1
  254. package/lib/components/Icon/__tests__/__snapshots__/Icon.test.tsx.snap +16 -12
  255. package/lib/components/Icon/svgImports.ts +318 -296
  256. package/lib/components/IconButton/IconButton.stories.tsx +1 -1
  257. package/lib/components/IconButton/IconButton.tsx +3 -4
  258. package/lib/components/IconButton/__tests__/__snapshots__/IconButton.test.tsx.snap +12 -9
  259. package/lib/components/Input/Input.stories.tsx +1 -1
  260. package/lib/components/Label/Label.stories.tsx +1 -1
  261. package/lib/components/Link/BaseLink.tsx +114 -71
  262. package/lib/components/Link/Link.stories.tsx +1 -1
  263. package/lib/components/Link/Link.tsx +120 -109
  264. package/lib/components/Link/__tests__/__snapshots__/link.test.tsx.snap +2 -2
  265. package/lib/components/Main/Main.stories.tsx +36 -0
  266. package/lib/components/Main/Main.tsx +46 -0
  267. package/lib/components/Main/__tests__/Main.test.tsx +80 -0
  268. package/lib/components/Main/__tests__/__snapshots__/Main.test.tsx.snap +33 -0
  269. package/lib/components/Main/index.ts +2 -0
  270. package/lib/components/MenuNew/Menu.context.tsx +149 -0
  271. package/lib/components/MenuNew/Menu.tsx +75 -0
  272. package/lib/components/MenuNew/MenuContent.tsx +140 -0
  273. package/lib/components/MenuNew/MenuItem.tsx +101 -0
  274. package/lib/components/MenuNew/MenuSection.tsx +47 -0
  275. package/lib/components/MenuNew/index.ts +8 -0
  276. package/lib/components/MenuNew/trigger/ButtonMenuTrigger.tsx +42 -0
  277. package/lib/components/MenuNew/trigger/IconMenuTrigger.tsx +40 -0
  278. package/lib/components/NativeDatepicker/NativeDatepicker.stories.tsx +100 -0
  279. package/lib/components/{Datepicker/subcomponents → NativeDatepicker}/NativeDatepicker.tsx +14 -15
  280. package/lib/components/NativeDatepicker/NativeDatepicker.types.ts +19 -0
  281. package/lib/components/NativeDatepicker/index.ts +2 -0
  282. package/lib/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.ts +1 -1
  283. package/lib/components/NativeDatepicker/utils/index.ts +1 -0
  284. package/lib/components/Pagination/Pagination.stories.tsx +1 -1
  285. package/lib/components/Pagination/PaginationControls.tsx +59 -17
  286. package/lib/components/Pagination/PaginationInfo.tsx +7 -4
  287. package/lib/components/Paragraph/Paragraph.stories.tsx +1 -1
  288. package/lib/components/ParagraphNew/Paragraph.tsx +200 -0
  289. package/lib/components/ParagraphNew/index.ts +6 -0
  290. package/lib/components/Radio/Radio.stories.tsx +1 -1
  291. package/lib/components/Radio/Radio.tsx +8 -8
  292. package/lib/components/Radio/__tests__/__snapshots__/Radio.test.tsx.snap +4 -4
  293. package/lib/components/Search/Search.stories.tsx +1 -1
  294. package/lib/components/Search/Search.tsx +4 -1
  295. package/lib/components/Search/__tests__/Search.test.tsx +19 -1
  296. package/lib/components/Search/__tests__/__snapshots__/Search.test.tsx.snap +12 -32
  297. package/lib/components/Select/Select.mdx +192 -0
  298. package/lib/components/Select/Select.stories.tsx +229 -48
  299. package/lib/components/Select/Select.tsx +50 -15
  300. package/lib/components/Select/Select.types.ts +99 -44
  301. package/lib/components/Select/__tests__/Select.test.tsx +698 -8
  302. package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +5 -4
  303. package/lib/components/Select/subcomponents/CustomOption.tsx +12 -4
  304. package/lib/components/Select/subcomponents/CustomSelect.tsx +411 -41
  305. package/lib/components/Select/subcomponents/FilterInput.tsx +90 -0
  306. package/lib/components/Select/subcomponents/NativeSelect.tsx +21 -17
  307. package/lib/components/Select/subcomponents/Panel.tsx +2 -2
  308. package/lib/components/Select/subcomponents/VisibleField.tsx +59 -6
  309. package/lib/components/Select/subcomponents/index.tsx +1 -0
  310. package/lib/components/Snackbar/Snackbar.stories.tsx +1 -1
  311. package/lib/components/Snackbar/__tests__/__snapshots__/Snackbar.test.tsx.snap +9 -15
  312. package/lib/components/Spinner/Spinner.stories.tsx +1 -1
  313. package/lib/components/Spinner/Spinner.tsx +24 -5
  314. package/lib/components/Spinner/__tests__/Spinner.test.tsx +35 -5
  315. package/lib/components/Spinner/__tests__/__snapshots__/Spinner.test.tsx.snap +40 -16
  316. package/lib/components/StandaloneLink/StandaloneLink.stories.tsx +1 -1
  317. package/lib/components/StandaloneLink/StandaloneLink.tsx +180 -163
  318. package/lib/components/StandaloneLink/__tests__/__snapshots__/StandaloneLink.test.tsx.snap +2 -2
  319. package/lib/components/Table/Table.stories.tsx +1 -1
  320. package/lib/components/Table/Table.tsx +2 -0
  321. package/lib/components/Table/Table.types.ts +1 -0
  322. package/lib/components/Table/__tests__/Table.test.tsx +19 -0
  323. package/lib/components/Table/__tests__/__snapshots__/Table.test.tsx.snap +7 -3
  324. package/lib/components/Table/subcomponents/Cell/Cell.stories.tsx +1 -1
  325. package/lib/components/Table/subcomponents/Cell/Cell.tsx +23 -2
  326. package/lib/components/Table/subcomponents/Cell/CellContent.tsx +12 -1
  327. package/lib/components/Table/subcomponents/Cell/__tests__/Cell.test.tsx +106 -0
  328. package/lib/components/Table/subcomponents/Cell/__tests__/__snapshots__/Cell.test.tsx.snap +4 -3
  329. package/lib/components/Table/subcomponents/HeadCell/HeadCell.stories.tsx +1 -1
  330. package/lib/components/Table/subcomponents/HeadCell/HeadCell.tsx +28 -6
  331. package/lib/components/Table/subcomponents/HeadCell/HeadCellContent.tsx +3 -0
  332. package/lib/components/Table/subcomponents/HeadCell/__tests__/HeadCell.test.tsx +221 -2
  333. package/lib/components/Table/subcomponents/HeadCell/__tests__/__snapshots__/HeadCell.test.tsx.snap +6 -4
  334. package/lib/components/Table/subcomponents/Row.tsx +2 -2
  335. package/lib/components/Table/subcomponents/SortIcon.tsx +1 -0
  336. package/lib/components/Table/subcomponents/__tests__/Row.test.tsx +59 -0
  337. package/lib/components/Tabs/Tab.tsx +3 -3
  338. package/lib/components/Tabs/Tabs.stories.tsx +1 -1
  339. package/lib/components/Tabs/Tabs.tsx +5 -3
  340. package/lib/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +4 -4
  341. package/lib/components/Textarea/Textarea.stories.tsx +1 -1
  342. package/lib/components/Timepicker/Timepicker.stories.tsx +1 -1
  343. package/lib/components/Timepicker/Timepicker.tsx +4 -0
  344. package/lib/components/Timepicker/__tests__/__snapshots__/Timepicker.test.tsx.snap +2 -2
  345. package/lib/components/Toggle/Toggle.stories.tsx +1 -1
  346. package/lib/components/Toggle/Toggle.tsx +5 -5
  347. package/lib/components/Toggle/ToggleHandle.tsx +2 -3
  348. package/lib/components/Tooltip/Tooltip.stories.tsx +1 -1
  349. package/lib/components/Tooltip/Tooltip.tsx +2 -2
  350. package/lib/components/Tooltip/__tests__/__snapshots__/tooltip.test.tsx.snap +2 -2
  351. package/lib/components/UclLogoNew/UclLogo.tsx +42 -0
  352. package/lib/components/UclLogoNew/index.ts +2 -0
  353. package/lib/components/WeekPicker/WeekPicker.stories.tsx +145 -0
  354. package/lib/components/WeekPicker/WeekPicker.tsx +2 -2
  355. package/lib/components/WeekPicker/WeekPicker.types.ts +21 -0
  356. package/lib/components/WeekPicker/index.ts +1 -0
  357. package/lib/components/WeekPicker/subcomponents/CustomDatepicker.tsx +1 -1
  358. package/lib/components/common/Common.mdx +1 -2
  359. package/lib/components/index.ts +30 -3
  360. package/lib/hooks/useFocusTrap.ts +40 -4
  361. package/lib/index.ts +1 -0
  362. package/lib/theme/Colours.mdx +1 -1
  363. package/lib/theme/Theme.mdx +1 -1
  364. package/lib/theme/Typography.mdx +1 -1
  365. package/lib/theme/__tests__/fonts.test.ts +37 -0
  366. package/lib/theme/common/themeCommon.ts +515 -0
  367. package/lib/theme/fonts.ts +110 -0
  368. package/lib/theme/index.ts +6 -6
  369. package/lib/theme/light/lightColour.ts +232 -0
  370. package/lib/theme/light/lightTheme.ts +37 -0
  371. package/lib/theme/{defaultTheme.ts → original/color.ts} +17 -199
  372. package/lib/theme/original/defaultTheme.ts +207 -0
  373. package/lib/theme/original/originalColourNewStructure.ts +185 -0
  374. package/lib/theme/useTheme.tsx +72 -15
  375. package/lib/types/assets.d.ts +10 -0
  376. package/lib/utils/__tests__/announce.test.ts +121 -0
  377. package/lib/utils/addAlphaToHex.ts +29 -0
  378. package/lib/utils/announce.ts +134 -0
  379. package/lib/utils/index.ts +1 -0
  380. package/lib/utils/scrollToTop.ts +5 -0
  381. package/package.json +11 -9
  382. package/dist/components/Button/buttonPrimaryStyle.d.ts +0 -4
  383. package/dist/components/Button/buttonSecondaryStyle.d.ts +0 -4
  384. package/dist/components/Button/buttonTertiaryStyle.d.ts +0 -4
  385. package/dist/components/Datepicker/subcomponents/NativeDatepicker.d.ts +0 -6
  386. package/dist/theme/defaultTheme.d.ts +0 -274
  387. package/lib/components/Accordion/Accordion.stories.tsx.NOT_READY +0 -93
  388. package/lib/components/Button/buttonPrimaryStyle.ts +0 -62
  389. package/lib/components/Button/buttonSecondaryStyle.ts +0 -65
  390. package/lib/components/Button/buttonTertiaryStyle.ts +0 -54
  391. /package/dist/components/{Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts → FooterNew/__tests__/Footer.test.d.ts} +0 -0
  392. /package/lib/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.test.ts +0 -0
@@ -1,5 +1,6 @@
1
- import type { InputHTMLAttributes, HTMLAttributes, RefObject } from 'react';
1
+ import type { HTMLAttributes, RefObject } from 'react';
2
2
  import type { CalendarEvent, AcademicWeek } from '../Calendar';
3
+ import type { InputProps } from './subcomponents/DatepickerInput';
3
4
 
4
5
  export type DatepickerValue = Date | null;
5
6
 
@@ -16,23 +17,13 @@ interface BaseDatepickerProps {
16
17
  academicWeeks?: AcademicWeek[];
17
18
  testId?: string;
18
19
  disabled?: boolean;
20
+ clearable?: boolean;
19
21
  ref?: RefObject<HTMLDivElement>;
20
22
  inputRef?: RefObject<HTMLInputElement>;
23
+ inputProps?: InputProps;
21
24
  }
22
25
 
23
- // Valid HTML attributes for <input type="date">
24
- type NativeDatepickerAttributeProps = Omit<
25
- InputHTMLAttributes<HTMLInputElement>,
26
- // Remove shared 'top-level' & handled props
27
- 'value' | 'onChange' | 'min' | 'max' | 'className' | 'disabled'
28
- >;
29
-
30
26
  // `className?: string` is received automatically from `HTMLAttributes<HTMLDivElement>`
31
27
  export interface DatepickerProps
32
28
  extends BaseDatepickerProps,
33
- HTMLAttributes<HTMLDivElement> {
34
- native?: boolean;
35
- // Attached as a nested object
36
- nativeHTMLAttributes?: NativeDatepickerAttributeProps;
37
- nativeRef?: RefObject<HTMLInputElement>;
38
- }
29
+ HTMLAttributes<HTMLDivElement> {}
@@ -1,7 +1,13 @@
1
- import { describe, test, expect, vi } from 'vitest';
1
+ import { useState } from 'react';
2
+ import { describe, test, expect, vi, beforeEach } from 'vitest';
2
3
  import { render, screen, fireEvent } from '@testing-library/react';
4
+ import userEvent from '@testing-library/user-event';
3
5
  import { ThemeContextProvider } from '../../../theme';
4
6
  import Datepicker from '..';
7
+ import announce from '../../../utils/announce';
8
+
9
+ // Mock the announce utility
10
+ vi.mock('../../../utils/announce');
5
11
 
6
12
  const defaultTestId = 'ucl-uikit-datepicker';
7
13
 
@@ -14,8 +20,11 @@ const customRender = (ui: React.ReactElement) => {
14
20
  };
15
21
 
16
22
  describe('Datepicker', () => {
17
- // Snapshot tests
23
+ beforeEach(() => {
24
+ vi.clearAllMocks();
25
+ });
18
26
 
27
+ // #region Snapshot tests
19
28
  test('Snapshot: no date provided', () => {
20
29
  const renderResult = customRender(<Datepicker />);
21
30
  expect(renderResult.container.firstChild).toMatchSnapshot();
@@ -23,13 +32,13 @@ describe('Datepicker', () => {
23
32
 
24
33
  test('Snapshot: with date provided', () => {
25
34
  const renderResult = customRender(
26
- <Datepicker value={new Date('2025-03-10')} />
35
+ <Datepicker value={new Date(2025, 2, 10)} />
27
36
  );
28
37
  expect(renderResult.container.firstChild).toMatchSnapshot();
29
38
  });
39
+ // #endregion
30
40
 
31
- // Unit tests
32
-
41
+ // #region Functional tests
33
42
  test('Can be found via default test ID', () => {
34
43
  customRender(<Datepicker />);
35
44
  const datepicker = screen.getByTestId(defaultTestId);
@@ -43,8 +52,55 @@ describe('Datepicker', () => {
43
52
  expect(datepicker).toBeInTheDocument();
44
53
  });
45
54
 
55
+ test('Parses date on ENTER key press', async () => {
56
+ const onValueChange = vi.fn();
57
+ const user = userEvent.setup();
58
+ customRender(<Datepicker onValueChange={onValueChange} />);
59
+
60
+ // Internal <input> (the user types into)
61
+ const datepickerInput = screen.getByRole('textbox');
62
+ await user.type(datepickerInput, '10/03/2025');
63
+ // Internal input holds typed value before parsing
64
+ expect(datepickerInput).toHaveValue('10/03/2025');
65
+
66
+ // Wait for ENTER key press to trigger parsing
67
+ expect(onValueChange).not.toHaveBeenCalled();
68
+
69
+ // Trigger user-entered date parsing
70
+ await user.keyboard('{Enter}');
71
+
72
+ expect(onValueChange).toHaveBeenCalledWith(
73
+ new Date('2025-03-10'),
74
+ expect.anything() // 2nd parameter is the event object (which we're not testing here)
75
+ );
76
+ });
77
+
78
+ test('Parses date on TAB key press', async () => {
79
+ const onValueChange = vi.fn();
80
+ const user = userEvent.setup();
81
+ customRender(<Datepicker onValueChange={onValueChange} />);
82
+
83
+ // Internal <input> (the user types into)
84
+ const datepickerInput = screen.getByRole('textbox');
85
+ await user.type(datepickerInput, '10/03/2025');
86
+ // Internal input holds typed value before parsing
87
+ expect(datepickerInput).toHaveValue('10/03/2025');
88
+
89
+ // Wait for TAB key press to trigger parsing
90
+ expect(onValueChange).not.toHaveBeenCalled();
91
+
92
+ // Trigger user-entered date parsing
93
+ await user.keyboard('{Tab}');
94
+
95
+ expect(onValueChange).toHaveBeenCalledWith(
96
+ new Date('2025-03-10'),
97
+ expect.anything() // 2nd parameter is the event object (which we're not testing here)
98
+ );
99
+ });
100
+
46
101
  test('Input value reflects the date', () => {
47
- customRender(<Datepicker value={new Date('2025-03-10')} />);
102
+ // 10th March 2025 (2 is March, because month is 0-indexed in Date constructor)
103
+ customRender(<Datepicker value={new Date(2025, 2, 10)} />);
48
104
  const input = screen.getByTestId('ucl-uikit-datepicker__input');
49
105
  expect(input).toHaveValue('10/03/2025');
50
106
  });
@@ -66,7 +122,7 @@ describe('Datepicker', () => {
66
122
  const onValueChange = vi.fn();
67
123
  customRender(
68
124
  <Datepicker
69
- value={new Date('2025-01-10')} // Before minDate
125
+ value={new Date(2025, 0, 10)} // Before minDate (0 is January, because month is 0-indexed in Date constructor)
70
126
  minDate='2025-02-01'
71
127
  onValueChange={onValueChange}
72
128
  />
@@ -78,11 +134,214 @@ describe('Datepicker', () => {
78
134
  const onValueChange = vi.fn();
79
135
  customRender(
80
136
  <Datepicker
81
- value={new Date('2025-03-10')} // After maxDate
137
+ value={new Date(2025, 2, 10)} // After maxDate (2 is March, because month is 0-indexed in Date constructor)
82
138
  maxDate='2025-01-31'
83
139
  onValueChange={onValueChange}
84
140
  />
85
141
  );
86
142
  expect(onValueChange).toHaveBeenCalledWith(null);
87
143
  });
144
+
145
+ test('Input can receive additional HTML attributes via inputProps', () => {
146
+ customRender(
147
+ <Datepicker
148
+ inputProps={{
149
+ 'aria-describedby': 'customValue',
150
+ 'aria-required': 'true',
151
+ }}
152
+ />
153
+ );
154
+ const input = screen.getByTestId('ucl-uikit-datepicker__input');
155
+ expect(input).toHaveAttribute('aria-describedby', 'customValue');
156
+ expect(input).toHaveAttribute('aria-required', 'true');
157
+ });
158
+ // #endregion
159
+
160
+ // #region Accessibility tests
161
+ test("Input can be found via getByRole('textbox')", () => {
162
+ customRender(<Datepicker />);
163
+ const input = screen.getByRole('textbox');
164
+ expect(input).toBeInTheDocument();
165
+ });
166
+
167
+ test('Announces date on input text parse', () => {
168
+ customRender(<Datepicker />);
169
+ const input = screen.getByTestId('ucl-uikit-datepicker__input');
170
+ fireEvent.change(input, { target: { value: '15/03/2026' } });
171
+ fireEvent.keyDown(input, { key: 'Enter' });
172
+
173
+ expect(announce).toHaveBeenCalledWith(
174
+ 'Selected date ' +
175
+ // March is month 2 in 0-indexed Date constructor
176
+ new Date(2026, 2, 15).toLocaleDateString('en-GB', {
177
+ weekday: 'long',
178
+ day: 'numeric',
179
+ month: 'long',
180
+ year: 'numeric',
181
+ })
182
+ );
183
+ });
184
+
185
+ test("Announces picked date on Calendar 'pick'", () => {
186
+ customRender(<Datepicker value={new Date(2026, 2, 10)} />);
187
+ // TODO: Separate this button from the 'clear date' button - [Datepicker_improv_005]
188
+ const openCalendarButton = screen.getByTestId(
189
+ 'ucl-uikit-datepicker__visible-field-icon-button'
190
+ );
191
+ fireEvent.click(openCalendarButton);
192
+
193
+ const days = screen.getAllByTestId('ucl-uikit-calendar__day');
194
+ // Pick 15th March 2026:
195
+ // 6 days of previous month
196
+ // + 15 days of current month (1st to 15th)
197
+ // - 1 because of 0-indexing in the array
198
+ const dateArrayIndex = 6 + 15 - 1;
199
+ fireEvent.click(days[dateArrayIndex]);
200
+
201
+ expect(announce).toHaveBeenCalledWith(
202
+ 'Selected date ' +
203
+ // March is month 2 in 0-indexed Date constructor
204
+ new Date(2026, 2, 15).toLocaleDateString('en-GB', {
205
+ weekday: 'long',
206
+ day: 'numeric',
207
+ month: 'long',
208
+ year: 'numeric',
209
+ })
210
+ );
211
+ });
212
+
213
+ test('Input can be found via label text when associated via id', () => {
214
+ customRender(
215
+ <>
216
+ <label htmlFor='my-datepicker'>Date of event</label>
217
+ <Datepicker inputProps={{ id: 'my-datepicker' }} />
218
+ </>
219
+ );
220
+ const input = screen.getByLabelText('Date of event');
221
+ expect(input).toHaveAttribute('data-testid', 'ucl-uikit-datepicker__input');
222
+ });
223
+
224
+ test('Custom aria-label can be set via inputProps', () => {
225
+ const customAriaLabel = 'Custom aria label';
226
+ customRender(<Datepicker inputProps={{ 'aria-label': customAriaLabel }} />);
227
+ const input = screen.getByTestId('ucl-uikit-datepicker__input');
228
+ expect(input).toHaveAttribute('aria-label', customAriaLabel);
229
+ });
230
+
231
+ test('Default Datepicker does not add an extra icon tab stop', async () => {
232
+ const user = userEvent.setup();
233
+
234
+ customRender(
235
+ <div>
236
+ <button type='button'>Before</button>
237
+ <Datepicker />
238
+ <button type='button'>After</button>
239
+ </div>
240
+ );
241
+
242
+ await user.tab();
243
+ expect(screen.getByRole('button', { name: 'Before' })).toHaveFocus();
244
+
245
+ await user.tab();
246
+ expect(screen.getByRole('textbox')).toHaveFocus();
247
+
248
+ await user.tab();
249
+ expect(screen.getByRole('button', { name: 'After' })).toHaveFocus();
250
+ });
251
+
252
+ test('Clearable Datepicker clear icon is focusable when a value is present', async () => {
253
+ const user = userEvent.setup();
254
+
255
+ customRender(
256
+ <div>
257
+ <button type='button'>Before</button>
258
+ <Datepicker
259
+ clearable
260
+ value={new Date(2025, 2, 10)}
261
+ />
262
+ <button type='button'>After</button>
263
+ </div>
264
+ );
265
+
266
+ await user.tab();
267
+ expect(screen.getByRole('button', { name: 'Before' })).toHaveFocus();
268
+
269
+ await user.tab();
270
+ expect(screen.getByRole('textbox')).toHaveFocus();
271
+
272
+ await user.tab();
273
+ expect(screen.getByRole('button', { name: 'Clear date' })).toHaveFocus();
274
+
275
+ await user.tab();
276
+ expect(screen.getByRole('button', { name: 'After' })).toHaveFocus();
277
+ });
278
+
279
+ test('Clearing with the keyboard restores focus to the input', async () => {
280
+ const user = userEvent.setup();
281
+
282
+ const ControlledDatepicker = () => {
283
+ const [value, setValue] = useState<Date | null>(new Date(2025, 2, 10));
284
+
285
+ return (
286
+ <Datepicker
287
+ clearable
288
+ value={value}
289
+ onValueChange={setValue}
290
+ />
291
+ );
292
+ };
293
+
294
+ customRender(<ControlledDatepicker />);
295
+
296
+ await user.tab();
297
+ expect(screen.getByRole('textbox')).toHaveFocus();
298
+
299
+ await user.tab();
300
+ const clearButton = screen.getByRole('button', { name: 'Clear date' });
301
+ expect(clearButton).toHaveFocus();
302
+
303
+ await user.keyboard('{Enter}');
304
+
305
+ expect(screen.getByRole('textbox')).toHaveFocus();
306
+ expect(
307
+ screen.queryByRole('button', { name: 'Clear date' })
308
+ ).not.toBeInTheDocument();
309
+ });
310
+
311
+ test('Disabled Datepicker is skipped when tabbing', async () => {
312
+ const user = userEvent.setup();
313
+
314
+ customRender(
315
+ <div>
316
+ <button type='button'>Before</button>
317
+ <Datepicker disabled />
318
+ <button type='button'>After</button>
319
+ </div>
320
+ );
321
+
322
+ await user.tab();
323
+ expect(screen.getByRole('button', { name: 'Before' })).toHaveFocus();
324
+
325
+ await user.tab();
326
+ expect(screen.getByRole('button', { name: 'After' })).toHaveFocus();
327
+ });
328
+
329
+ test('Disabled Datepicker icon control is not tabbable and cannot open', async () => {
330
+ const user = userEvent.setup();
331
+
332
+ customRender(<Datepicker disabled />);
333
+
334
+ const iconButton = screen.getByTestId(
335
+ 'ucl-uikit-datepicker__visible-field-icon-button'
336
+ );
337
+
338
+ expect(iconButton).toHaveAttribute('aria-label', 'Open calendar');
339
+ expect(iconButton).toHaveAttribute('tabindex', '-1');
340
+ expect(iconButton).toBeDisabled();
341
+
342
+ await user.click(iconButton);
343
+
344
+ expect(screen.queryByTestId('ucl-uikit-calendar')).not.toBeInTheDocument();
345
+ });
346
+ // #endregion
88
347
  });
@@ -10,7 +10,6 @@ exports[`Datepicker > Snapshot: no date provided 1`] = `
10
10
  data-testid="ucl-uikit-datepicker__visible-field"
11
11
  >
12
12
  <input
13
- aria-label="Currently selected date: "
14
13
  class="ucl-uikit-datepicker__input css-1k3pose"
15
14
  data-testid="ucl-uikit-datepicker__input"
16
15
  inputmode="numeric"
@@ -18,13 +17,18 @@ exports[`Datepicker > Snapshot: no date provided 1`] = `
18
17
  type="text"
19
18
  value=""
20
19
  />
21
- <div
22
- class="css-3tb0p0"
20
+ <button
21
+ aria-label="Open calendar"
22
+ class="ucl-uikit-icon-button css-1uz96ir"
23
+ data-testid="ucl-uikit-datepicker__visible-field-icon-button"
24
+ tabindex="-1"
25
+ type="button"
23
26
  >
24
27
  <svg
25
28
  class="ucl-uikit-icon css-1u4xgls"
26
29
  data-testid="ucl-uikit-icon"
27
30
  fill="none"
31
+ focusable="false"
28
32
  height="24"
29
33
  stroke="currentColor"
30
34
  stroke-linecap="round"
@@ -42,26 +46,11 @@ exports[`Datepicker > Snapshot: no date provided 1`] = `
42
46
  x="3"
43
47
  y="4"
44
48
  />
45
- <line
46
- x1="16"
47
- x2="16"
48
- y1="2"
49
- y2="6"
50
- />
51
- <line
52
- x1="8"
53
- x2="8"
54
- y1="2"
55
- y2="6"
56
- />
57
- <line
58
- x1="3"
59
- x2="21"
60
- y1="10"
61
- y2="10"
49
+ <path
50
+ d="M16 2v4M8 2v4m-5 4h18"
62
51
  />
63
52
  </svg>
64
- </div>
53
+ </button>
65
54
  </div>
66
55
  </div>
67
56
  `;
@@ -76,7 +65,6 @@ exports[`Datepicker > Snapshot: with date provided 1`] = `
76
65
  data-testid="ucl-uikit-datepicker__visible-field"
77
66
  >
78
67
  <input
79
- aria-label="Currently selected date: 10/03/2025"
80
68
  class="ucl-uikit-datepicker__input css-1k3pose"
81
69
  data-testid="ucl-uikit-datepicker__input"
82
70
  inputmode="numeric"
@@ -84,13 +72,18 @@ exports[`Datepicker > Snapshot: with date provided 1`] = `
84
72
  type="text"
85
73
  value="10/03/2025"
86
74
  />
87
- <div
88
- class="css-3tb0p0"
75
+ <button
76
+ aria-label="Open calendar"
77
+ class="ucl-uikit-icon-button css-1uz96ir"
78
+ data-testid="ucl-uikit-datepicker__visible-field-icon-button"
79
+ tabindex="-1"
80
+ type="button"
89
81
  >
90
82
  <svg
91
83
  class="ucl-uikit-icon css-1u4xgls"
92
84
  data-testid="ucl-uikit-icon"
93
85
  fill="none"
86
+ focusable="false"
94
87
  height="24"
95
88
  stroke="currentColor"
96
89
  stroke-linecap="round"
@@ -108,26 +101,11 @@ exports[`Datepicker > Snapshot: with date provided 1`] = `
108
101
  x="3"
109
102
  y="4"
110
103
  />
111
- <line
112
- x1="16"
113
- x2="16"
114
- y1="2"
115
- y2="6"
116
- />
117
- <line
118
- x1="8"
119
- x2="8"
120
- y1="2"
121
- y2="6"
122
- />
123
- <line
124
- x1="3"
125
- x2="21"
126
- y1="10"
127
- y2="10"
104
+ <path
105
+ d="M16 2v4M8 2v4m-5 4h18"
128
106
  />
129
107
  </svg>
130
- </div>
108
+ </button>
131
109
  </div>
132
110
  </div>
133
111
  `;
@@ -3,8 +3,10 @@ import { css, cx } from '@emotion/css';
3
3
  import { VisibleField, Panel } from '.';
4
4
  import { Calendar } from '../..';
5
5
  import { parseInputValue } from '../utils';
6
+ import announce from '../../../utils/announce';
6
7
  import type { DatepickerValue } from '../Datepicker.types';
7
8
  import type { CalendarEvent, AcademicWeek } from '../../Calendar';
9
+ import type { InputProps } from './DatepickerInput';
8
10
 
9
11
  interface CustomDatepickerProps extends React.HTMLAttributes<HTMLDivElement> {
10
12
  value?: DatepickerValue;
@@ -15,12 +17,14 @@ interface CustomDatepickerProps extends React.HTMLAttributes<HTMLDivElement> {
15
17
  minDate?: string | null; // ISO date string: YYYY-MM-DD
16
18
  maxDate?: string | null; // ISO date string: YYYY-MM-DD
17
19
  disabled?: boolean;
20
+ clearable?: boolean;
18
21
  events?: CalendarEvent[];
19
22
  showAcademicWeeks?: boolean;
20
23
  academicWeeks?: AcademicWeek[];
21
24
  testId?: string;
22
25
  ref?: React.RefObject<HTMLDivElement>;
23
26
  inputRef?: React.RefObject<HTMLInputElement>;
27
+ inputProps?: InputProps;
24
28
  }
25
29
 
26
30
  const NAME = 'ucl-uikit-datepicker';
@@ -31,6 +35,7 @@ const CustomDatepicker = ({
31
35
  minDate,
32
36
  maxDate,
33
37
  disabled = false,
38
+ clearable = false,
34
39
  events,
35
40
  showAcademicWeeks,
36
41
  academicWeeks = [],
@@ -38,6 +43,7 @@ const CustomDatepicker = ({
38
43
  className,
39
44
  ref,
40
45
  inputRef,
46
+ inputProps = {},
41
47
  ...props
42
48
  }: CustomDatepickerProps) => {
43
49
  if (value && isNaN(value.getTime())) {
@@ -49,6 +55,7 @@ const CustomDatepicker = ({
49
55
  const effectiveRef = ref || internalRef;
50
56
  const internalInputRef = useRef<HTMLInputElement>(null);
51
57
  const effectiveInputRef = inputRef || internalInputRef;
58
+ const restoreFocusAfterClearRef = useRef(false);
52
59
 
53
60
  const [panelOpen, setPanelOpen] = useState(false);
54
61
 
@@ -69,15 +76,31 @@ const CustomDatepicker = ({
69
76
  resetField();
70
77
  }, [value, resetField]);
71
78
 
79
+ useEffect(() => {
80
+ if (!value && restoreFocusAfterClearRef.current) {
81
+ effectiveInputRef.current?.focus();
82
+ restoreFocusAfterClearRef.current = false;
83
+ }
84
+ }, [value, effectiveInputRef]);
85
+
72
86
  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
73
87
  setInputValue(event.target.value);
74
88
  };
75
89
 
76
90
  const handleParseInput = (event: React.SyntheticEvent) => {
77
91
  // `parseInputValue` checks the date is valid and within min/max range.
78
- const parseDate = parseInputValue(inputValue, minDate, maxDate);
79
- if (parseDate) {
80
- onValueChange(parseDate, event);
92
+ const parsedDate = parseInputValue(inputValue, minDate, maxDate);
93
+ if (parsedDate) {
94
+ announce(
95
+ 'Selected date ' +
96
+ parsedDate.toLocaleDateString('en-GB', {
97
+ weekday: 'long',
98
+ day: 'numeric',
99
+ month: 'long',
100
+ year: 'numeric',
101
+ })
102
+ );
103
+ onValueChange(parsedDate, event);
81
104
  } else {
82
105
  resetField();
83
106
  }
@@ -91,7 +114,8 @@ const CustomDatepicker = ({
91
114
  effectiveInputRef.current?.blur();
92
115
  setPanelOpen(false);
93
116
  } else if (event.key === 'Tab') {
94
- resetField();
117
+ // Parse input on TAB (same as ENTER) for better accessibility
118
+ handleParseInput(event);
95
119
  setPanelOpen(false);
96
120
  }
97
121
  };
@@ -149,6 +173,15 @@ const CustomDatepicker = ({
149
173
  event?: React.SyntheticEvent
150
174
  ) => {
151
175
  if (onValueChange) {
176
+ announce(
177
+ 'Selected date ' +
178
+ date?.toLocaleDateString('en-GB', {
179
+ weekday: 'long',
180
+ day: 'numeric',
181
+ month: 'long',
182
+ year: 'numeric',
183
+ })
184
+ );
152
185
  onValueChange(date, event);
153
186
  }
154
187
  setPanelOpen(false);
@@ -164,6 +197,12 @@ const CustomDatepicker = ({
164
197
  setPanelOpen((prev) => !prev);
165
198
  };
166
199
 
200
+ const handleClear = (event: React.SyntheticEvent) => {
201
+ event.stopPropagation(); // Prevent opening the calendar panel
202
+ restoreFocusAfterClearRef.current = true;
203
+ onValueChange(null, event);
204
+ };
205
+
167
206
  const baseStyle = css`
168
207
  width: 196px;
169
208
  height: 48px;
@@ -171,7 +210,7 @@ const CustomDatepicker = ({
171
210
  position: relative;
172
211
  `;
173
212
 
174
- const style = cx(NAME, className, baseStyle);
213
+ const style = cx(NAME, baseStyle, className);
175
214
 
176
215
  return (
177
216
  <div
@@ -186,8 +225,12 @@ const CustomDatepicker = ({
186
225
  onInputKeyDown={handleInputKeyDown}
187
226
  onInputFocus={handleShowPanel}
188
227
  onButtonClick={handleTogglePanel}
228
+ onClear={handleClear}
189
229
  disabled={disabled}
230
+ clearable={clearable}
231
+ hasValue={!!value}
190
232
  inputRef={effectiveInputRef}
233
+ inputProps={inputProps}
191
234
  />
192
235
  {panelOpen && (
193
236
  <Panel>
@@ -1,7 +1,24 @@
1
1
  import { css, cx } from '@emotion/css';
2
2
  import { useTheme } from '../../../theme';
3
3
 
4
- interface DatepickerInputProps {
4
+ /**
5
+ * HTML attributes that consumers can pass to the underlying `<input>` via the
6
+ * `inputProps` prop on `<Datepicker>`.
7
+ *
8
+ * Controlled and internally-managed attributes are omitted to prevent conflicts.
9
+ */
10
+ export type InputProps = Omit<
11
+ React.InputHTMLAttributes<HTMLInputElement>,
12
+ 'value' | 'onChange' | 'onKeyDown' | 'onFocus' | 'disabled' | 'type'
13
+ > & {
14
+ testId?: string;
15
+ };
16
+
17
+ /**
18
+ * Internal props for `<DatepickerInput>`, combining consumer-facing
19
+ * input attributes with controlled props managed by `<VisibleField>`.
20
+ */
21
+ interface DatepickerInputProps extends InputProps {
5
22
  value: string;
6
23
  onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
7
24
  onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
@@ -13,12 +30,12 @@ interface DatepickerInputProps {
13
30
  const NAME = 'ucl-uikit-datepicker__input';
14
31
 
15
32
  const DatepickerInput = ({
16
- value,
17
- onChange,
18
- onKeyDown,
19
- onFocus,
20
33
  disabled,
21
- ref,
34
+ placeholder = 'DD/MM/YYYY',
35
+ inputMode = 'numeric',
36
+ testId = NAME,
37
+ className,
38
+ ...props
22
39
  }: DatepickerInputProps) => {
23
40
  const [theme] = useTheme();
24
41
 
@@ -51,22 +68,18 @@ const DatepickerInput = ({
51
68
  }
52
69
  `;
53
70
 
54
- const style = cx(NAME, baseStyle, disabled && disabledStyle);
71
+ const style = cx(NAME, baseStyle, disabled && disabledStyle, className);
55
72
 
56
73
  return (
57
74
  <input
58
- value={value}
59
- onChange={onChange}
60
- onKeyDown={onKeyDown}
61
- onFocus={onFocus}
75
+ placeholder={placeholder}
76
+ inputMode={inputMode}
77
+ data-testid={testId}
78
+ className={style}
79
+ {...props}
80
+ // Controlled attributes -- not exposed to consumers to prevent conflicts
62
81
  disabled={disabled}
63
82
  type='text'
64
- inputMode='numeric'
65
- placeholder='DD/MM/YYYY'
66
- className={style}
67
- data-testid={NAME}
68
- ref={ref}
69
- aria-label={`Currently selected date: ${value}`}
70
83
  />
71
84
  );
72
85
  };