@astryxdesign/core 0.1.2 → 0.1.3-canary.6d57c8d

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
@@ -9,7 +9,15 @@
9
9
  * SYNC: When Typeahead components change, update tests to match
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
12
+ import {
13
+ describe,
14
+ it,
15
+ expect,
16
+ vi,
17
+ beforeAll,
18
+ afterAll,
19
+ beforeEach,
20
+ } from 'vitest';
13
21
  import {render, screen, fireEvent, waitFor} from '@testing-library/react';
14
22
  import userEvent from '@testing-library/user-event';
15
23
  import {Typeahead} from './Typeahead';
@@ -122,6 +130,47 @@ describe('BaseTypeahead', () => {
122
130
  });
123
131
  });
124
132
 
133
+ it('announces the result count to a live region (comboboxes-6)', async () => {
134
+ render(
135
+ <BaseTypeahead
136
+ searchSource={fruitSource}
137
+ value={null}
138
+ onChange={() => {}}
139
+ debounceMs={0}
140
+ />,
141
+ );
142
+ const input = screen.getByRole('combobox');
143
+ fireEvent.change(input, {target: {value: 'Ap'}});
144
+
145
+ await waitFor(() => {
146
+ const region = document.querySelector(
147
+ '[data-astryx-live-region="polite"]',
148
+ );
149
+ expect(region?.textContent).toMatch(/\d+ results?/);
150
+ });
151
+ });
152
+
153
+ it('announces "no results found" when the search is empty (comboboxes-6)', async () => {
154
+ render(
155
+ <BaseTypeahead
156
+ searchSource={fruitSource}
157
+ value={null}
158
+ onChange={() => {}}
159
+ debounceMs={0}
160
+ emptySearchResultsText="No results found"
161
+ />,
162
+ );
163
+ const input = screen.getByRole('combobox');
164
+ fireEvent.change(input, {target: {value: 'zzzzz'}});
165
+
166
+ await waitFor(() => {
167
+ const region = document.querySelector(
168
+ '[data-astryx-live-region="polite"]',
169
+ );
170
+ expect(region).toHaveTextContent('No results found');
171
+ });
172
+ });
173
+
125
174
  it('disables input when isDisabled', () => {
126
175
  render(
127
176
  <BaseTypeahead
@@ -180,6 +229,90 @@ describe('BaseTypeahead', () => {
180
229
  });
181
230
  });
182
231
 
232
+ describe('BaseTypeahead focus-out', () => {
233
+ it('closes the dropdown when focus leaves the input', async () => {
234
+ render(
235
+ <>
236
+ <BaseTypeahead
237
+ searchSource={fruitSource}
238
+ value={null}
239
+ onChange={() => {}}
240
+ debounceMs={0}
241
+ />
242
+ <button type="button">Outside</button>
243
+ </>,
244
+ );
245
+ const input = screen.getByRole('combobox');
246
+ fireEvent.change(input, {target: {value: 'App'}});
247
+
248
+ await waitFor(() => {
249
+ expect(input).toHaveAttribute('aria-expanded', 'true');
250
+ });
251
+
252
+ // Focus moves to an element outside the field/dropdown → menu closes.
253
+ const outside = screen.getByRole('button', {name: 'Outside'});
254
+ fireEvent.blur(input, {relatedTarget: outside});
255
+
256
+ await waitFor(() => {
257
+ expect(input).toHaveAttribute('aria-expanded', 'false');
258
+ });
259
+ });
260
+
261
+ it('keeps the dropdown open when focus moves into the anchor wrapper', async () => {
262
+ const anchor = document.createElement('div');
263
+ document.body.appendChild(anchor);
264
+ const anchorRef = {current: anchor};
265
+ render(
266
+ <BaseTypeahead
267
+ searchSource={fruitSource}
268
+ value={null}
269
+ onChange={() => {}}
270
+ anchorRef={anchorRef}
271
+ debounceMs={0}
272
+ />,
273
+ );
274
+ const input = screen.getByRole('combobox');
275
+ // The input lives inside the wrapper we hand to anchorRef.
276
+ anchor.appendChild(input.closest('div') ?? input);
277
+ fireEvent.change(input, {target: {value: 'App'}});
278
+
279
+ await waitFor(() => {
280
+ expect(input).toHaveAttribute('aria-expanded', 'true');
281
+ });
282
+
283
+ // A sibling control inside the field (e.g. a clear button) receives focus.
284
+ const sibling = document.createElement('button');
285
+ anchor.appendChild(sibling);
286
+ fireEvent.blur(input, {relatedTarget: sibling});
287
+
288
+ // Menu stays open because focus is still within the field.
289
+ expect(input).toHaveAttribute('aria-expanded', 'true');
290
+ document.body.removeChild(anchor);
291
+ });
292
+
293
+ it('does not close when a dropdown option receives focus', async () => {
294
+ render(
295
+ <BaseTypeahead
296
+ searchSource={fruitSource}
297
+ value={null}
298
+ onChange={() => {}}
299
+ debounceMs={0}
300
+ />,
301
+ );
302
+ const input = screen.getByRole('combobox');
303
+ fireEvent.change(input, {target: {value: 'App'}});
304
+
305
+ await waitFor(() => {
306
+ expect(input).toHaveAttribute('aria-expanded', 'true');
307
+ });
308
+
309
+ const option = screen.getByRole('option', {hidden: true});
310
+ fireEvent.blur(input, {relatedTarget: option});
311
+
312
+ expect(input).toHaveAttribute('aria-expanded', 'true');
313
+ });
314
+ });
315
+
183
316
  describe('Typeahead', () => {
184
317
  it('renders with label', () => {
185
318
  render(
@@ -573,4 +706,201 @@ describe('BaseTypeahead paste behavior', () => {
573
706
  expect(screen.getByText('No results found')).toBeInTheDocument();
574
707
  });
575
708
  });
709
+
710
+ it('scrolls the highlighted option into view during arrow navigation', async () => {
711
+ const scrollIntoView = vi.fn();
712
+ Object.defineProperty(HTMLElement.prototype, 'scrollIntoView', {
713
+ configurable: true,
714
+ value: scrollIntoView,
715
+ });
716
+ try {
717
+ const user = userEvent.setup();
718
+ render(
719
+ <BaseTypeahead
720
+ searchSource={fruitSource}
721
+ value={null}
722
+ onChange={() => {}}
723
+ debounceMs={0}
724
+ />,
725
+ );
726
+
727
+ const input = screen.getByRole('combobox');
728
+ await user.click(input);
729
+ await user.paste('e'); // matches multiple fruits, opens listbox
730
+ await waitFor(() => {
731
+ expect(screen.getByRole('listbox', {hidden: true})).toBeInTheDocument();
732
+ });
733
+
734
+ scrollIntoView.mockClear();
735
+ await user.keyboard('{ArrowDown}');
736
+ await user.keyboard('{ArrowDown}');
737
+
738
+ expect(scrollIntoView).toHaveBeenCalledWith({block: 'nearest'});
739
+ } finally {
740
+ delete (HTMLElement.prototype as unknown as {scrollIntoView?: unknown})
741
+ .scrollIntoView;
742
+ }
743
+ });
744
+ });
745
+
746
+ describe('Typeahead disabledMessage', () => {
747
+ // jsdom does not implement the Popover API used by the tooltip, so mock
748
+ // showPopover/hidePopover to toggle a `popover-open` attribute the tests
749
+ // can assert on.
750
+ beforeEach(() => {
751
+ HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
752
+ this.setAttribute('popover-open', '');
753
+ });
754
+ HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
755
+ this.removeAttribute('popover-open');
756
+ });
757
+ });
758
+
759
+ // jsdom popover content is in the DOM but not "visible" in the
760
+ // accessibility tree; use hidden: true to find it.
761
+ const h = {hidden: true} as const;
762
+
763
+ it('shows the reason tooltip on hover when disabled with a reason', async () => {
764
+ render(
765
+ <Typeahead
766
+ label="Assignee"
767
+ searchSource={fruitSource}
768
+ value={null}
769
+ onChange={() => {}}
770
+ isDisabled
771
+ disabledMessage="You need the Editor role"
772
+ />,
773
+ );
774
+
775
+ const container = screen.getByRole('combobox').parentElement as HTMLElement;
776
+ const tooltip = screen.getByRole('tooltip', h);
777
+ expect(tooltip).toHaveTextContent('You need the Editor role');
778
+
779
+ fireEvent.mouseEnter(container);
780
+ await waitFor(() => {
781
+ expect(tooltip).toHaveAttribute('popover-open');
782
+ });
783
+
784
+ fireEvent.mouseLeave(container);
785
+ await waitFor(() => {
786
+ expect(tooltip).not.toHaveAttribute('popover-open');
787
+ });
788
+ });
789
+
790
+ it('shows the reason tooltip on keyboard focus', async () => {
791
+ const user = userEvent.setup();
792
+ render(
793
+ <Typeahead
794
+ label="Assignee"
795
+ searchSource={fruitSource}
796
+ value={null}
797
+ onChange={() => {}}
798
+ isDisabled
799
+ disabledMessage="You need the Editor role"
800
+ />,
801
+ );
802
+
803
+ const tooltip = screen.getByRole('tooltip', h);
804
+ await user.tab();
805
+ expect(screen.getByRole('combobox')).toHaveFocus();
806
+ await waitFor(() => {
807
+ expect(tooltip).toHaveAttribute('popover-open');
808
+ });
809
+ });
810
+
811
+ it('does not render a tooltip when not disabled', () => {
812
+ render(
813
+ <Typeahead
814
+ label="Assignee"
815
+ searchSource={fruitSource}
816
+ value={null}
817
+ onChange={() => {}}
818
+ disabledMessage="You need the Editor role"
819
+ />,
820
+ );
821
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
822
+ });
823
+
824
+ it('does not render a tooltip when disabled without a reason', () => {
825
+ render(
826
+ <Typeahead
827
+ label="Assignee"
828
+ searchSource={fruitSource}
829
+ value={null}
830
+ onChange={() => {}}
831
+ isDisabled
832
+ />,
833
+ );
834
+ expect(screen.queryByRole('tooltip', h)).not.toBeInTheDocument();
835
+ });
836
+
837
+ it('keeps the input focusable via aria-disabled when a reason is provided', () => {
838
+ render(
839
+ <Typeahead
840
+ label="Assignee"
841
+ searchSource={fruitSource}
842
+ value={null}
843
+ onChange={() => {}}
844
+ isDisabled
845
+ disabledMessage="You need the Editor role"
846
+ />,
847
+ );
848
+ const input = screen.getByRole('combobox');
849
+ expect(input).not.toBeDisabled();
850
+ expect(input).toHaveAttribute('aria-disabled', 'true');
851
+ expect(input).toHaveAttribute('readonly');
852
+ });
853
+
854
+ it('links the reason tooltip from the input via aria-describedby', () => {
855
+ render(
856
+ <Typeahead
857
+ label="Assignee"
858
+ searchSource={fruitSource}
859
+ value={null}
860
+ onChange={() => {}}
861
+ isDisabled
862
+ disabledMessage="You need the Editor role"
863
+ />,
864
+ );
865
+ const input = screen.getByRole('combobox');
866
+ const tooltip = screen.getByRole('tooltip', h);
867
+ expect(input.getAttribute('aria-describedby')).toContain(tooltip.id);
868
+ });
869
+
870
+ it('blocks typing and selection while focusable-disabled', async () => {
871
+ const user = userEvent.setup();
872
+ const onChange = vi.fn();
873
+ render(
874
+ <Typeahead
875
+ label="Assignee"
876
+ searchSource={fruitSource}
877
+ value={null}
878
+ onChange={onChange}
879
+ isDisabled
880
+ disabledMessage="You need the Editor role"
881
+ />,
882
+ );
883
+
884
+ const input = screen.getByRole('combobox');
885
+ await user.click(input);
886
+ await user.type(input, 'App');
887
+ expect(input).toHaveValue('');
888
+ expect(input).toHaveAttribute('aria-expanded', 'false');
889
+ expect(onChange).not.toHaveBeenCalled();
890
+ });
891
+
892
+ it('remains natively disabled when disabled without a reason', () => {
893
+ render(
894
+ <Typeahead
895
+ label="Assignee"
896
+ searchSource={fruitSource}
897
+ value={null}
898
+ onChange={() => {}}
899
+ isDisabled
900
+ />,
901
+ );
902
+ const input = screen.getByRole('combobox');
903
+ expect(input).toBeDisabled();
904
+ expect(input).not.toHaveAttribute('aria-disabled');
905
+ });
576
906
  });
@@ -4,9 +4,9 @@
4
4
 
5
5
  /**
6
6
  * @file Typeahead.tsx
7
- * @input Uses React, BaseTypeahead, Field, Token
7
+ * @input Uses React, BaseTypeahead, Field, Token, InputGroupContext
8
8
  * @output Exports Typeahead styled typeahead component
9
- * @position Styled wrapper; composes BaseTypeahead with Field
9
+ * @position Styled wrapper; composes BaseTypeahead with Field or InputGroup
10
10
  *
11
11
  * Owns the input wrapper (border, padding, status styles), selected value
12
12
  * token with spacing compensation, and edit mode behavior. Delegates
@@ -38,9 +38,13 @@ import {
38
38
  inputStatusFocusWithinStyles,
39
39
  } from '../Field';
40
40
  import {Token} from '../Token';
41
+ import {useTooltip} from '../Tooltip';
41
42
  import {renderIconSlot, type IconType} from '../Icon';
43
+ import {VisuallyHidden} from '../VisuallyHidden';
42
44
  import {spacingVars, sizeVars} from '../theme/tokens.stylex';
43
- import {mergeProps} from '../utils';
45
+ import {groupStyles} from '../InputGroup/groupStyles';
46
+ import {useInputGroup} from '../InputGroup/InputGroupContext';
47
+ import {getInputARIA, mergeProps, mergeRefs} from '../utils';
44
48
  import type {BaseProps} from '../BaseProps';
45
49
  import type {SizeValue} from '../utils/types';
46
50
  import type {SearchableItem, SearchSource} from './types';
@@ -101,6 +105,29 @@ export interface TypeaheadProps<T extends SearchableItem> extends Omit<
101
105
  emptySearchResultsText?: string;
102
106
  /** Whether the input is disabled. @default false */
103
107
  isDisabled?: boolean;
108
+ /**
109
+ * Explains why the input is disabled. When set together with `isDisabled`,
110
+ * the input shows a tooltip with this text on hover and keyboard focus, and
111
+ * the field stays focusable (via `aria-disabled`) so the reason is
112
+ * discoverable by keyboard and assistive technology. Editing and selection
113
+ * stay blocked.
114
+ *
115
+ * Use this instead of wrapping a disabled input in `Tooltip` — disabled
116
+ * controls don't emit the pointer events an external tooltip needs.
117
+ *
118
+ * @example
119
+ * ```
120
+ * <Typeahead
121
+ * label="Assignee"
122
+ * searchSource={userSource}
123
+ * value={assignee}
124
+ * onChange={setAssignee}
125
+ * isDisabled
126
+ * disabledMessage="You need the Editor role to change this"
127
+ * />
128
+ * ```
129
+ */
130
+ disabledMessage?: string;
104
131
  /** Show clear button. @default true */
105
132
  hasClear?: boolean;
106
133
  /** Auto-focus on mount. @default false */
@@ -209,6 +236,7 @@ export function Typeahead<T extends SearchableItem>({
209
236
  maxMenuItems,
210
237
  emptySearchResultsText,
211
238
  isDisabled = false,
239
+ disabledMessage,
212
240
  hasClear = true,
213
241
  hasAutoFocus,
214
242
  size: sizeProp,
@@ -223,13 +251,30 @@ export function Typeahead<T extends SearchableItem>({
223
251
  }: TypeaheadProps<T>) {
224
252
  const size = useSize(sizeProp, 'md');
225
253
  const inputId = useId();
254
+ const inputLabelId = useId();
226
255
  const descriptionId = useId();
227
256
  const statusMessageId = useId();
257
+ const inputGroup = useInputGroup();
228
258
 
229
259
  const wrapperRef = useRef<HTMLDivElement>(null);
230
260
  const inputRef = useRef<HTMLInputElement>(null);
231
261
  const tokenRef = useRef<HTMLElement>(null);
232
262
 
263
+ // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
264
+ // tooltip listeners attach to the input wrapper (which already exists) and
265
+ // the input stays perceivable via aria-disabled + readOnly instead of the
266
+ // disabled attribute. Editing and selection stay blocked by the isDisabled
267
+ // guards (handleWrapperClick, handleEnterEditMode, and BaseTypeahead's own
268
+ // focus/change guards).
269
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
270
+ const disabledMessageTooltip = useTooltip({
271
+ placement: 'above',
272
+ // The wrapper div is not naturally focusable; focusin bubbles up from the
273
+ // input, so always attach focus listeners.
274
+ focusTrigger: 'always',
275
+ isEnabled: showsDisabledMessage,
276
+ });
277
+
233
278
  // Edit mode: when the user clicks the token to edit the selected value
234
279
  const [isEditing, setIsEditing] = useState(false);
235
280
  const [editingValue, setEditingValue] = useState<T | null>(null);
@@ -335,43 +380,26 @@ export function Typeahead<T extends SearchableItem>({
335
380
  }
336
381
  }, [isDisabled, showToken, handleEnterEditMode]);
337
382
 
338
- const ariaDescribedBy =
383
+ const {ariaLabelledBy, ariaDescribedBy} = getInputARIA(
384
+ inputLabelId,
339
385
  [
340
386
  description ? descriptionId : null,
341
387
  status?.message ? statusMessageId : null,
342
- ]
343
- .filter(Boolean)
344
- .join(' ') || undefined;
388
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
389
+ ],
390
+ inputGroup,
391
+ );
345
392
 
346
393
  const sizeStyle = wrapperSizeStyles[size];
347
394
 
348
- return (
349
- <Field
350
- ref={ref}
351
- label={label}
352
- isLabelHidden={isLabelHidden}
353
- description={description}
354
- inputID={inputId}
355
- descriptionID={description ? descriptionId : undefined}
356
- isOptional={isOptional}
357
- isRequired={isRequired}
358
- isDisabled={isDisabled}
359
- status={
360
- status
361
- ? {
362
- type: status.type,
363
- message: status.message,
364
- messageID: status.message ? statusMessageId : undefined,
365
- }
366
- : undefined
367
- }
368
- labelTooltip={labelTooltip}
369
- width={width}
370
- xstyle={xstyle}
371
- className={className}
372
- style={style}>
395
+ const typeaheadContent = (
396
+ <>
373
397
  <div
374
- ref={wrapperRef}
398
+ ref={mergeRefs(
399
+ wrapperRef,
400
+ disabledMessageTooltip.ref,
401
+ inputGroup ? ref : undefined,
402
+ )}
375
403
  data-testid={testId}
376
404
  onClick={handleWrapperClick}
377
405
  onBlur={handleBlur}
@@ -385,10 +413,17 @@ export function Typeahead<T extends SearchableItem>({
385
413
  status && inputStatusHoverShadowStyles[status.type],
386
414
  status && inputStatusFocusWithinStyles[status.type],
387
415
  isDisabled && inputWrapperStyles.disabled,
416
+ inputGroup && groupStyles.inGroup,
417
+ inputGroup && xstyle,
388
418
  ),
419
+ inputGroup ? className : undefined,
420
+ inputGroup ? style : undefined,
389
421
  )}>
390
422
  {startIcon &&
391
423
  renderIconSlot(startIcon, {size: 'sm', color: 'secondary'})}
424
+ {inputGroup && (
425
+ <VisuallyHidden id={inputLabelId}>{label}</VisuallyHidden>
426
+ )}
392
427
  {showToken && (
393
428
  <Token
394
429
  ref={tokenRef}
@@ -411,8 +446,10 @@ export function Typeahead<T extends SearchableItem>({
411
446
  emptySearchResultsText={emptySearchResultsText}
412
447
  isDisabled={isDisabled}
413
448
  hasAutoFocus={hasAutoFocus}
449
+ isFocusableDisabled={showsDisabledMessage}
414
450
  inputId={inputId}
415
451
  ariaDescribedBy={ariaDescribedBy}
452
+ ariaLabelledBy={ariaLabelledBy}
416
453
  onChangeQuery={onChangeQuery}
417
454
  onOpenChange={onOpenChange}
418
455
  debounceMs={debounceMs}
@@ -432,6 +469,41 @@ export function Typeahead<T extends SearchableItem>({
432
469
  />
433
470
  )}
434
471
  </div>
472
+ {showsDisabledMessage &&
473
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
474
+ </>
475
+ );
476
+
477
+ if (inputGroup) {
478
+ return typeaheadContent;
479
+ }
480
+
481
+ return (
482
+ <Field
483
+ ref={ref}
484
+ label={label}
485
+ isLabelHidden={isLabelHidden}
486
+ description={description}
487
+ inputID={inputId}
488
+ descriptionID={description ? descriptionId : undefined}
489
+ isOptional={isOptional}
490
+ isRequired={isRequired}
491
+ isDisabled={isDisabled}
492
+ status={
493
+ status
494
+ ? {
495
+ type: status.type,
496
+ message: status.message,
497
+ messageID: status.message ? statusMessageId : undefined,
498
+ }
499
+ : undefined
500
+ }
501
+ labelTooltip={labelTooltip}
502
+ width={width}
503
+ xstyle={xstyle}
504
+ className={className}
505
+ style={style}>
506
+ {typeaheadContent}
435
507
  </Field>
436
508
  );
437
509
  }
@@ -0,0 +1,80 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+
4
+
5
+ /** @type {import('../docs-types').ComponentDoc} */
6
+
7
+ export const docs = {
8
+ name: 'VisuallyHidden',
9
+ displayName: 'VisuallyHidden',
10
+ category: 'Utility',
11
+ keywords: ["visually hidden","sr-only","screen reader","accessibility","a11y","aria-live","hidden label","assistive technology"],
12
+ props: [
13
+ {
14
+ name: 'children',
15
+ type: 'ReactNode',
16
+ description: 'Content exposed to assistive technology while hidden from sight.',
17
+ },
18
+ {
19
+ name: 'as',
20
+ type: 'ElementType',
21
+ description: 'HTML tag to render as. Use a block element for live regions.',
22
+ default: "'span'",
23
+ },
24
+ ],
25
+ usage: {
26
+ description: 'Renders content in the accessibility tree while hiding it visually. Use for accessible names on icon-only controls, aria-live announcement regions, and supplementary screen-reader context. Deliberately has no styling props; the whole point is to stay invisible.',
27
+ bestPractices: [
28
+ { guidance: true, description: 'Use to give icon-only buttons and controls an accessible name that screen readers announce.' },
29
+ { guidance: true, description: 'Render as a block element (as="div") with aria-live to announce dynamic updates like drag-and-drop or result counts.' },
30
+ { guidance: false, description: 'Use it to hide content from everyone; it stays in the accessibility tree; use conditional rendering or `hidden` to remove content entirely.' },
31
+ { guidance: false, description: 'Put interactive controls inside it; the content is not visible and cannot receive pointer input.' },
32
+ ],
33
+ },
34
+ };
35
+
36
+ /** @type {import('../docs-types').ComponentDoc} */
37
+ export const docsZh = {
38
+ name: 'VisuallyHidden',
39
+ displayName: 'VisuallyHidden',
40
+ props: [
41
+ {
42
+ name: 'children',
43
+ type: 'ReactNode',
44
+ description: '对辅助技术公开但对视觉隐藏的内容。',
45
+ },
46
+ {
47
+ name: 'as',
48
+ type: 'ElementType',
49
+ description: '要渲染的 HTML 标签。实时区域请使用块级元素。',
50
+ default: "'span'",
51
+ },
52
+ ],
53
+ usage: {
54
+ description: 'Renders content in the accessibility tree while hiding it visually. Use for accessible names on icon-only controls, aria-live announcement regions, and supplementary screen-reader context.',
55
+ bestPractices: [
56
+ { guidance: true, description: 'Use to give icon-only buttons and controls an accessible name that screen readers announce.' },
57
+ { guidance: true, description: 'Render as a block element (as="div") with aria-live to announce dynamic updates like drag-and-drop or result counts.' },
58
+ { guidance: false, description: 'Use it to hide content from everyone; it stays in the accessibility tree; use conditional rendering or `hidden` to remove content entirely.' },
59
+ { guidance: false, description: 'Put interactive controls inside it; the content is not visible and cannot receive pointer input.' },
60
+ ],
61
+ },
62
+ };
63
+
64
+ /** @type {import('../docs-types').TranslationDoc} */
65
+ export const docsDense = {
66
+ description: 'renders content in the a11y tree while hiding it visually (sr-only)',
67
+ usage: {
68
+ description: 'Renders content in the accessibility tree while hiding it visually. Use for accessible names on icon-only controls, aria-live announcement regions, and supplementary screen-reader context.',
69
+ bestPractices: [
70
+ { guidance: true, description: 'Use to give icon-only buttons and controls an accessible name that screen readers announce.' },
71
+ { guidance: true, description: 'Render as a block element (as="div") with aria-live to announce dynamic updates like drag-and-drop or result counts.' },
72
+ { guidance: false, description: 'Use it to hide content from everyone; it stays in the accessibility tree; use conditional rendering or `hidden` to remove content entirely.' },
73
+ { guidance: false, description: 'Put interactive controls inside it; the content is not visible and cannot receive pointer input.' },
74
+ ],
75
+ },
76
+ propDescriptions: {
77
+ children: 'content exposed to AT while visually hidden',
78
+ as: 'HTML tag to render as; block element for live regions',
79
+ },
80
+ };