uikit-react-public 0.11.24 → 0.17.4

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 (301) hide show
  1. package/README.md +4 -2
  2. package/dist/components/Accordion/Accordion.Heading.d.ts +4 -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/Avatar/Avatar.stories.d.ts +107 -1
  8. package/dist/components/Badge/Badge.d.ts +6 -0
  9. package/dist/components/Badge/Badge.stories.d.ts +15 -0
  10. package/dist/components/Badge/index.d.ts +2 -0
  11. package/dist/components/Button/Button.d.ts +3 -1
  12. package/dist/components/Calendar/index.d.ts +1 -1
  13. package/dist/components/CookieNotice/CookieNotice.d.ts +16 -0
  14. package/dist/components/CookieNotice/index.d.ts +2 -0
  15. package/dist/components/Datepicker/Datepicker.d.ts +1 -1
  16. package/dist/components/Datepicker/Datepicker.stories.d.ts +4 -3
  17. package/dist/components/Datepicker/Datepicker.types.d.ts +4 -5
  18. package/dist/components/Datepicker/subcomponents/CustomDatepicker.d.ts +4 -1
  19. package/dist/components/Datepicker/subcomponents/DatepickerInput.d.ts +15 -2
  20. package/dist/components/Datepicker/subcomponents/Panel.d.ts +1 -1
  21. package/dist/components/Datepicker/subcomponents/VisibleField.d.ts +6 -1
  22. package/dist/components/Datepicker/subcomponents/index.d.ts +0 -1
  23. package/dist/components/Datepicker/utils/index.d.ts +0 -1
  24. package/dist/components/Dialog/BaseDialog.d.ts +8 -2
  25. package/dist/components/Dialog/Dialog.d.ts +2 -0
  26. package/dist/components/FileInput/FileInput.d.ts +8 -0
  27. package/dist/components/FileInput/FileInput.stories.d.ts +16 -0
  28. package/dist/components/FileInput/index.d.ts +2 -0
  29. package/dist/components/Header/Header.d.ts +7 -1
  30. package/dist/components/Header/Header.stories.d.ts +40 -0
  31. package/dist/components/Heading/Heading.d.ts +1 -1
  32. package/dist/components/Link/BaseLink.d.ts +10 -0
  33. package/dist/components/Link/Link.d.ts +5 -10
  34. package/dist/components/Link/Link.stories.d.ts +1 -1
  35. package/dist/components/Link/index.d.ts +1 -1
  36. package/dist/components/Main/Main.d.ts +21 -0
  37. package/dist/components/Main/Main.stories.d.ts +15 -0
  38. package/dist/components/Main/__tests__/Main.test.d.ts +1 -0
  39. package/dist/components/Main/index.d.ts +2 -0
  40. package/dist/components/Menu/MenuContent.d.ts +1 -1
  41. package/dist/components/Menu/MenuItem.d.ts +2 -0
  42. package/dist/components/Menu/MenuSection.d.ts +1 -1
  43. package/dist/components/NativeDatepicker/NativeDatepicker.d.ts +3 -0
  44. package/dist/components/NativeDatepicker/NativeDatepicker.stories.d.ts +36 -0
  45. package/dist/components/NativeDatepicker/NativeDatepicker.types.d.ts +10 -0
  46. package/dist/components/NativeDatepicker/index.d.ts +2 -0
  47. package/dist/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.d.ts +1 -1
  48. package/dist/components/NativeDatepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts +1 -0
  49. package/dist/components/NativeDatepicker/utils/index.d.ts +1 -0
  50. package/dist/components/Search/Search.d.ts +16 -0
  51. package/dist/components/Search/Search.stories.d.ts +34 -0
  52. package/dist/components/Search/__tests__/Search.test.d.ts +1 -0
  53. package/dist/components/Search/index.d.ts +2 -0
  54. package/dist/components/Select/Select.d.ts +1 -1
  55. package/dist/components/Select/Select.stories.d.ts +157 -9
  56. package/dist/components/Select/Select.types.d.ts +66 -32
  57. package/dist/components/Select/subcomponents/CustomOption.d.ts +1 -1
  58. package/dist/components/Select/subcomponents/CustomSelect.d.ts +3 -3
  59. package/dist/components/Select/subcomponents/FilterInput.d.ts +14 -0
  60. package/dist/components/Select/subcomponents/NativeSelect.d.ts +5 -1
  61. package/dist/components/Select/subcomponents/Panel.d.ts +1 -1
  62. package/dist/components/Select/subcomponents/VisibleField.d.ts +6 -4
  63. package/dist/components/Select/subcomponents/index.d.ts +1 -0
  64. package/dist/components/StandaloneLink/StandaloneLink.d.ts +12 -0
  65. package/dist/components/StandaloneLink/StandaloneLink.stories.d.ts +13 -0
  66. package/dist/components/StandaloneLink/__tests__/StandaloneLink.test.d.ts +1 -0
  67. package/dist/components/StandaloneLink/index.d.ts +2 -0
  68. package/dist/components/Table/Table.d.ts +10 -8
  69. package/dist/components/Table/Table.stories.d.ts +21 -0
  70. package/dist/components/Table/Table.types.d.ts +11 -0
  71. package/dist/components/Table/__tests__/Table.test.d.ts +1 -0
  72. package/dist/components/Table/index.d.ts +2 -1
  73. package/dist/components/Table/subcomponents/Body.d.ts +4 -0
  74. package/dist/components/Table/subcomponents/Cell/Cell.d.ts +12 -0
  75. package/dist/components/Table/subcomponents/Cell/Cell.stories.d.ts +313 -0
  76. package/dist/components/Table/subcomponents/Cell/CellContent.d.ts +10 -0
  77. package/dist/components/Table/subcomponents/Cell/__tests__/Cell.test.d.ts +1 -0
  78. package/dist/components/Table/subcomponents/Head.d.ts +4 -0
  79. package/dist/components/Table/subcomponents/HeadCell/HeadCell.d.ts +13 -0
  80. package/dist/components/Table/subcomponents/HeadCell/HeadCell.stories.d.ts +312 -0
  81. package/dist/components/Table/subcomponents/HeadCell/HeadCellContent.d.ts +10 -0
  82. package/dist/components/Table/subcomponents/HeadCell/__tests__/HeadCell.test.d.ts +1 -0
  83. package/dist/components/Table/subcomponents/Row.d.ts +5 -0
  84. package/dist/components/Table/subcomponents/SortIcon.d.ts +7 -0
  85. package/dist/components/Table/subcomponents/index.d.ts +10 -0
  86. package/dist/components/Tabs/Tab.d.ts +1 -1
  87. package/dist/components/Tabs/TabContext.d.ts +1 -0
  88. package/dist/components/Tabs/Tabs.d.ts +3 -1
  89. package/dist/components/Tabs/Tabs.stories.d.ts +3 -0
  90. package/dist/components/Timepicker/Timepicker.d.ts +10 -0
  91. package/dist/components/Timepicker/Timepicker.stories.d.ts +7 -0
  92. package/dist/components/Timepicker/__tests__/Timepicker.test.d.ts +1 -0
  93. package/dist/components/Timepicker/index.d.ts +2 -0
  94. package/dist/components/Timepicker/utils/convertDateToTimeString.d.ts +2 -0
  95. package/dist/components/Timepicker/utils/convertDateToTimeString.test.d.ts +1 -0
  96. package/dist/components/Timepicker/utils/index.d.ts +1 -0
  97. package/dist/components/WeekPicker/WeekPicker.d.ts +3 -0
  98. package/dist/components/WeekPicker/WeekPicker.stories.d.ts +41 -0
  99. package/dist/components/WeekPicker/WeekPicker.types.d.ts +16 -0
  100. package/dist/components/WeekPicker/index.d.ts +2 -0
  101. package/dist/components/WeekPicker/subcomponents/CustomDatepicker.d.ts +17 -0
  102. package/dist/components/WeekPicker/subcomponents/DatepickerInput.d.ts +13 -0
  103. package/dist/components/WeekPicker/subcomponents/VisibleField.d.ts +15 -0
  104. package/dist/components/WeekPicker/subcomponents/index.d.ts +3 -0
  105. package/dist/components/index.d.ts +19 -0
  106. package/dist/hooks/index.d.ts +2 -0
  107. package/dist/hooks/useFocusTrap.d.ts +10 -0
  108. package/dist/index.d.ts +2 -0
  109. package/dist/index.js +6460 -4607
  110. package/dist/theme/defaultTheme.d.ts +7 -0
  111. package/dist/theme/useTheme.d.ts +14 -0
  112. package/dist/utils/__tests__/announce.test.d.ts +1 -0
  113. package/dist/utils/__tests__/capitalise.test.d.ts +1 -0
  114. package/dist/utils/announce.d.ts +6 -0
  115. package/dist/utils/capitalise.d.ts +2 -0
  116. package/dist/utils/index.d.ts +1 -0
  117. package/lib/components/Accordion/Accordion.Heading.tsx +27 -8
  118. package/lib/components/Accordion/Accordion.Panel.tsx +11 -3
  119. package/lib/components/Accordion/Accordion.stories.tsx +139 -0
  120. package/lib/components/Accordion/Accordion.tsx +10 -8
  121. package/lib/components/Accordion/__tests__/__snapshots__/Accordion.test.tsx.snap +7 -7
  122. package/lib/components/Accordion/index.ts +2 -0
  123. package/lib/components/Alert/Alert.stories.tsx +1 -1
  124. package/lib/components/Alert/Alert.tsx +7 -1
  125. package/lib/components/Alert/__tests__/__snapshots__/Alert.test.tsx.snap +4 -0
  126. package/lib/components/Avatar/Avatar.mdx +117 -0
  127. package/lib/components/Avatar/Avatar.stories.tsx +110 -2
  128. package/lib/components/Badge/Badge.stories.tsx +19 -0
  129. package/lib/components/Badge/Badge.tsx +48 -0
  130. package/lib/components/Badge/index.ts +2 -0
  131. package/lib/components/Blanket/Blanket.stories.tsx +1 -1
  132. package/lib/components/Breadcrumbs/__tests__/__snapshots__/Breadcrumbs.test.tsx.snap +4 -4
  133. package/lib/components/Button/Button.stories.tsx +1 -1
  134. package/lib/components/Button/Button.tsx +6 -2
  135. package/lib/components/Calendar/Calendar.stories.tsx +12 -32
  136. package/lib/components/Calendar/__tests__/Calendar.test.tsx +23 -15
  137. package/lib/components/Calendar/index.ts +1 -5
  138. package/lib/components/Calendar/subcomponents/AcademicWeeks.tsx +2 -1
  139. package/lib/components/Calendar/subcomponents/ColumnHeading.tsx +5 -1
  140. package/lib/components/Calendar/subcomponents/EventDot.tsx +2 -1
  141. package/lib/components/Calendar/subcomponents/Grid.tsx +0 -1
  142. package/lib/components/Calendar/subcomponents/index.ts +1 -1
  143. package/lib/components/Calendar/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.ts +43 -11
  144. package/lib/components/Calendar/utils/normaliseMonth/normaliseMonth.test.ts +5 -5
  145. package/lib/components/CookieNotice/CookieNotice.tsx +114 -0
  146. package/lib/components/CookieNotice/index.ts +2 -0
  147. package/lib/components/Datepicker/Datepicker.lld.md +108 -0
  148. package/lib/components/Datepicker/Datepicker.stories.tsx +44 -5
  149. package/lib/components/Datepicker/Datepicker.tsx +14 -36
  150. package/lib/components/Datepicker/Datepicker.types.ts +5 -14
  151. package/lib/components/Datepicker/__tests__/Datepicker.test.tsx +150 -8
  152. package/lib/components/Datepicker/__tests__/__snapshots__/Datepicker.test.tsx.snap +10 -4
  153. package/lib/components/Datepicker/subcomponents/CustomDatepicker.tsx +39 -5
  154. package/lib/components/Datepicker/subcomponents/DatepickerInput.tsx +30 -17
  155. package/lib/components/Datepicker/subcomponents/Panel.tsx +6 -2
  156. package/lib/components/Datepicker/subcomponents/VisibleField.tsx +40 -3
  157. package/lib/components/Datepicker/subcomponents/index.ts +0 -1
  158. package/lib/components/Datepicker/utils/index.ts +0 -1
  159. package/lib/components/Dialog/BaseDialog.tsx +55 -4
  160. package/lib/components/Dialog/Dialog.tsx +8 -1
  161. package/lib/components/Dialog/DialogBody.tsx +5 -1
  162. package/lib/components/Dialog/DialogHeader.tsx +2 -1
  163. package/lib/components/Divider/Divider.stories.tsx +1 -1
  164. package/lib/components/Field/ErrorText.tsx +1 -0
  165. package/lib/components/Field/Field.stories.tsx +1 -1
  166. package/lib/components/Field/__tests__/Field.test.tsx +161 -148
  167. package/lib/components/FileInput/FileInput.stories.tsx +70 -0
  168. package/lib/components/FileInput/FileInput.tsx +68 -0
  169. package/lib/components/FileInput/__tests__/FileInput.test.tsx +99 -0
  170. package/lib/components/FileInput/__tests__/__snapshots__/FileInput.test.tsx.snap +91 -0
  171. package/lib/components/FileInput/index.ts +2 -0
  172. package/lib/components/Footer/Footer.stories.tsx +1 -1
  173. package/lib/components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap +28 -28
  174. package/lib/components/Header/Header.mdx +52 -0
  175. package/lib/components/Header/Header.stories.tsx +98 -0
  176. package/lib/components/Header/Header.tsx +65 -3
  177. package/lib/components/Header/__tests__/Header.test.tsx +17 -1
  178. package/lib/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +4 -4
  179. package/lib/components/Heading/Documentation.mdx +1 -1
  180. package/lib/components/Heading/Heading.stories.tsx +1 -1
  181. package/lib/components/Heading/Heading.tsx +1 -1
  182. package/lib/components/Heading/__tests__/Heading.test.tsx +7 -19
  183. package/lib/components/Heading/__tests__/__snapshots__/Heading.test.tsx.snap +7 -7
  184. package/lib/components/Icon/Icon.stories.tsx +1 -1
  185. package/lib/components/IconButton/IconButton.stories.tsx +1 -1
  186. package/lib/components/Input/Input.stories.tsx +1 -1
  187. package/lib/components/Label/Label.stories.tsx +1 -1
  188. package/lib/components/Label/Label.tsx +0 -2
  189. package/lib/components/Label/__tests__/__snapshots__/Label.test.tsx.snap +7 -7
  190. package/lib/components/Link/BaseLink.tsx +84 -0
  191. package/lib/components/Link/Link.tsx +72 -32
  192. package/lib/components/Link/__tests__/__snapshots__/link.test.tsx.snap +3 -3
  193. package/lib/components/Link/__tests__/link.test.tsx +6 -13
  194. package/lib/components/Link/index.ts +1 -1
  195. package/lib/components/Main/Main.stories.tsx +36 -0
  196. package/lib/components/Main/Main.tsx +46 -0
  197. package/lib/components/Main/__tests__/Main.test.tsx +80 -0
  198. package/lib/components/Main/__tests__/__snapshots__/Main.test.tsx.snap +33 -0
  199. package/lib/components/Main/index.ts +2 -0
  200. package/lib/components/Menu/Menu.context.tsx +3 -1
  201. package/lib/components/Menu/Menu.tsx +2 -2
  202. package/lib/components/Menu/MenuContent.tsx +5 -5
  203. package/lib/components/Menu/MenuItem.tsx +20 -3
  204. package/lib/components/Menu/MenuSection.tsx +4 -3
  205. package/lib/components/NativeDatepicker/NativeDatepicker.stories.tsx +100 -0
  206. package/lib/components/{Datepicker/subcomponents → NativeDatepicker}/NativeDatepicker.tsx +14 -15
  207. package/lib/components/NativeDatepicker/NativeDatepicker.types.ts +19 -0
  208. package/lib/components/NativeDatepicker/index.ts +2 -0
  209. package/lib/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.ts +1 -1
  210. package/lib/components/NativeDatepicker/utils/index.ts +1 -0
  211. package/lib/components/Pagination/PaginationControls.tsx +56 -15
  212. package/lib/components/Pagination/PaginationInfo.tsx +5 -1
  213. package/lib/components/Paragraph/Paragraph.stories.tsx +1 -1
  214. package/lib/components/Search/Search.stories.tsx +41 -0
  215. package/lib/components/Search/Search.tsx +170 -0
  216. package/lib/components/Search/__tests__/Search.test.tsx +112 -0
  217. package/lib/components/Search/__tests__/__snapshots__/Search.test.tsx.snap +179 -0
  218. package/lib/components/Search/index.ts +2 -0
  219. package/lib/components/Select/Select.mdx +169 -0
  220. package/lib/components/Select/Select.stories.tsx +198 -77
  221. package/lib/components/Select/Select.tsx +37 -13
  222. package/lib/components/Select/Select.types.ts +77 -54
  223. package/lib/components/Select/__tests__/Select.test.tsx +448 -7
  224. package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +3 -3
  225. package/lib/components/Select/subcomponents/CustomOption.tsx +24 -10
  226. package/lib/components/Select/subcomponents/CustomSelect.tsx +333 -52
  227. package/lib/components/Select/subcomponents/FilterInput.tsx +80 -0
  228. package/lib/components/Select/subcomponents/NativeSelect.tsx +13 -1
  229. package/lib/components/Select/subcomponents/Panel.tsx +4 -5
  230. package/lib/components/Select/subcomponents/VisibleField.tsx +36 -24
  231. package/lib/components/Select/subcomponents/index.tsx +1 -0
  232. package/lib/components/Snackbar/Snackbar.stories.tsx +1 -1
  233. package/lib/components/Spinner/Spinner.stories.tsx +1 -1
  234. package/lib/components/StandaloneLink/StandaloneLink.stories.tsx +32 -0
  235. package/lib/components/StandaloneLink/StandaloneLink.tsx +183 -0
  236. package/lib/components/StandaloneLink/__tests__/StandaloneLink.test.tsx +57 -0
  237. package/lib/components/StandaloneLink/__tests__/__snapshots__/StandaloneLink.test.tsx.snap +19 -0
  238. package/lib/components/StandaloneLink/index.ts +2 -0
  239. package/lib/components/Table/Table.stories.tsx +337 -0
  240. package/lib/components/Table/Table.tsx +42 -67
  241. package/lib/components/Table/Table.types.ts +14 -0
  242. package/lib/components/Table/__tests__/Table.test.tsx +121 -0
  243. package/lib/components/Table/__tests__/__snapshots__/Table.test.tsx.snap +210 -0
  244. package/lib/components/Table/index.ts +8 -1
  245. package/lib/components/Table/subcomponents/Body.tsx +18 -0
  246. package/lib/components/Table/subcomponents/Cell/Cell.stories.tsx +151 -0
  247. package/lib/components/Table/subcomponents/Cell/Cell.tsx +72 -0
  248. package/lib/components/Table/subcomponents/Cell/CellContent.tsx +91 -0
  249. package/lib/components/Table/subcomponents/Cell/__tests__/Cell.test.tsx +115 -0
  250. package/lib/components/Table/subcomponents/Cell/__tests__/__snapshots__/Cell.test.tsx.snap +107 -0
  251. package/lib/components/Table/subcomponents/Head.tsx +34 -0
  252. package/lib/components/Table/subcomponents/HeadCell/HeadCell.stories.tsx +85 -0
  253. package/lib/components/Table/subcomponents/HeadCell/HeadCell.tsx +99 -0
  254. package/lib/components/Table/subcomponents/HeadCell/HeadCellContent.tsx +61 -0
  255. package/lib/components/Table/subcomponents/HeadCell/__tests__/HeadCell.test.tsx +137 -0
  256. package/lib/components/Table/subcomponents/HeadCell/__tests__/__snapshots__/HeadCell.test.tsx.snap +110 -0
  257. package/lib/components/Table/subcomponents/Row.tsx +49 -0
  258. package/lib/components/Table/subcomponents/SortIcon.tsx +63 -0
  259. package/lib/components/Table/subcomponents/index.ts +14 -0
  260. package/lib/components/Tabs/Tab.tsx +3 -3
  261. package/lib/components/Tabs/TabContext.tsx +1 -0
  262. package/lib/components/Tabs/Tabs.stories.tsx +9 -3
  263. package/lib/components/Tabs/Tabs.tsx +10 -32
  264. package/lib/components/Tabs/__tests__/Tabs.test.tsx +10 -4
  265. package/lib/components/Tabs/__tests__/__snapshots__/Tabs.test.tsx.snap +32 -32
  266. package/lib/components/Textarea/Textarea.stories.tsx +1 -1
  267. package/lib/components/Timepicker/Timepicker.stories.tsx +43 -0
  268. package/lib/components/Timepicker/Timepicker.tsx +100 -0
  269. package/lib/components/Timepicker/__tests__/Timepicker.test.tsx +55 -0
  270. package/lib/components/Timepicker/__tests__/__snapshots__/Timepicker.test.tsx.snap +19 -0
  271. package/lib/components/Timepicker/index.tsx +2 -0
  272. package/lib/components/Timepicker/utils/convertDateToTimeString.test.ts +54 -0
  273. package/lib/components/Timepicker/utils/convertDateToTimeString.ts +10 -0
  274. package/lib/components/Timepicker/utils/index.ts +1 -0
  275. package/lib/components/Toggle/Toggle.stories.tsx +1 -1
  276. package/lib/components/Tooltip/Tooltip.stories.tsx +1 -1
  277. package/lib/components/WeekPicker/WeekPicker.stories.tsx +147 -0
  278. package/lib/components/WeekPicker/WeekPicker.tsx +26 -0
  279. package/lib/components/WeekPicker/WeekPicker.types.ts +21 -0
  280. package/lib/components/WeekPicker/index.ts +2 -0
  281. package/lib/components/WeekPicker/subcomponents/CustomDatepicker.tsx +298 -0
  282. package/lib/components/WeekPicker/subcomponents/DatepickerInput.tsx +111 -0
  283. package/lib/components/WeekPicker/subcomponents/VisibleField.tsx +126 -0
  284. package/lib/components/WeekPicker/subcomponents/index.ts +3 -0
  285. package/lib/components/common/Common.mdx +1 -1
  286. package/lib/components/index.ts +28 -2
  287. package/lib/hooks/index.ts +2 -0
  288. package/lib/hooks/useFocusTrap.ts +159 -0
  289. package/lib/index.ts +2 -0
  290. package/lib/theme/defaultTheme.ts +7 -0
  291. package/lib/utils/__tests__/announce.test.ts +121 -0
  292. package/lib/utils/__tests__/capitalise.test.ts +40 -0
  293. package/lib/utils/announce.ts +134 -0
  294. package/lib/utils/capitalise.ts +4 -0
  295. package/lib/utils/index.ts +1 -0
  296. package/package.json +3 -6
  297. package/dist/components/Datepicker/subcomponents/NativeDatepicker.d.ts +0 -6
  298. package/lib/components/Accordion/Accordion.stories.tsx.NOT_READY +0 -93
  299. package/lib/components/Field/__tests__/__snapshots__/Field.test.tsx.snap +0 -300
  300. /package/dist/components/{Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts → FileInput/__tests__/FileInput.test.d.ts} +0 -0
  301. /package/lib/components/{Datepicker → NativeDatepicker}/utils/dateToLocaleISOString/dateToLocaleISOString.test.ts +0 -0
@@ -1,4 +1,5 @@
1
- import { describe, expect, test } from 'vitest';
1
+ import { describe, expect, test, vi, beforeAll } from 'vitest';
2
+ import { useState } from 'react';
2
3
  import { render } from '@testing-library/react';
3
4
  import userEvent from '@testing-library/user-event';
4
5
  import Select from '../Select';
@@ -11,6 +12,13 @@ const defaultOptions = [
11
12
  ];
12
13
 
13
14
  describe('Select', () => {
15
+ beforeAll(() => {
16
+ Object.defineProperty(Element.prototype, 'scrollIntoView', {
17
+ value: vi.fn(),
18
+ writable: true,
19
+ });
20
+ });
21
+
14
22
  // Snapshot tests
15
23
 
16
24
  test('Snapshot: default', () => {
@@ -19,7 +27,7 @@ describe('Select', () => {
19
27
  <Select
20
28
  options={defaultOptions}
21
29
  value=''
22
- onChange={() => {}}
30
+ onValueChange={() => {}}
23
31
  />
24
32
  </ThemeContextProvider>
25
33
  );
@@ -35,7 +43,7 @@ describe('Select', () => {
35
43
  <Select
36
44
  options={defaultOptions}
37
45
  value=''
38
- onChange={() => {}}
46
+ onValueChange={() => {}}
39
47
  />
40
48
  </ThemeContextProvider>
41
49
  );
@@ -50,7 +58,7 @@ describe('Select', () => {
50
58
  native
51
59
  options={defaultOptions}
52
60
  value=''
53
- onChange={() => {}}
61
+ nativeHtmlAttributes={{ onChange: () => {} }}
54
62
  />
55
63
  </ThemeContextProvider>
56
64
  );
@@ -66,7 +74,7 @@ describe('Select', () => {
66
74
  <Select
67
75
  options={defaultOptions}
68
76
  value=''
69
- onChange={() => {}}
77
+ onValueChange={() => {}}
70
78
  />
71
79
  </ThemeContextProvider>
72
80
  );
@@ -91,7 +99,7 @@ describe('Select', () => {
91
99
  disabled
92
100
  options={defaultOptions}
93
101
  value=''
94
- onChange={() => {}}
102
+ onValueChange={() => {}}
95
103
  />
96
104
  </ThemeContextProvider>
97
105
  );
@@ -109,11 +117,444 @@ describe('Select', () => {
109
117
  native
110
118
  options={defaultOptions}
111
119
  value='1'
112
- onChange={() => {}}
120
+ nativeHtmlAttributes={{ onChange: () => {} }}
113
121
  />
114
122
  </ThemeContextProvider>
115
123
  );
116
124
  const select = renderResult.getByTestId('ucl-uikit-select--native');
117
125
  expect(select).toHaveProperty('disabled');
118
126
  });
127
+
128
+ test('Filters options when filterable is true', async () => {
129
+ const user = userEvent.setup();
130
+ const renderResult = render(
131
+ <ThemeContextProvider>
132
+ <Select
133
+ filterable
134
+ options={defaultOptions}
135
+ value=''
136
+ onValueChange={() => {}}
137
+ />
138
+ </ThemeContextProvider>
139
+ );
140
+ await user.click(renderResult.getByTestId('ucl-uikit-select'));
141
+ const input = renderResult.container.querySelector('input');
142
+ expect(input).not.toBeNull();
143
+ await user.type(input as HTMLInputElement, '2');
144
+
145
+ const options = await renderResult.findAllByRole('option');
146
+ expect(options.length).toBe(1);
147
+ expect(options[0].textContent).toBe('Option 2');
148
+ });
149
+
150
+ test('shows empty state when filter yields no options', async () => {
151
+ const user = userEvent.setup();
152
+ const result = render(
153
+ <ThemeContextProvider>
154
+ <Select
155
+ filterable
156
+ options={defaultOptions}
157
+ value=''
158
+ onValueChange={() => {}}
159
+ />
160
+ </ThemeContextProvider>
161
+ );
162
+
163
+ await user.click(result.getByTestId('ucl-uikit-select'));
164
+ await user.type(result.getByRole('searchbox'), 'zzz');
165
+
166
+ expect(result.getByText('No options')).toBeInTheDocument();
167
+ expect(result.queryAllByTestId('ucl-uikit-select__option')).toHaveLength(0);
168
+ });
169
+
170
+ test('clears filter text after selecting an option', async () => {
171
+ const user = userEvent.setup();
172
+ const ControlledSelect = () => {
173
+ const [value, setValue] = useState('');
174
+ return (
175
+ <Select
176
+ filterable
177
+ options={defaultOptions}
178
+ value={value}
179
+ onValueChange={(next) => setValue(next as string)}
180
+ />
181
+ );
182
+ };
183
+
184
+ const result = render(
185
+ <ThemeContextProvider>
186
+ <ControlledSelect />
187
+ </ThemeContextProvider>
188
+ );
189
+
190
+ await user.click(result.getByTestId('ucl-uikit-select'));
191
+ await user.type(result.getByRole('searchbox'), '2');
192
+ await user.click(result.getByText('Option 2'));
193
+
194
+ await user.click(result.getByTestId('ucl-uikit-select'));
195
+ expect(result.getByRole('searchbox')).toHaveValue('');
196
+ expect(result.getAllByTestId('ucl-uikit-select__option')).toHaveLength(
197
+ defaultOptions.length
198
+ );
199
+ });
200
+
201
+ test('clears filter text when dropdown closes', async () => {
202
+ const user = userEvent.setup();
203
+ const result = render(
204
+ <ThemeContextProvider>
205
+ <Select
206
+ filterable
207
+ options={defaultOptions}
208
+ value=''
209
+ onValueChange={() => {}}
210
+ />
211
+ </ThemeContextProvider>
212
+ );
213
+
214
+ await user.click(result.getByTestId('ucl-uikit-select'));
215
+ await user.type(result.getByRole('searchbox'), '1');
216
+
217
+ await user.click(document.body);
218
+ await user.click(result.getByTestId('ucl-uikit-select'));
219
+
220
+ expect(result.getByRole('searchbox')).toHaveValue('');
221
+ });
222
+
223
+ test('keyboard navigation uses filtered options', async () => {
224
+ const user = userEvent.setup();
225
+ const options = [
226
+ { label: 'Alpha', value: 'alpha' },
227
+ { label: 'Echo', value: 'echo' },
228
+ { label: 'Gamma', value: 'gamma' },
229
+ ];
230
+ const changeSpy = vi.fn();
231
+
232
+ const ControlledSelect = () => {
233
+ const [value, setValue] = useState('');
234
+ const handleChange = vi.fn((next, ev) => {
235
+ setValue(next as string);
236
+ changeSpy(next, ev);
237
+ });
238
+ return (
239
+ <Select
240
+ filterable
241
+ options={options}
242
+ value={value}
243
+ onValueChange={handleChange}
244
+ />
245
+ );
246
+ };
247
+
248
+ const result = render(
249
+ <ThemeContextProvider>
250
+ <ControlledSelect />
251
+ </ThemeContextProvider>
252
+ );
253
+
254
+ await user.click(result.getByTestId('ucl-uikit-select'));
255
+ await user.type(result.getByRole('searchbox'), 'a');
256
+ expect(result.getAllByTestId('ucl-uikit-select__option')).toHaveLength(2);
257
+
258
+ await user.keyboard('{ArrowDown}');
259
+ await user.keyboard('{ArrowDown}');
260
+ await user.keyboard('{ArrowDown}');
261
+
262
+ expect(changeSpy.mock.calls.map(([val]) => val)).toEqual([
263
+ 'alpha',
264
+ 'gamma',
265
+ 'alpha',
266
+ ]);
267
+ });
268
+
269
+ test('selectionBehaviour=commit does not call onValueChange on arrow keys', async () => {
270
+ const user = userEvent.setup();
271
+ const changeSpy = vi.fn();
272
+
273
+ const ControlledSelect = () => {
274
+ const [value, setValue] = useState('');
275
+ return (
276
+ <Select
277
+ options={defaultOptions}
278
+ value={value}
279
+ selectionBehaviour='commit'
280
+ onValueChange={(next, ev) => {
281
+ setValue(next as string);
282
+ changeSpy(next, ev);
283
+ }}
284
+ />
285
+ );
286
+ };
287
+
288
+ const result = render(
289
+ <ThemeContextProvider>
290
+ <ControlledSelect />
291
+ </ThemeContextProvider>
292
+ );
293
+
294
+ await user.click(result.getByTestId('ucl-uikit-select'));
295
+ await user.keyboard('{ArrowDown}');
296
+ await user.keyboard('{ArrowDown}');
297
+ await user.keyboard('{ArrowDown}');
298
+
299
+ expect(changeSpy).not.toHaveBeenCalled();
300
+ });
301
+
302
+ test('selectionBehaviour=commit commits highlighted option on Enter', async () => {
303
+ const user = userEvent.setup();
304
+ const changeSpy = vi.fn();
305
+
306
+ const ControlledSelect = () => {
307
+ const [value, setValue] = useState('');
308
+ return (
309
+ <Select
310
+ options={defaultOptions}
311
+ value={value}
312
+ selectionBehaviour='commit'
313
+ onValueChange={(next, ev) => {
314
+ setValue(next as string);
315
+ changeSpy(next, ev);
316
+ }}
317
+ />
318
+ );
319
+ };
320
+
321
+ const result = render(
322
+ <ThemeContextProvider>
323
+ <ControlledSelect />
324
+ </ThemeContextProvider>
325
+ );
326
+
327
+ await user.click(result.getByTestId('ucl-uikit-select'));
328
+ await user.keyboard('{ArrowDown}');
329
+ await user.keyboard('{ArrowDown}');
330
+ await user.keyboard('{Enter}');
331
+
332
+ expect(changeSpy.mock.calls.map(([val]) => val)).toEqual(['2']);
333
+ expect(
334
+ result.queryByTestId('ucl-uikit-select__panel')
335
+ ).not.toBeInTheDocument();
336
+ expect(result.getByTestId('ucl-uikit-select')).toHaveTextContent(
337
+ 'Option 2'
338
+ );
339
+ });
340
+
341
+ test('keyboard navigation does not get stuck on duplicate values', async () => {
342
+ const user = userEvent.setup();
343
+ const options = [
344
+ { label: 'First duplicate', value: 'dup' },
345
+ { label: 'Second duplicate', value: 'dup' },
346
+ { label: 'Unique', value: 'unique' },
347
+ ];
348
+ const changeSpy = vi.fn();
349
+
350
+ const ControlledSelect = () => {
351
+ const [value, setValue] = useState('');
352
+ const handleChange = vi.fn((next, ev) => {
353
+ setValue(next as string);
354
+ changeSpy(next, ev);
355
+ });
356
+ return (
357
+ <Select
358
+ options={options}
359
+ value={value}
360
+ onValueChange={handleChange}
361
+ />
362
+ );
363
+ };
364
+
365
+ const result = render(
366
+ <ThemeContextProvider>
367
+ <ControlledSelect />
368
+ </ThemeContextProvider>
369
+ );
370
+
371
+ await user.click(result.getByTestId('ucl-uikit-select'));
372
+ await user.keyboard('{ArrowDown}');
373
+ await user.keyboard('{ArrowDown}');
374
+ await user.keyboard('{ArrowDown}');
375
+
376
+ expect(changeSpy.mock.calls.map(([val]) => val)).toEqual([
377
+ 'dup',
378
+ 'dup',
379
+ 'unique',
380
+ ]);
381
+
382
+ const optionElements = result.getAllByRole('option');
383
+ expect(
384
+ optionElements.filter((el) => el.getAttribute('aria-selected') === 'true')
385
+ ).toHaveLength(1);
386
+ expect(optionElements[2]).toHaveAttribute('aria-selected', 'true');
387
+ });
388
+
389
+ test('keeps duplicate label identity when selecting by click', async () => {
390
+ const user = userEvent.setup();
391
+ const options = [
392
+ { label: 'First duplicate', value: 'dup' },
393
+ { label: 'Second duplicate', value: 'dup' },
394
+ ];
395
+
396
+ const ControlledSelect = () => {
397
+ const [value, setValue] = useState('');
398
+ return (
399
+ <Select
400
+ options={options}
401
+ value={value}
402
+ onValueChange={(next) => setValue(next as string)}
403
+ />
404
+ );
405
+ };
406
+
407
+ const result = render(
408
+ <ThemeContextProvider>
409
+ <ControlledSelect />
410
+ </ThemeContextProvider>
411
+ );
412
+
413
+ await user.click(result.getByTestId('ucl-uikit-select'));
414
+ await user.click(result.getAllByRole('option')[1]);
415
+
416
+ expect(result.getByTestId('ucl-uikit-select')).toHaveTextContent(
417
+ 'Second duplicate'
418
+ );
419
+ });
420
+
421
+ test('forwards id and title attributes to the combobox', () => {
422
+ const result = render(
423
+ <ThemeContextProvider>
424
+ <Select
425
+ id='my-select'
426
+ title='Select an option'
427
+ options={defaultOptions}
428
+ value=''
429
+ onValueChange={() => {}}
430
+ />
431
+ </ThemeContextProvider>
432
+ );
433
+
434
+ const combobox = result.getByRole('combobox');
435
+ expect(combobox).toHaveAttribute('id', 'my-select');
436
+ expect(combobox).toHaveAttribute('title', 'Select an option');
437
+ });
438
+
439
+ test('applies filterInputProps to the filter input', async () => {
440
+ const user = userEvent.setup();
441
+ const result = render(
442
+ <ThemeContextProvider>
443
+ <div>
444
+ <span id='filter-hint'>Filter options by text</span>
445
+ <Select
446
+ filterable
447
+ options={defaultOptions}
448
+ value=''
449
+ onValueChange={() => {}}
450
+ filterInputProps={{ 'aria-describedby': 'filter-hint' }}
451
+ />
452
+ </div>
453
+ </ThemeContextProvider>
454
+ );
455
+
456
+ await user.click(result.getByRole('combobox'));
457
+ const filterInput = result.getByRole('searchbox');
458
+ expect(filterInput).toHaveAttribute('aria-describedby', 'filter-hint');
459
+ });
460
+
461
+ test('opens filter input on keyboard focus', async () => {
462
+ const user = userEvent.setup();
463
+ const result = render(
464
+ <ThemeContextProvider>
465
+ <Select
466
+ filterable
467
+ options={defaultOptions}
468
+ value=''
469
+ onValueChange={() => {}}
470
+ />
471
+ </ThemeContextProvider>
472
+ );
473
+
474
+ await user.tab();
475
+
476
+ const filterInput = await result.findByRole('searchbox');
477
+ expect(filterInput).toHaveFocus();
478
+ expect(result.getByRole('listbox')).toBeInTheDocument();
479
+ });
480
+
481
+ test('pressing Escape closes dropdown and clears filter', async () => {
482
+ const user = userEvent.setup();
483
+ const result = render(
484
+ <ThemeContextProvider>
485
+ <Select
486
+ filterable
487
+ options={defaultOptions}
488
+ value=''
489
+ onValueChange={() => {}}
490
+ />
491
+ </ThemeContextProvider>
492
+ );
493
+
494
+ const combobox = result.getByTestId('ucl-uikit-select');
495
+ await user.click(combobox);
496
+ await user.type(result.getByRole('searchbox'), '1');
497
+ await user.keyboard('{Escape}');
498
+
499
+ expect(
500
+ result.queryByTestId('ucl-uikit-select__panel')
501
+ ).not.toBeInTheDocument();
502
+ expect(combobox).toHaveFocus();
503
+
504
+ await user.click(combobox);
505
+ expect(result.getByRole('searchbox')).toHaveValue('');
506
+ });
507
+
508
+ test('announces active option and option position metadata', async () => {
509
+ const user = userEvent.setup();
510
+ const result = render(
511
+ <ThemeContextProvider>
512
+ <Select
513
+ options={defaultOptions}
514
+ value=''
515
+ onValueChange={() => {}}
516
+ />
517
+ </ThemeContextProvider>
518
+ );
519
+
520
+ const combobox = result.getByRole('combobox');
521
+ await user.click(combobox);
522
+
523
+ const options = result.getAllByRole('option');
524
+ expect(options[0]).toHaveAttribute('aria-posinset', '1');
525
+ expect(options[0]).toHaveAttribute('aria-setsize', '3');
526
+ expect(options[1]).toHaveAttribute('aria-posinset', '2');
527
+ expect(options[1]).toHaveAttribute('aria-setsize', '3');
528
+
529
+ await user.keyboard('{ArrowDown}');
530
+ expect(combobox).toHaveAttribute('aria-activedescendant', options[0].id);
531
+
532
+ await user.keyboard('{ArrowDown}');
533
+ expect(combobox).toHaveAttribute('aria-activedescendant', options[1].id);
534
+ });
535
+
536
+ test('filterable input tracks active descendant while navigating options', async () => {
537
+ const user = userEvent.setup();
538
+ const result = render(
539
+ <ThemeContextProvider>
540
+ <Select
541
+ filterable
542
+ options={defaultOptions}
543
+ value=''
544
+ onValueChange={() => {}}
545
+ />
546
+ </ThemeContextProvider>
547
+ );
548
+
549
+ await user.click(result.getByRole('combobox'));
550
+ const filterInput = result.getByRole('searchbox');
551
+ expect(filterInput).toHaveFocus();
552
+
553
+ const options = result.getAllByRole('option');
554
+ await user.keyboard('{ArrowDown}');
555
+ expect(filterInput).toHaveAttribute('aria-activedescendant', options[0].id);
556
+
557
+ await user.keyboard('{ArrowDown}');
558
+ expect(filterInput).toHaveAttribute('aria-activedescendant', options[1].id);
559
+ });
119
560
  });
@@ -4,7 +4,7 @@ exports[`Select > Snapshot: default 1`] = `
4
4
  <div
5
5
  aria-expanded="false"
6
6
  aria-haspopup="listbox"
7
- class="ucl-uikit-select css-1g3qqhm"
7
+ class="ucl-uikit-select css-1kop2c3"
8
8
  data-testid="ucl-uikit-select"
9
9
  role="combobox"
10
10
  tabindex="0"
@@ -14,10 +14,10 @@ exports[`Select > Snapshot: default 1`] = `
14
14
  data-testid="ucl-uikit-select__visible-field"
15
15
  >
16
16
  <span
17
- class="css-1oagzrk"
17
+ class="css-16t5nns"
18
18
  />
19
19
  <svg
20
- class="ucl-uikit-icon css-1ieo112"
20
+ class="ucl-uikit-icon css-hseitk"
21
21
  data-testid="ucl-uikit-icon"
22
22
  fill="none"
23
23
  height="24"
@@ -5,15 +5,17 @@ import { CustomOptionProps } from '../Select.types';
5
5
 
6
6
  const NAME = 'ucl-uikit-select__option';
7
7
 
8
- const CustomOption = ({
8
+ const CustomOption = <T extends string | number>({
9
9
  value,
10
+ optionIndex,
10
11
  isSelected = false,
11
12
  onSelect,
13
+ lineBreak = false,
12
14
  testId = NAME,
13
15
  className,
14
16
  children,
15
17
  ...props
16
- }: CustomOptionProps) => {
18
+ }: CustomOptionProps<T>) => {
17
19
  const [theme] = useTheme();
18
20
  const internalRef = useRef<HTMLDivElement>(null);
19
21
 
@@ -28,35 +30,47 @@ const CustomOption = ({
28
30
  }, [isSelected]);
29
31
 
30
32
  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
31
- onSelect(event, value);
33
+ onSelect(event, value, optionIndex);
32
34
  event.stopPropagation(); // Otherwise the panel will open again instantaneously
33
35
  };
34
36
 
35
37
  const baseStyle = css`
36
- display: flex;
37
- align-items: center;
38
- justify-content: left;
39
38
  gap: 16px;
40
- width: 100%;
41
39
  min-height: 40px;
42
40
  box-sizing: border-box;
43
- padding: 8px 16px;
41
+ padding: ${theme.padding.p8} ${theme.padding.p16};
44
42
  font-family: ${theme.font.family.primary};
45
43
  font-size: ${theme.font.size.f16};
44
+ line-height: ${theme.font.lineHeight.h140};
46
45
  background-color: ${theme.color.neutral.white};
47
46
  overflow: hidden;
48
- white-space: nowrap;
49
47
 
50
48
  &:hover {
51
49
  background-color: ${theme.color.neutral.grey5};
52
50
  }
53
51
  `;
54
52
 
53
+ const noLineWrapStyle = css`
54
+ white-space: nowrap;
55
+ text-overflow: ellipsis;
56
+ `;
57
+
58
+ const lineBreakStyle = css`
59
+ word-break: break-all;
60
+ `;
61
+
55
62
  const selectedStyle = css`
56
63
  background-color: ${theme.color.neutral.grey5};
57
64
  `;
58
65
 
59
- const style = cx(NAME, baseStyle, isSelected && selectedStyle, className);
66
+ const style = cx(
67
+ NAME,
68
+ baseStyle,
69
+ !lineBreak && noLineWrapStyle,
70
+ lineBreak && lineBreakStyle,
71
+ isSelected && selectedStyle,
72
+ className
73
+ );
60
74
 
61
75
  return (
62
76
  <div