@astryxdesign/core 0.1.2 → 0.1.3

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 (567) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +12 -12
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +2 -4
  10. package/dist/ButtonGroup/ButtonGroup.d.ts.map +1 -1
  11. package/dist/ButtonGroup/ButtonGroup.js +0 -1
  12. package/dist/Calendar/Calendar.d.ts +5 -4
  13. package/dist/Calendar/Calendar.d.ts.map +1 -1
  14. package/dist/Calendar/Calendar.js +82 -43
  15. package/dist/Calendar/hooks/index.d.ts +0 -2
  16. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  17. package/dist/Calendar/hooks/index.js +1 -2
  18. package/dist/Calendar/index.d.ts +3 -3
  19. package/dist/Calendar/index.d.ts.map +1 -1
  20. package/dist/Calendar/index.js +1 -1
  21. package/dist/Calendar/styles.d.ts +3 -9
  22. package/dist/Calendar/styles.d.ts.map +1 -1
  23. package/dist/Calendar/styles.js +3 -11
  24. package/dist/Carousel/Carousel.d.ts.map +1 -1
  25. package/dist/Carousel/Carousel.js +17 -3
  26. package/dist/Center/Center.d.ts +11 -1
  27. package/dist/Center/Center.d.ts.map +1 -1
  28. package/dist/Center/Center.js +9 -3
  29. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  30. package/dist/Chat/ChatComposerInput.js +0 -1
  31. package/dist/Chat/ChatMessageList.d.ts +14 -1
  32. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  33. package/dist/Chat/ChatMessageList.js +2 -0
  34. package/dist/Chat/useTriggerMenu.d.ts +8 -1
  35. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  36. package/dist/Chat/useTriggerMenu.js +15 -3
  37. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  38. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  39. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  40. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  41. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  42. package/dist/CheckboxList/CheckboxList.js +39 -9
  43. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  44. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  45. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  46. package/dist/CheckboxList/CheckboxListItem.js +0 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +9 -5
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +51 -37
  51. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  52. package/dist/CommandPalette/CommandPalette.js +5 -5
  53. package/dist/ContextMenu/ContextMenu.d.ts +4 -5
  54. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  55. package/dist/ContextMenu/ContextMenu.js +93 -11
  56. package/dist/DateInput/DateInput.d.ts +23 -1
  57. package/dist/DateInput/DateInput.d.ts.map +1 -1
  58. package/dist/DateInput/DateInput.js +167 -99
  59. package/dist/DateRangeInput/DateRangeInput.d.ts +23 -1
  60. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  61. package/dist/DateRangeInput/DateRangeInput.js +42 -9
  62. package/dist/DateTimeInput/DateTimeInput.d.ts +35 -2
  63. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  64. package/dist/DateTimeInput/DateTimeInput.js +78 -15
  65. package/dist/Dialog/Dialog.d.ts.map +1 -1
  66. package/dist/Dialog/Dialog.js +12 -0
  67. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -8
  68. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  69. package/dist/DropdownMenu/DropdownMenu.js +42 -14
  70. package/dist/Field/Field.d.ts +19 -2
  71. package/dist/Field/Field.d.ts.map +1 -1
  72. package/dist/Field/Field.js +4 -0
  73. package/dist/Field/FieldLabel.d.ts +21 -2
  74. package/dist/Field/FieldLabel.d.ts.map +1 -1
  75. package/dist/Field/FieldLabel.js +41 -26
  76. package/dist/FileInput/FileInput.d.ts +22 -1
  77. package/dist/FileInput/FileInput.d.ts.map +1 -1
  78. package/dist/FileInput/FileInput.js +56 -10
  79. package/dist/Grid/Grid.d.ts +11 -1
  80. package/dist/Grid/Grid.d.ts.map +1 -1
  81. package/dist/Grid/Grid.js +32 -6
  82. package/dist/InputGroup/InputGroup.d.ts +4 -3
  83. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  84. package/dist/InputGroup/InputGroup.js +16 -6
  85. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  86. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  87. package/dist/InputGroup/InputGroupContext.js +2 -2
  88. package/dist/InputGroup/groupStyles.d.ts +1 -1
  89. package/dist/InputGroup/groupStyles.d.ts.map +1 -1
  90. package/dist/InputGroup/groupStyles.js +3 -3
  91. package/dist/InputGroup/index.d.ts +1 -0
  92. package/dist/InputGroup/index.d.ts.map +1 -1
  93. package/dist/Kbd/Kbd.d.ts.map +1 -1
  94. package/dist/Kbd/Kbd.js +33 -1
  95. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  96. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  97. package/dist/Layer/layerAnimations.stylex.js +8 -4
  98. package/dist/Layer/useLayer.d.ts +10 -0
  99. package/dist/Layer/useLayer.d.ts.map +1 -1
  100. package/dist/Layer/useLayer.js +64 -9
  101. package/dist/Layout/Layout.d.ts +4 -11
  102. package/dist/Layout/Layout.d.ts.map +1 -1
  103. package/dist/Layout/Layout.js +10 -11
  104. package/dist/Layout/LayoutFooter.d.ts +2 -2
  105. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  106. package/dist/Layout/LayoutHeader.d.ts +2 -2
  107. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  108. package/dist/Layout/LayoutPanel.d.ts +2 -2
  109. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  110. package/dist/Layout/padding.stylex.d.ts +51 -0
  111. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  112. package/dist/Layout/padding.stylex.js +63 -0
  113. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  114. package/dist/Lightbox/Lightbox.js +4 -2
  115. package/dist/Link/Link.d.ts +16 -1
  116. package/dist/Link/Link.d.ts.map +1 -1
  117. package/dist/Link/Link.js +18 -7
  118. package/dist/Markdown/Markdown.d.ts.map +1 -1
  119. package/dist/Markdown/Markdown.js +7 -0
  120. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  121. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  122. package/dist/MoreMenu/MoreMenu.js +0 -2
  123. package/dist/MultiSelector/MultiSelector.d.ts +27 -2
  124. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  125. package/dist/MultiSelector/MultiSelector.js +141 -42
  126. package/dist/MultiSelector/hooks.d.ts +12 -1
  127. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  128. package/dist/MultiSelector/hooks.js +14 -1
  129. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  130. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  131. package/dist/NumberInput/NumberInput.d.ts +23 -2
  132. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  133. package/dist/NumberInput/NumberInput.js +62 -12
  134. package/dist/Pagination/Pagination.d.ts +1 -1
  135. package/dist/Pagination/Pagination.d.ts.map +1 -1
  136. package/dist/Pagination/Pagination.js +15 -1
  137. package/dist/Popover/Popover.d.ts +18 -2
  138. package/dist/Popover/Popover.d.ts.map +1 -1
  139. package/dist/Popover/Popover.js +5 -2
  140. package/dist/Popover/usePopover.d.ts +38 -3
  141. package/dist/Popover/usePopover.d.ts.map +1 -1
  142. package/dist/Popover/usePopover.js +24 -9
  143. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  144. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  145. package/dist/PowerSearch/PowerSearch.js +6 -1
  146. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  147. package/dist/ProgressBar/ProgressBar.js +3 -5
  148. package/dist/RadioList/RadioList.d.ts +20 -2
  149. package/dist/RadioList/RadioList.d.ts.map +1 -1
  150. package/dist/RadioList/RadioList.js +105 -9
  151. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  152. package/dist/RadioList/RadioListItem.js +13 -2
  153. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  154. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  155. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  156. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  157. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  159. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  160. package/dist/Selector/Selector.d.ts +24 -1
  161. package/dist/Selector/Selector.d.ts.map +1 -1
  162. package/dist/Selector/Selector.js +122 -43
  163. package/dist/Selector/hooks.d.ts +8 -1
  164. package/dist/Selector/hooks.d.ts.map +1 -1
  165. package/dist/Selector/hooks.js +14 -1
  166. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  167. package/dist/SideNav/SideNavHeading.js +2 -0
  168. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  169. package/dist/SideNav/SideNavItem.js +1 -1
  170. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  171. package/dist/Skeleton/Skeleton.js +8 -2
  172. package/dist/Slider/Slider.d.ts +21 -0
  173. package/dist/Slider/Slider.d.ts.map +1 -1
  174. package/dist/Slider/Slider.js +36 -9
  175. package/dist/Spinner/Spinner.d.ts.map +1 -1
  176. package/dist/Spinner/Spinner.js +1 -1
  177. package/dist/Stack/Stack.d.ts +38 -23
  178. package/dist/Stack/Stack.d.ts.map +1 -1
  179. package/dist/Stack/Stack.js +25 -1
  180. package/dist/Stack/StackItem.d.ts +12 -23
  181. package/dist/Stack/StackItem.d.ts.map +1 -1
  182. package/dist/Stack/StackItem.js +8 -1
  183. package/dist/Switch/Switch.d.ts +23 -2
  184. package/dist/Switch/Switch.d.ts.map +1 -1
  185. package/dist/Switch/Switch.js +41 -11
  186. package/dist/TabList/Tab.d.ts.map +1 -1
  187. package/dist/TabList/Tab.js +6 -0
  188. package/dist/TabList/TabList.d.ts +16 -4
  189. package/dist/TabList/TabList.d.ts.map +1 -1
  190. package/dist/TabList/TabList.js +87 -10
  191. package/dist/TabList/TabListContext.d.ts +5 -0
  192. package/dist/TabList/TabListContext.d.ts.map +1 -1
  193. package/dist/TabList/TabListContext.js +5 -0
  194. package/dist/TabList/TabMenu.d.ts.map +1 -1
  195. package/dist/TabList/TabMenu.js +6 -1
  196. package/dist/Table/BaseTable.d.ts.map +1 -1
  197. package/dist/Table/BaseTable.js +11 -8
  198. package/dist/Table/Table.d.ts.map +1 -1
  199. package/dist/Table/Table.js +10 -1
  200. package/dist/Table/TableCell.d.ts +8 -1
  201. package/dist/Table/TableCell.d.ts.map +1 -1
  202. package/dist/Table/TableCell.js +10 -10
  203. package/dist/Table/TableHeaderCell.d.ts +8 -1
  204. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  205. package/dist/Table/TableHeaderCell.js +16 -16
  206. package/dist/Table/index.d.ts +2 -1
  207. package/dist/Table/index.d.ts.map +1 -1
  208. package/dist/Table/index.js +2 -2
  209. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  210. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  211. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  212. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  213. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  214. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  215. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  216. package/dist/Table/tableContextMenu.d.ts +33 -0
  217. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  218. package/dist/Table/tableContextMenu.js +133 -0
  219. package/dist/Table/types.d.ts +66 -0
  220. package/dist/Table/types.d.ts.map +1 -1
  221. package/dist/TextArea/TextArea.d.ts +22 -1
  222. package/dist/TextArea/TextArea.d.ts.map +1 -1
  223. package/dist/TextArea/TextArea.js +41 -9
  224. package/dist/TextInput/TextInput.d.ts +23 -2
  225. package/dist/TextInput/TextInput.d.ts.map +1 -1
  226. package/dist/TextInput/TextInput.js +55 -10
  227. package/dist/TimeInput/TimeInput.d.ts +23 -1
  228. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  229. package/dist/TimeInput/TimeInput.js +147 -76
  230. package/dist/Toast/Toast.d.ts.map +1 -1
  231. package/dist/Toast/Toast.js +14 -0
  232. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  233. package/dist/Toast/ToastViewport.js +131 -3
  234. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  235. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.js +26 -4
  237. package/dist/Toolbar/Toolbar.d.ts +3 -3
  238. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  239. package/dist/Toolbar/Toolbar.js +51 -7
  240. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  241. package/dist/Tooltip/useTooltip.js +56 -9
  242. package/dist/TopNav/TopNavHeading.d.ts +7 -1
  243. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  244. package/dist/TopNav/TopNavHeading.js +8 -0
  245. package/dist/TreeList/TreeList.d.ts +1 -1
  246. package/dist/TreeList/TreeList.d.ts.map +1 -1
  247. package/dist/TreeList/TreeList.js +72 -2
  248. package/dist/TreeList/TreeListItem.d.ts +11 -1
  249. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  250. package/dist/TreeList/TreeListItem.js +51 -12
  251. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  252. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  253. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  254. package/dist/Typeahead/BaseTypeahead.d.ts +13 -0
  255. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  256. package/dist/Typeahead/BaseTypeahead.js +69 -6
  257. package/dist/Typeahead/Typeahead.d.ts +26 -3
  258. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  259. package/dist/Typeahead/Typeahead.js +65 -29
  260. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  261. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  262. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  263. package/dist/VisuallyHidden/index.d.ts +11 -0
  264. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  265. package/dist/VisuallyHidden/index.js +13 -0
  266. package/dist/astryx.css +18 -0
  267. package/dist/astryx.umd.js +55 -55
  268. package/dist/astryx.umd.js.map +4 -4
  269. package/dist/hooks/focusableSelector.d.ts +22 -0
  270. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  271. package/dist/hooks/focusableSelector.js +24 -0
  272. package/dist/hooks/index.d.ts +12 -2
  273. package/dist/hooks/index.d.ts.map +1 -1
  274. package/dist/hooks/index.js +7 -2
  275. package/dist/hooks/useAnnounce.d.ts +40 -0
  276. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  277. package/dist/hooks/useAnnounce.js +142 -0
  278. package/dist/hooks/useClickableContainer.d.ts +16 -1
  279. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  280. package/dist/hooks/useClickableContainer.js +16 -4
  281. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  282. package/dist/hooks/useEntryAnimation.js +4 -4
  283. package/dist/hooks/useFocusTrap.d.ts +18 -0
  284. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  285. package/dist/hooks/useFocusTrap.js +87 -3
  286. package/dist/hooks/useGridFocus.d.ts +88 -1
  287. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  288. package/dist/hooks/useGridFocus.js +261 -62
  289. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  290. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  291. package/dist/hooks/useKeyboardHint.js +220 -0
  292. package/dist/hooks/useListFocus.d.ts +78 -7
  293. package/dist/hooks/useListFocus.d.ts.map +1 -1
  294. package/dist/hooks/useListFocus.js +305 -52
  295. package/dist/hooks/useLongPress.d.ts +21 -0
  296. package/dist/hooks/useLongPress.d.ts.map +1 -0
  297. package/dist/hooks/useLongPress.js +89 -0
  298. package/dist/hooks/useTreeFocus.d.ts +157 -0
  299. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  300. package/dist/hooks/useTreeFocus.js +337 -0
  301. package/dist/hooks/useTypeahead.d.ts +63 -0
  302. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  303. package/dist/hooks/useTypeahead.js +117 -0
  304. package/dist/index.d.ts +1 -0
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +1 -0
  307. package/dist/utils/dateTypes.d.ts +12 -0
  308. package/dist/utils/dateTypes.d.ts.map +1 -1
  309. package/dist/utils/dateTypes.js +41 -1
  310. package/dist/utils/index.d.ts +4 -0
  311. package/dist/utils/index.d.ts.map +1 -1
  312. package/dist/utils/index.js +3 -1
  313. package/dist/utils/inputAria.d.ts +29 -0
  314. package/dist/utils/inputAria.d.ts.map +1 -0
  315. package/dist/utils/inputAria.js +30 -0
  316. package/dist/utils/timeParser.d.ts.map +1 -1
  317. package/dist/utils/timeParser.js +5 -2
  318. package/docs.mjs +2 -2
  319. package/package.json +12 -8
  320. package/src/Avatar/Avatar.test.tsx +37 -0
  321. package/src/Avatar/Avatar.tsx +10 -5
  322. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  323. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  324. package/src/Button/Button.tsx +3 -16
  325. package/src/ButtonGroup/ButtonGroup.test.tsx +9 -9
  326. package/src/ButtonGroup/ButtonGroup.tsx +0 -1
  327. package/src/Calendar/Calendar.doc.mjs +4 -4
  328. package/src/Calendar/Calendar.test.tsx +168 -30
  329. package/src/Calendar/Calendar.tsx +121 -66
  330. package/src/Calendar/hooks/index.ts +0 -6
  331. package/src/Calendar/index.ts +1 -3
  332. package/src/Calendar/styles.ts +4 -9
  333. package/src/Carousel/Carousel.doc.mjs +7 -1
  334. package/src/Carousel/Carousel.test.tsx +20 -0
  335. package/src/Carousel/Carousel.tsx +16 -1
  336. package/src/Center/Center.doc.mjs +12 -2
  337. package/src/Center/Center.tsx +28 -3
  338. package/src/Chat/ChatComposerInput.test.tsx +26 -7
  339. package/src/Chat/ChatComposerInput.tsx +0 -1
  340. package/src/Chat/ChatDictationButton.doc.mjs +1 -1
  341. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  342. package/src/Chat/ChatMessageList.test.tsx +18 -0
  343. package/src/Chat/ChatMessageList.tsx +16 -0
  344. package/src/Chat/useTriggerMenu.tsx +22 -4
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +10 -0
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  347. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  348. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  349. package/src/CheckboxList/CheckboxList.test.tsx +192 -26
  350. package/src/CheckboxList/CheckboxList.tsx +56 -3
  351. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  352. package/src/CheckboxList/CheckboxListItem.tsx +0 -3
  353. package/src/Citation/Citation.doc.mjs +10 -0
  354. package/src/Citation/Citation.test.tsx +121 -0
  355. package/src/Citation/Citation.tsx +15 -3
  356. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  357. package/src/CodeBlock/CodeBlock.tsx +63 -33
  358. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  359. package/src/CommandPalette/CommandPalette.tsx +9 -21
  360. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  361. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  362. package/src/ContextMenu/ContextMenu.tsx +113 -16
  363. package/src/DateInput/DateInput.doc.mjs +286 -56
  364. package/src/DateInput/DateInput.test.tsx +248 -2
  365. package/src/DateInput/DateInput.tsx +196 -100
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  368. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  371. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  372. package/src/Dialog/Dialog.doc.mjs +2 -2
  373. package/src/Dialog/Dialog.tsx +12 -0
  374. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  375. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  376. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  377. package/src/Field/Field.doc.mjs +59 -18
  378. package/src/Field/Field.test.tsx +32 -0
  379. package/src/Field/Field.tsx +23 -5
  380. package/src/Field/FieldLabel.tsx +55 -17
  381. package/src/FileInput/FileInput.doc.mjs +17 -0
  382. package/src/FileInput/FileInput.test.tsx +283 -49
  383. package/src/FileInput/FileInput.tsx +76 -4
  384. package/src/Grid/Grid.doc.mjs +17 -4
  385. package/src/Grid/Grid.test.tsx +43 -30
  386. package/src/Grid/Grid.tsx +38 -3
  387. package/src/Icon/Icon.doc.mjs +3 -3
  388. package/src/InputGroup/InputGroup.doc.mjs +111 -23
  389. package/src/InputGroup/InputGroup.test.tsx +257 -60
  390. package/src/InputGroup/InputGroup.tsx +23 -5
  391. package/src/InputGroup/InputGroupContext.ts +9 -4
  392. package/src/InputGroup/groupStyles.ts +7 -1
  393. package/src/InputGroup/index.ts +1 -0
  394. package/src/Kbd/Kbd.test.tsx +22 -4
  395. package/src/Kbd/Kbd.tsx +35 -3
  396. package/src/Layer/layerAnimations.stylex.ts +20 -4
  397. package/src/Layer/useLayer.doc.mjs +5 -0
  398. package/src/Layer/useLayer.test.tsx +111 -0
  399. package/src/Layer/useLayer.tsx +90 -9
  400. package/src/Layout/Layout.tsx +8 -16
  401. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  402. package/src/Layout/LayoutFooter.tsx +3 -3
  403. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  404. package/src/Layout/LayoutHeader.tsx +3 -3
  405. package/src/Layout/LayoutPanel.tsx +3 -3
  406. package/src/Layout/padding.stylex.ts +52 -0
  407. package/src/Lightbox/Lightbox.doc.mjs +9 -0
  408. package/src/Lightbox/Lightbox.test.tsx +53 -5
  409. package/src/Lightbox/Lightbox.tsx +9 -4
  410. package/src/Link/Link.doc.mjs +14 -0
  411. package/src/Link/Link.test.tsx +50 -0
  412. package/src/Link/Link.tsx +39 -4
  413. package/src/Markdown/Markdown.test.tsx +11 -0
  414. package/src/Markdown/Markdown.tsx +7 -0
  415. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  416. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  417. package/src/MoreMenu/MoreMenu.tsx +0 -9
  418. package/src/MultiSelector/MultiSelector.doc.mjs +30 -0
  419. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  420. package/src/MultiSelector/MultiSelector.tsx +194 -48
  421. package/src/MultiSelector/hooks.ts +27 -0
  422. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  423. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  424. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  425. package/src/NumberInput/NumberInput.test.tsx +242 -40
  426. package/src/NumberInput/NumberInput.tsx +89 -11
  427. package/src/Pagination/Pagination.doc.mjs +3 -3
  428. package/src/Pagination/Pagination.test.tsx +117 -5
  429. package/src/Pagination/Pagination.tsx +21 -1
  430. package/src/Popover/Popover.doc.mjs +26 -0
  431. package/src/Popover/Popover.test.tsx +121 -0
  432. package/src/Popover/Popover.tsx +23 -2
  433. package/src/Popover/usePopover.doc.mjs +9 -3
  434. package/src/Popover/usePopover.tsx +70 -12
  435. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  436. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  437. package/src/PowerSearch/PowerSearch.tsx +16 -3
  438. package/src/ProgressBar/ProgressBar.test.tsx +34 -26
  439. package/src/ProgressBar/ProgressBar.tsx +3 -4
  440. package/src/RadioList/RadioList.doc.mjs +9 -0
  441. package/src/RadioList/RadioList.test.tsx +252 -16
  442. package/src/RadioList/RadioList.tsx +166 -10
  443. package/src/RadioList/RadioListItem.tsx +14 -2
  444. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  445. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  446. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  447. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  448. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  449. package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
  450. package/src/Selector/Selector.doc.mjs +26 -0
  451. package/src/Selector/Selector.test.tsx +387 -15
  452. package/src/Selector/Selector.tsx +175 -67
  453. package/src/Selector/hooks.ts +23 -0
  454. package/src/SideNav/SideNav.test.tsx +48 -0
  455. package/src/SideNav/SideNavHeading.tsx +8 -2
  456. package/src/SideNav/SideNavItem.tsx +2 -4
  457. package/src/Skeleton/Skeleton.test.tsx +25 -0
  458. package/src/Skeleton/Skeleton.tsx +11 -1
  459. package/src/Slider/Slider.doc.mjs +15 -0
  460. package/src/Slider/Slider.test.tsx +155 -2
  461. package/src/Slider/Slider.tsx +53 -5
  462. package/src/Spinner/Spinner.tsx +7 -1
  463. package/src/Stack/Stack.doc.mjs +77 -0
  464. package/src/Stack/Stack.test.tsx +55 -5
  465. package/src/Stack/Stack.tsx +73 -24
  466. package/src/Stack/StackItem.test.tsx +23 -0
  467. package/src/Stack/StackItem.tsx +20 -23
  468. package/src/Switch/Switch.doc.mjs +15 -0
  469. package/src/Switch/Switch.test.tsx +162 -27
  470. package/src/Switch/Switch.tsx +61 -21
  471. package/src/TabList/Tab.tsx +6 -0
  472. package/src/TabList/TabList.doc.mjs +6 -0
  473. package/src/TabList/TabList.test.tsx +251 -0
  474. package/src/TabList/TabList.tsx +103 -12
  475. package/src/TabList/TabListContext.ts +7 -3
  476. package/src/TabList/TabMenu.tsx +6 -3
  477. package/src/Table/BaseTable.tsx +21 -17
  478. package/src/Table/Table.doc.mjs +2 -0
  479. package/src/Table/Table.test.tsx +10 -0
  480. package/src/Table/Table.tsx +8 -0
  481. package/src/Table/TableCell.tsx +25 -23
  482. package/src/Table/TableHeaderCell.tsx +28 -29
  483. package/src/Table/index.ts +3 -2
  484. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  485. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  486. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  487. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  488. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  489. package/src/Table/tableContextMenu.test.tsx +157 -0
  490. package/src/Table/tableContextMenu.tsx +142 -0
  491. package/src/Table/types.ts +74 -0
  492. package/src/Table/useTableColumnResize.doc.mjs +3 -3
  493. package/src/Table/useTableStickyColumns.doc.mjs +2 -2
  494. package/src/Text/Text.doc.mjs +18 -18
  495. package/src/TextArea/TextArea.doc.mjs +17 -0
  496. package/src/TextArea/TextArea.test.tsx +167 -35
  497. package/src/TextArea/TextArea.tsx +62 -15
  498. package/src/TextInput/TextInput.doc.mjs +17 -0
  499. package/src/TextInput/TextInput.test.tsx +168 -34
  500. package/src/TextInput/TextInput.tsx +77 -9
  501. package/src/Thumbnail/Thumbnail.doc.mjs +4 -4
  502. package/src/TimeInput/TimeInput.doc.mjs +340 -60
  503. package/src/TimeInput/TimeInput.test.tsx +255 -7
  504. package/src/TimeInput/TimeInput.tsx +176 -70
  505. package/src/Timestamp/Timestamp.doc.mjs +19 -19
  506. package/src/Toast/Toast.tsx +14 -0
  507. package/src/Toast/ToastViewport.test.tsx +214 -0
  508. package/src/Toast/ToastViewport.tsx +167 -21
  509. package/src/Tokenizer/Tokenizer.doc.mjs +29 -14
  510. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  511. package/src/Tokenizer/Tokenizer.tsx +37 -1
  512. package/src/Toolbar/Toolbar.test.tsx +109 -6
  513. package/src/Toolbar/Toolbar.tsx +59 -5
  514. package/src/Tooltip/Tooltip.test.tsx +68 -0
  515. package/src/Tooltip/useTooltip.tsx +55 -8
  516. package/src/TopNav/TopNav.test.tsx +50 -0
  517. package/src/TopNav/TopNavHeading.tsx +17 -1
  518. package/src/TreeList/TreeList.test.tsx +295 -6
  519. package/src/TreeList/TreeList.tsx +73 -1
  520. package/src/TreeList/TreeListItem.tsx +51 -5
  521. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  522. package/src/Typeahead/BaseTypeahead.tsx +93 -9
  523. package/src/Typeahead/Typeahead.doc.mjs +141 -30
  524. package/src/Typeahead/Typeahead.test.tsx +331 -1
  525. package/src/Typeahead/Typeahead.tsx +105 -33
  526. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  527. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  528. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  529. package/src/VisuallyHidden/index.ts +15 -0
  530. package/src/docs-script.test.mjs +50 -0
  531. package/src/hooks/focusableSelector.ts +25 -0
  532. package/src/hooks/index.ts +28 -2
  533. package/src/hooks/useAnnounce.test.tsx +101 -0
  534. package/src/hooks/useAnnounce.ts +168 -0
  535. package/src/hooks/useClickableContainer.ts +16 -4
  536. package/src/hooks/useEntryAnimation.ts +16 -4
  537. package/src/hooks/useFocusTrap.test.tsx +132 -0
  538. package/src/hooks/useFocusTrap.ts +98 -4
  539. package/src/hooks/useGridFocus.doc.mjs +40 -2
  540. package/src/hooks/useGridFocus.test.tsx +132 -0
  541. package/src/hooks/useGridFocus.ts +343 -67
  542. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  543. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  544. package/src/hooks/useKeyboardHint.tsx +332 -0
  545. package/src/hooks/useListFocus.doc.mjs +50 -10
  546. package/src/hooks/useListFocus.test.tsx +375 -0
  547. package/src/hooks/useListFocus.ts +395 -57
  548. package/src/hooks/useLongPress.test.tsx +134 -0
  549. package/src/hooks/useLongPress.ts +119 -0
  550. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  551. package/src/hooks/useTreeFocus.test.tsx +214 -0
  552. package/src/hooks/useTreeFocus.ts +559 -0
  553. package/src/hooks/useTypeahead.test.tsx +109 -0
  554. package/src/hooks/useTypeahead.ts +183 -0
  555. package/src/index.ts +1 -0
  556. package/src/theme/derivedVarRegistry.test.ts +10 -5
  557. package/src/utils/dateTypes.test.ts +40 -0
  558. package/src/utils/dateTypes.ts +40 -0
  559. package/src/utils/index.ts +9 -0
  560. package/src/utils/inputAria.test.ts +39 -0
  561. package/src/utils/inputAria.ts +58 -0
  562. package/src/utils/timeParser.test.ts +10 -0
  563. package/src/utils/timeParser.ts +5 -2
  564. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  565. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  566. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  567. package/src/Calendar/hooks/useCalendarRovingTabindex.ts +0 -118
@@ -86,6 +86,12 @@ export const docs = {
86
86
  description: 'Disables the selector.',
87
87
  default: 'false',
88
88
  },
89
+ {
90
+ name: 'disabledMessage',
91
+ type: 'string',
92
+ description:
93
+ 'Explains why the selector is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the trigger focusable via aria-disabled (activation stays blocked). Use this instead of wrapping a disabled Selector in Tooltip — disabled controls swallow the hover events an external Tooltip needs.',
94
+ },
89
95
  {
90
96
  name: 'isLabelHidden',
91
97
  type: 'boolean',
@@ -151,6 +157,11 @@ export const docs = {
151
157
  description:
152
158
  'Set a meaningful placeholder that hints at the expected selection (e.g. "Choose a country" not "Select...").',
153
159
  },
160
+ {
161
+ guidance: true,
162
+ description:
163
+ 'Use inside InputGroup only when the selector needs a short prefix or suffix addon as part of one decorated input surface.',
164
+ },
154
165
  {
155
166
  guidance: false,
156
167
  description:
@@ -176,6 +187,11 @@ export const docs = {
176
187
  description:
177
188
  'Put more than ~20 options without sections; consider Typeahead for large lists.',
178
189
  },
190
+ {
191
+ guidance: false,
192
+ description:
193
+ 'Wrap a disabled Selector in Tooltip to explain why it is disabled; disabled triggers swallow the hover events the wrapper needs. Use the disabledMessage prop instead.',
194
+ },
179
195
  ],
180
196
  anatomy: [
181
197
  {
@@ -263,6 +279,11 @@ export const docsZh = {
263
279
  description:
264
280
  'Put more than ~20 options without sections; consider Typeahead for large lists.',
265
281
  },
282
+ {
283
+ guidance: false,
284
+ description:
285
+ 'Wrap a disabled Selector in Tooltip to explain why it is disabled; disabled triggers swallow the hover events the wrapper needs. Use the disabledMessage prop instead.',
286
+ },
266
287
  ],
267
288
  anatomy: [
268
289
  {
@@ -350,6 +371,11 @@ export const docsDense = {
350
371
  description:
351
372
  'Put more than ~20 options without sections; consider Typeahead for large lists.',
352
373
  },
374
+ {
375
+ guidance: false,
376
+ description:
377
+ 'Wrap a disabled Selector in Tooltip to explain why it is disabled; disabled triggers swallow the hover events the wrapper needs. Use the disabledMessage prop instead.',
378
+ },
353
379
  ],
354
380
  anatomy: [
355
381
  {
@@ -10,10 +10,11 @@
10
10
  */
11
11
 
12
12
  import {describe, it, expect, vi, beforeEach} from 'vitest';
13
- import {render, screen, waitFor} from '@testing-library/react';
13
+ import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {Selector} from './Selector';
16
16
  import {SelectorOption} from './SelectorOption';
17
+ import {InputGroup, InputGroupText} from '../InputGroup';
17
18
 
18
19
  // Mock showPopover and hidePopover methods since they're not implemented in jsdom
19
20
  beforeEach(() => {
@@ -83,7 +84,12 @@ function mockSelectorRects() {
83
84
  'innerHeight',
84
85
  );
85
86
  HTMLElement.prototype.getBoundingClientRect = function () {
86
- if (this.getAttribute('role') === 'combobox') {
87
+ // The trigger is role="combobox" by default, or a plain button with
88
+ // aria-haspopup="listbox" in hasSearch mode — match either.
89
+ if (
90
+ this.getAttribute('role') === 'combobox' ||
91
+ this.getAttribute('aria-haspopup') === 'listbox'
92
+ ) {
87
93
  return rect({top: 160, bottom: 190, height: 30});
88
94
  }
89
95
  if (this.getAttribute('role') === 'listbox') {
@@ -108,9 +114,7 @@ function mockSelectorRects() {
108
114
 
109
115
  describe('Selector', () => {
110
116
  it('renders with placeholder when no value', () => {
111
- render(
112
- <Selector label="Fruit" options={OPTIONS} placeholder="Pick one" />,
113
- );
117
+ render(<Selector label="Fruit" options={OPTIONS} placeholder="Pick one" />);
114
118
  expect(screen.getByRole('combobox')).toHaveTextContent('Pick one');
115
119
  });
116
120
 
@@ -147,6 +151,27 @@ describe('Selector', () => {
147
151
  expect(screen.getByTestId('option-badge')).toHaveTextContent('Owner');
148
152
  });
149
153
 
154
+ it('exposes the popup as a listbox, not a modal dialog', () => {
155
+ render(
156
+ <Selector
157
+ label="Fruit"
158
+ options={OPTIONS}
159
+ value="Banana"
160
+ onChange={() => {}}
161
+ />,
162
+ );
163
+ // The combobox trigger keeps DOM focus; the popup must expose its own
164
+ // role="listbox" and must not be wrapped in a role="dialog" aria-modal
165
+ // element, which would tell AT the focused trigger is inert.
166
+ expect(screen.getByRole('listbox', {hidden: true})).toBeInTheDocument();
167
+ expect(
168
+ screen.queryByRole('dialog', {hidden: true}),
169
+ ).not.toBeInTheDocument();
170
+ expect(
171
+ document.querySelector('[aria-modal="true"]'),
172
+ ).not.toBeInTheDocument();
173
+ });
174
+
150
175
  it('supports explicit menu placement', () => {
151
176
  render(
152
177
  <Selector
@@ -310,6 +335,59 @@ describe('Selector', () => {
310
335
  expect(onChange).toHaveBeenCalledWith(null);
311
336
  });
312
337
 
338
+ it('clears the value via Delete on the focused trigger', async () => {
339
+ const user = userEvent.setup();
340
+ const onChange = vi.fn();
341
+ render(
342
+ <Selector
343
+ label="Fruit"
344
+ options={OPTIONS}
345
+ value="Banana"
346
+ onChange={onChange}
347
+ hasClear
348
+ />,
349
+ );
350
+ const trigger = screen.getByRole('combobox');
351
+ trigger.focus();
352
+ await user.keyboard('{Delete}');
353
+ expect(onChange).toHaveBeenCalledWith(null);
354
+ });
355
+
356
+ it('clears the value via Backspace on the focused trigger', async () => {
357
+ const user = userEvent.setup();
358
+ const onChange = vi.fn();
359
+ render(
360
+ <Selector
361
+ label="Fruit"
362
+ options={OPTIONS}
363
+ value="Banana"
364
+ onChange={onChange}
365
+ hasClear
366
+ />,
367
+ );
368
+ const trigger = screen.getByRole('combobox');
369
+ trigger.focus();
370
+ await user.keyboard('{Backspace}');
371
+ expect(onChange).toHaveBeenCalledWith(null);
372
+ });
373
+
374
+ it('does not clear via Delete when hasClear is not set', async () => {
375
+ const user = userEvent.setup();
376
+ const onChange = vi.fn();
377
+ render(
378
+ <Selector
379
+ label="Fruit"
380
+ options={OPTIONS}
381
+ value="Banana"
382
+ onChange={onChange}
383
+ />,
384
+ );
385
+ const trigger = screen.getByRole('combobox');
386
+ trigger.focus();
387
+ await user.keyboard('{Delete}');
388
+ expect(onChange).not.toHaveBeenCalled();
389
+ });
390
+
313
391
  it('shows placeholder after clearing', () => {
314
392
  render(
315
393
  <Selector
@@ -355,8 +433,33 @@ describe('Selector', () => {
355
433
  hasSearch
356
434
  />,
357
435
  );
358
- await user.click(screen.getByRole('combobox'));
359
- expect(screen.getByRole('searchbox', h)).toBeInTheDocument();
436
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
437
+ expect(screen.getByRole('combobox', h)).toBeInTheDocument();
438
+ });
439
+
440
+ it('wires the search input as the combobox with activedescendant (comboboxes-4)', async () => {
441
+ const user = userEvent.setup();
442
+ render(
443
+ <Selector
444
+ label="Fruit"
445
+ options={OPTIONS}
446
+ value="Apple"
447
+ onChange={() => {}}
448
+ hasSearch
449
+ />,
450
+ );
451
+ const triggerBtn = screen.getByRole('button', {name: 'Fruit'});
452
+ // In hasSearch mode the trigger is a plain button, not a combobox.
453
+ expect(triggerBtn).not.toHaveAttribute('role', 'combobox');
454
+ await user.click(triggerBtn);
455
+ const search = screen.getByRole('combobox', h);
456
+ expect(search).toHaveAttribute('aria-autocomplete', 'list');
457
+ expect(search).toHaveAttribute('aria-expanded', 'true');
458
+ expect(search).toHaveAttribute('aria-controls');
459
+ // ArrowDown moves the highlight; the search input reports it via
460
+ // aria-activedescendant (previously silent on the trigger).
461
+ await user.keyboard('{ArrowDown}');
462
+ expect(search).toHaveAttribute('aria-activedescendant');
360
463
  });
361
464
 
362
465
  it('does not render search input when hasSearch is false', async () => {
@@ -369,6 +472,8 @@ describe('Selector', () => {
369
472
  onChange={() => {}}
370
473
  />,
371
474
  );
475
+ // hasSearch is false, so the trigger itself is the combobox and there is
476
+ // no separate search input inside the popup.
372
477
  await user.click(screen.getByRole('combobox'));
373
478
  expect(screen.queryByRole('searchbox', h)).not.toBeInTheDocument();
374
479
  });
@@ -384,8 +489,8 @@ describe('Selector', () => {
384
489
  hasSearch
385
490
  />,
386
491
  );
387
- await user.click(screen.getByRole('combobox'));
388
- await user.type(screen.getByRole('searchbox', h), 'ban');
492
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
493
+ await user.type(screen.getByRole('combobox', h), 'ban');
389
494
  const options = screen.getAllByRole('option', h);
390
495
  expect(options).toHaveLength(1);
391
496
  expect(options[0]).toHaveTextContent('Banana');
@@ -402,8 +507,8 @@ describe('Selector', () => {
402
507
  hasSearch
403
508
  />,
404
509
  );
405
- await user.click(screen.getByRole('combobox'));
406
- await user.type(screen.getByRole('searchbox', h), 'xyz');
510
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
511
+ await user.type(screen.getByRole('combobox', h), 'xyz');
407
512
  expect(screen.queryAllByRole('option', h)).toHaveLength(0);
408
513
  expect(screen.getByText('No results found')).toBeInTheDocument();
409
514
  });
@@ -420,8 +525,8 @@ describe('Selector', () => {
420
525
  hasSearch
421
526
  />,
422
527
  );
423
- await user.click(screen.getByRole('combobox'));
424
- await user.type(screen.getByRole('searchbox', h), 'ban');
528
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
529
+ await user.type(screen.getByRole('combobox', h), 'ban');
425
530
  await user.click(screen.getByRole('option', {name: /Banana/, ...h}));
426
531
  expect(onChange).toHaveBeenCalledWith('Banana');
427
532
  });
@@ -441,7 +546,9 @@ describe('Selector', () => {
441
546
  </>,
442
547
  );
443
548
 
444
- const trigger = screen.getByRole('combobox');
549
+ // In hasSearch mode the trigger is a plain button (the popup's search
550
+ // input is the combobox); it still owns aria-expanded.
551
+ const trigger = screen.getByRole('button', {name: 'Fruit'});
445
552
  await user.click(trigger);
446
553
  expect(trigger).toHaveAttribute('aria-expanded', 'true');
447
554
 
@@ -461,10 +568,275 @@ describe('Selector', () => {
461
568
  searchPlaceholder="Find a fruit..."
462
569
  />,
463
570
  );
464
- await user.click(screen.getByRole('combobox'));
571
+ await user.click(screen.getByRole('button', {name: 'Fruit'}));
465
572
  expect(
466
573
  screen.getByPlaceholderText('Find a fruit...'),
467
574
  ).toBeInTheDocument();
468
575
  });
469
576
  });
577
+
578
+ describe('keyboard accessibility', () => {
579
+ it('trigger is focusable via Tab when enabled', async () => {
580
+ const user = userEvent.setup();
581
+ render(<Selector label="Fruit" options={OPTIONS} />);
582
+
583
+ await user.tab();
584
+ expect(screen.getByRole('combobox')).toHaveFocus();
585
+ });
586
+
587
+ it('trigger is not focusable when disabled', () => {
588
+ render(<Selector label="Fruit" options={OPTIONS} isDisabled />);
589
+ expect(screen.getByRole('combobox')).toHaveAttribute('tabIndex', '-1');
590
+ });
591
+
592
+ it('opens the listbox with ArrowDown from a focused trigger', async () => {
593
+ const user = userEvent.setup();
594
+ render(<Selector label="Fruit" options={OPTIONS} />);
595
+
596
+ const trigger = screen.getByRole('combobox');
597
+ await user.tab();
598
+ expect(trigger).toHaveFocus();
599
+
600
+ await user.keyboard('{ArrowDown}');
601
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
602
+ });
603
+
604
+ it('opens and selects an option with Enter (no mouse)', async () => {
605
+ const user = userEvent.setup();
606
+ const onChange = vi.fn();
607
+ render(<Selector label="Fruit" options={OPTIONS} onChange={onChange} />);
608
+
609
+ await user.tab();
610
+ await user.keyboard('{Enter}'); // open
611
+ await user.keyboard('{ArrowDown}'); // move highlight
612
+ await user.keyboard('{Enter}'); // select
613
+
614
+ expect(onChange).toHaveBeenCalled();
615
+ });
616
+
617
+ it('clear button is reachable by keyboard', () => {
618
+ render(
619
+ <Selector
620
+ label="Fruit"
621
+ options={OPTIONS}
622
+ value="Apple"
623
+ onChange={() => {}}
624
+ hasClear
625
+ />,
626
+ );
627
+ const clear = screen.getByRole('button', {name: 'Clear Fruit'});
628
+ expect(clear).not.toHaveAttribute('tabIndex', '-1');
629
+ });
630
+
631
+ it('scrolls the highlighted option into view during arrow navigation', async () => {
632
+ const scrollIntoView = vi.fn();
633
+ Object.defineProperty(HTMLElement.prototype, 'scrollIntoView', {
634
+ configurable: true,
635
+ value: scrollIntoView,
636
+ });
637
+ try {
638
+ const user = userEvent.setup();
639
+ const longOptions = Array.from(
640
+ {length: 20},
641
+ (_, i) => `Option ${i + 1}`,
642
+ );
643
+ render(<Selector label="Fruit" options={longOptions} />);
644
+
645
+ await user.tab();
646
+ await user.keyboard('{Enter}'); // open
647
+ scrollIntoView.mockClear();
648
+ await user.keyboard('{ArrowDown}'); // move highlight
649
+ await user.keyboard('{ArrowDown}');
650
+
651
+ expect(scrollIntoView).toHaveBeenCalledWith({block: 'nearest'});
652
+ } finally {
653
+ delete (HTMLElement.prototype as unknown as {scrollIntoView?: unknown})
654
+ .scrollIntoView;
655
+ }
656
+ });
657
+ });
658
+
659
+ describe('InputGroup integration', () => {
660
+ it('uses the group Field chrome and composes group and selector labels', () => {
661
+ render(
662
+ <InputGroup
663
+ label="Destination"
664
+ description="Where the alert should route"
665
+ status={{type: 'error', message: 'Destination is required'}}>
666
+ <InputGroupText>#</InputGroupText>
667
+ <Selector
668
+ label="Channel"
669
+ isLabelHidden
670
+ options={OPTIONS}
671
+ placeholder="Choose a channel"
672
+ />
673
+ </InputGroup>,
674
+ );
675
+
676
+ const group = screen.getByRole('group', {name: 'Destination'});
677
+ const groupLabelID = group.getAttribute('aria-labelledby');
678
+ const trigger = screen.getByRole('combobox', {
679
+ name: 'Destination Channel',
680
+ });
681
+ const labelledByIDs =
682
+ trigger.getAttribute('aria-labelledby')?.split(' ') ?? [];
683
+
684
+ expect(labelledByIDs).toHaveLength(2);
685
+ expect(labelledByIDs[0]).toBe(groupLabelID);
686
+ expect(document.getElementById(labelledByIDs[1])).toHaveTextContent(
687
+ 'Channel',
688
+ );
689
+ expect(trigger).toHaveAttribute(
690
+ 'aria-describedby',
691
+ group.getAttribute('aria-describedby'),
692
+ );
693
+ expect(screen.getByText('#')).toBeInTheDocument();
694
+ });
695
+
696
+ it('keeps disabled reasons described when grouped', () => {
697
+ render(
698
+ <InputGroup label="Destination">
699
+ <InputGroupText>#</InputGroupText>
700
+ <Selector
701
+ label="Channel"
702
+ isLabelHidden
703
+ options={OPTIONS}
704
+ isDisabled
705
+ disabledMessage="Choose a project first"
706
+ />
707
+ </InputGroup>,
708
+ );
709
+
710
+ const trigger = screen.getByRole('combobox', {
711
+ name: 'Destination Channel',
712
+ });
713
+ const tooltip = screen.getByRole('tooltip', h);
714
+
715
+ expect(trigger).not.toBeDisabled();
716
+ expect(trigger).toHaveAttribute('aria-disabled', 'true');
717
+ expect(trigger.getAttribute('aria-describedby')).toContain(tooltip.id);
718
+ });
719
+ });
720
+
721
+ describe('disabledMessage', () => {
722
+ it('shows the reason tooltip on hover when disabled with a reason', async () => {
723
+ render(
724
+ <Selector
725
+ label="Fruit"
726
+ options={OPTIONS}
727
+ isDisabled
728
+ disabledMessage="You need the Editor role"
729
+ data-testid="fruit-selector"
730
+ />,
731
+ );
732
+
733
+ const container = screen.getByTestId('fruit-selector');
734
+ const tooltip = screen.getByRole('tooltip', h);
735
+ expect(tooltip).toHaveTextContent('You need the Editor role');
736
+
737
+ fireEvent.mouseEnter(container);
738
+ await waitFor(() => {
739
+ expect(tooltip).toHaveAttribute('popover-open');
740
+ });
741
+
742
+ fireEvent.mouseLeave(container);
743
+ await waitFor(() => {
744
+ expect(tooltip).not.toHaveAttribute('popover-open');
745
+ });
746
+ });
747
+
748
+ it('shows the reason tooltip on keyboard focus', async () => {
749
+ const user = userEvent.setup();
750
+ render(
751
+ <Selector
752
+ label="Fruit"
753
+ options={OPTIONS}
754
+ isDisabled
755
+ disabledMessage="You need the Editor role"
756
+ />,
757
+ );
758
+
759
+ const tooltip = screen.getByRole('tooltip', h);
760
+ await user.tab();
761
+ expect(screen.getByRole('combobox')).toHaveFocus();
762
+ await waitFor(() => {
763
+ expect(tooltip).toHaveAttribute('popover-open');
764
+ });
765
+ });
766
+
767
+ it('does not render a tooltip when not disabled', () => {
768
+ render(
769
+ <Selector
770
+ label="Fruit"
771
+ options={OPTIONS}
772
+ disabledMessage="You need the Editor role"
773
+ />,
774
+ );
775
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
776
+ });
777
+
778
+ it('does not render a tooltip when disabled without a reason', () => {
779
+ render(<Selector label="Fruit" options={OPTIONS} isDisabled />);
780
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
781
+ });
782
+
783
+ it('keeps the trigger focusable via aria-disabled when a reason is provided', () => {
784
+ render(
785
+ <Selector
786
+ label="Fruit"
787
+ options={OPTIONS}
788
+ isDisabled
789
+ disabledMessage="You need the Editor role"
790
+ />,
791
+ );
792
+ const trigger = screen.getByRole('combobox');
793
+ expect(trigger).not.toBeDisabled();
794
+ expect(trigger).toHaveAttribute('aria-disabled', 'true');
795
+ expect(trigger).toHaveAttribute('tabIndex', '0');
796
+ });
797
+
798
+ it('links the reason tooltip from the trigger via aria-describedby', () => {
799
+ render(
800
+ <Selector
801
+ label="Fruit"
802
+ options={OPTIONS}
803
+ isDisabled
804
+ disabledMessage="You need the Editor role"
805
+ />,
806
+ );
807
+ const trigger = screen.getByRole('combobox');
808
+ const tooltip = screen.getByRole('tooltip', h);
809
+ expect(trigger.getAttribute('aria-describedby')).toContain(tooltip.id);
810
+ });
811
+
812
+ it('blocks activation while focusable-disabled', async () => {
813
+ const user = userEvent.setup();
814
+ const onChange = vi.fn();
815
+ render(
816
+ <Selector
817
+ label="Fruit"
818
+ options={OPTIONS}
819
+ onChange={onChange}
820
+ isDisabled
821
+ disabledMessage="You need the Editor role"
822
+ />,
823
+ );
824
+
825
+ const trigger = screen.getByRole('combobox');
826
+ await user.click(trigger);
827
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
828
+
829
+ await user.keyboard('{Enter}');
830
+ await user.keyboard('{ArrowDown}');
831
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
832
+ expect(onChange).not.toHaveBeenCalled();
833
+ });
834
+
835
+ it('remains non-focusable when disabled without a reason', () => {
836
+ render(<Selector label="Fruit" options={OPTIONS} isDisabled />);
837
+ const trigger = screen.getByRole('combobox');
838
+ expect(trigger).toBeDisabled();
839
+ expect(trigger).toHaveAttribute('tabIndex', '-1');
840
+ });
841
+ });
470
842
  });