@astryxdesign/core 0.1.1 → 0.1.2-canary.04e71b2

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 (631) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +131 -13
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +11 -5
  5. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  6. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
  8. package/dist/Banner/Banner.d.ts +7 -0
  9. package/dist/Banner/Banner.d.ts.map +1 -1
  10. package/dist/Banner/Banner.js +9 -2
  11. package/dist/Breadcrumbs/BreadcrumbItem.d.ts +1 -1
  12. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  13. package/dist/Breadcrumbs/BreadcrumbItem.js +24 -5
  14. package/dist/Button/Button.d.ts +12 -2
  15. package/dist/Button/Button.d.ts.map +1 -1
  16. package/dist/Button/Button.js +53 -17
  17. package/dist/Calendar/Calendar.d.ts +5 -4
  18. package/dist/Calendar/Calendar.d.ts.map +1 -1
  19. package/dist/Calendar/Calendar.js +82 -43
  20. package/dist/Calendar/hooks/index.d.ts +0 -2
  21. package/dist/Calendar/hooks/index.d.ts.map +1 -1
  22. package/dist/Calendar/hooks/index.js +1 -2
  23. package/dist/Calendar/index.d.ts +3 -3
  24. package/dist/Calendar/index.d.ts.map +1 -1
  25. package/dist/Calendar/index.js +1 -1
  26. package/dist/Calendar/styles.d.ts +3 -9
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +3 -11
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +17 -3
  31. package/dist/Center/Center.d.ts +11 -1
  32. package/dist/Center/Center.d.ts.map +1 -1
  33. package/dist/Center/Center.js +9 -3
  34. package/dist/Chat/ChatMessageList.d.ts +14 -1
  35. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  36. package/dist/Chat/ChatMessageList.js +2 -0
  37. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  38. package/dist/Chat/useTriggerMenu.js +4 -1
  39. package/dist/CheckboxInput/CheckboxInput.d.ts +23 -2
  40. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  41. package/dist/CheckboxInput/CheckboxInput.js +51 -9
  42. package/dist/CheckboxList/CheckboxList.d.ts +13 -1
  43. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxList.js +39 -9
  45. package/dist/CheckboxList/CheckboxListContext.d.ts +7 -0
  46. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  47. package/dist/Citation/Citation.d.ts.map +1 -1
  48. package/dist/Citation/Citation.js +3 -3
  49. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  50. package/dist/CodeBlock/CodeBlock.js +49 -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 +59 -8
  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/EmptyState/EmptyState.d.ts.map +1 -1
  71. package/dist/EmptyState/EmptyState.js +7 -1
  72. package/dist/Field/Field.d.ts +19 -2
  73. package/dist/Field/Field.d.ts.map +1 -1
  74. package/dist/Field/Field.js +4 -0
  75. package/dist/Field/FieldLabel.d.ts +21 -2
  76. package/dist/Field/FieldLabel.d.ts.map +1 -1
  77. package/dist/Field/FieldLabel.js +41 -26
  78. package/dist/FileInput/FileInput.d.ts +22 -1
  79. package/dist/FileInput/FileInput.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.js +56 -10
  81. package/dist/Grid/Grid.d.ts +11 -1
  82. package/dist/Grid/Grid.d.ts.map +1 -1
  83. package/dist/Grid/Grid.js +8 -0
  84. package/dist/HoverCard/HoverCard.d.ts +2 -2
  85. package/dist/HoverCard/HoverCard.d.ts.map +1 -1
  86. package/dist/HoverCard/HoverCard.js +18 -6
  87. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  88. package/dist/HoverCard/useHoverCard.js +7 -3
  89. package/dist/InputGroup/InputGroup.d.ts +2 -2
  90. package/dist/InputGroup/InputGroup.d.ts.map +1 -1
  91. package/dist/InputGroup/InputGroup.js +14 -5
  92. package/dist/InputGroup/InputGroupContext.d.ts +4 -0
  93. package/dist/InputGroup/InputGroupContext.d.ts.map +1 -1
  94. package/dist/InputGroup/InputGroupContext.js +2 -2
  95. package/dist/InputGroup/index.d.ts +1 -0
  96. package/dist/InputGroup/index.d.ts.map +1 -1
  97. package/dist/Kbd/Kbd.d.ts.map +1 -1
  98. package/dist/Kbd/Kbd.js +34 -2
  99. package/dist/Layer/anchorName.d.ts +20 -0
  100. package/dist/Layer/anchorName.d.ts.map +1 -0
  101. package/dist/Layer/anchorName.js +36 -0
  102. package/dist/Layer/layerAnimations.stylex.d.ts +4 -0
  103. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  104. package/dist/Layer/layerAnimations.stylex.js +8 -4
  105. package/dist/Layer/useLayer.d.ts +29 -0
  106. package/dist/Layer/useLayer.d.ts.map +1 -1
  107. package/dist/Layer/useLayer.js +55 -11
  108. package/dist/Layout/Layout.d.ts +4 -11
  109. package/dist/Layout/Layout.d.ts.map +1 -1
  110. package/dist/Layout/Layout.js +10 -11
  111. package/dist/Layout/LayoutFooter.d.ts +2 -2
  112. package/dist/Layout/LayoutFooter.d.ts.map +1 -1
  113. package/dist/Layout/LayoutHeader.d.ts +2 -2
  114. package/dist/Layout/LayoutHeader.d.ts.map +1 -1
  115. package/dist/Layout/LayoutPanel.d.ts +2 -2
  116. package/dist/Layout/LayoutPanel.d.ts.map +1 -1
  117. package/dist/Layout/padding.stylex.d.ts +51 -0
  118. package/dist/Layout/padding.stylex.d.ts.map +1 -1
  119. package/dist/Layout/padding.stylex.js +63 -0
  120. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  121. package/dist/Lightbox/Lightbox.js +4 -2
  122. package/dist/Link/Link.d.ts +17 -2
  123. package/dist/Link/Link.d.ts.map +1 -1
  124. package/dist/Link/Link.js +20 -9
  125. package/dist/Link/LinkProvider.d.ts +1 -0
  126. package/dist/Link/LinkProvider.d.ts.map +1 -1
  127. package/dist/Link/LinkProvider.js +1 -0
  128. package/dist/Markdown/Markdown.d.ts.map +1 -1
  129. package/dist/Markdown/Markdown.js +18 -3
  130. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  131. package/dist/MobileNav/MobileNav.js +13 -0
  132. package/dist/MoreMenu/MoreMenu.d.ts +1 -7
  133. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  134. package/dist/MoreMenu/MoreMenu.js +0 -2
  135. package/dist/MultiSelector/MultiSelector.d.ts +25 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  137. package/dist/MultiSelector/MultiSelector.js +103 -22
  138. package/dist/MultiSelector/hooks.d.ts +12 -1
  139. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  140. package/dist/MultiSelector/hooks.js +14 -1
  141. package/dist/NavMenu/NavHeadingMenu.d.ts.map +1 -1
  142. package/dist/NavMenu/NavHeadingMenu.js +34 -3
  143. package/dist/NumberInput/NumberInput.d.ts +23 -2
  144. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  145. package/dist/NumberInput/NumberInput.js +62 -12
  146. package/dist/Pagination/Pagination.d.ts +1 -1
  147. package/dist/Pagination/Pagination.d.ts.map +1 -1
  148. package/dist/Pagination/Pagination.js +46 -28
  149. package/dist/Popover/usePopover.d.ts +25 -2
  150. package/dist/Popover/usePopover.d.ts.map +1 -1
  151. package/dist/Popover/usePopover.js +18 -6
  152. package/dist/PowerSearch/PowerSearch.d.ts +11 -1
  153. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  154. package/dist/PowerSearch/PowerSearch.js +6 -1
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +3 -5
  157. package/dist/RadioList/RadioList.d.ts +20 -2
  158. package/dist/RadioList/RadioList.d.ts.map +1 -1
  159. package/dist/RadioList/RadioList.js +105 -9
  160. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  161. package/dist/RadioList/RadioListItem.js +13 -2
  162. package/dist/SegmentedControl/SegmentedControl.d.ts +13 -2
  163. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  164. package/dist/SegmentedControl/SegmentedControl.js +78 -44
  165. package/dist/SegmentedControl/SegmentedControlContext.d.ts +7 -0
  166. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  167. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  168. package/dist/SegmentedControl/SegmentedControlItem.js +13 -2
  169. package/dist/Selector/Selector.d.ts +22 -1
  170. package/dist/Selector/Selector.d.ts.map +1 -1
  171. package/dist/Selector/Selector.js +84 -23
  172. package/dist/Selector/hooks.d.ts +8 -1
  173. package/dist/Selector/hooks.d.ts.map +1 -1
  174. package/dist/Selector/hooks.js +14 -1
  175. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  176. package/dist/SideNav/SideNavItem.js +1 -1
  177. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  178. package/dist/Skeleton/Skeleton.js +8 -2
  179. package/dist/Slider/Slider.d.ts +21 -0
  180. package/dist/Slider/Slider.d.ts.map +1 -1
  181. package/dist/Slider/Slider.js +38 -11
  182. package/dist/Spinner/Spinner.d.ts.map +1 -1
  183. package/dist/Spinner/Spinner.js +1 -1
  184. package/dist/Stack/Stack.d.ts +38 -23
  185. package/dist/Stack/Stack.d.ts.map +1 -1
  186. package/dist/Stack/Stack.js +25 -1
  187. package/dist/Stack/StackItem.d.ts +12 -23
  188. package/dist/Stack/StackItem.d.ts.map +1 -1
  189. package/dist/Stack/StackItem.js +8 -1
  190. package/dist/Switch/Switch.d.ts +23 -2
  191. package/dist/Switch/Switch.d.ts.map +1 -1
  192. package/dist/Switch/Switch.js +41 -11
  193. package/dist/TabList/Tab.d.ts.map +1 -1
  194. package/dist/TabList/Tab.js +6 -0
  195. package/dist/TabList/TabList.d.ts +16 -4
  196. package/dist/TabList/TabList.d.ts.map +1 -1
  197. package/dist/TabList/TabList.js +86 -10
  198. package/dist/TabList/TabListContext.d.ts +5 -0
  199. package/dist/TabList/TabListContext.d.ts.map +1 -1
  200. package/dist/TabList/TabListContext.js +5 -0
  201. package/dist/TabList/TabMenu.d.ts.map +1 -1
  202. package/dist/TabList/TabMenu.js +6 -1
  203. package/dist/Table/BaseTable.d.ts.map +1 -1
  204. package/dist/Table/BaseTable.js +38 -16
  205. package/dist/Table/Table.d.ts.map +1 -1
  206. package/dist/Table/Table.js +36 -7
  207. package/dist/Table/TableCell.d.ts +8 -1
  208. package/dist/Table/TableCell.d.ts.map +1 -1
  209. package/dist/Table/TableCell.js +10 -10
  210. package/dist/Table/TableHeaderCell.d.ts +8 -1
  211. package/dist/Table/TableHeaderCell.d.ts.map +1 -1
  212. package/dist/Table/TableHeaderCell.js +16 -16
  213. package/dist/Table/TableRow.d.ts +6 -1
  214. package/dist/Table/TableRow.d.ts.map +1 -1
  215. package/dist/Table/TableRow.js +15 -7
  216. package/dist/Table/index.d.ts +4 -1
  217. package/dist/Table/index.d.ts.map +1 -1
  218. package/dist/Table/index.js +3 -2
  219. package/dist/Table/plugins/pagination/paginateData.d.ts +2 -1
  220. package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
  221. package/dist/Table/plugins/pagination/paginateData.js +8 -3
  222. package/dist/Table/plugins/pagination/useTablePagination.d.ts.map +1 -1
  223. package/dist/Table/plugins/pagination/useTablePagination.js +6 -1
  224. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  225. package/dist/Table/plugins/sortable/useTableSortable.js +59 -3
  226. package/dist/Table/plugins/stickyColumns/index.d.ts +3 -0
  227. package/dist/Table/plugins/stickyColumns/index.d.ts.map +1 -0
  228. package/dist/Table/plugins/stickyColumns/index.js +3 -0
  229. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts +25 -0
  230. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -0
  231. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +369 -0
  232. package/dist/Table/tableContextMenu.d.ts +33 -0
  233. package/dist/Table/tableContextMenu.d.ts.map +1 -0
  234. package/dist/Table/tableContextMenu.js +133 -0
  235. package/dist/Table/types.d.ts +161 -5
  236. package/dist/Table/types.d.ts.map +1 -1
  237. package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
  238. package/dist/Table/useBaseTablePlugins.js +1 -1
  239. package/dist/Text/text.stylex.d.ts +2 -2
  240. package/dist/Text/text.stylex.js +2 -2
  241. package/dist/TextArea/TextArea.d.ts +22 -1
  242. package/dist/TextArea/TextArea.d.ts.map +1 -1
  243. package/dist/TextArea/TextArea.js +41 -9
  244. package/dist/TextInput/TextInput.d.ts +23 -2
  245. package/dist/TextInput/TextInput.d.ts.map +1 -1
  246. package/dist/TextInput/TextInput.js +55 -10
  247. package/dist/TimeInput/TimeInput.d.ts +23 -1
  248. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  249. package/dist/TimeInput/TimeInput.js +53 -10
  250. package/dist/Timestamp/Timestamp.d.ts +1 -1
  251. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  252. package/dist/Timestamp/Timestamp.js +28 -3
  253. package/dist/Toast/Toast.d.ts.map +1 -1
  254. package/dist/Toast/Toast.js +14 -0
  255. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  256. package/dist/Toast/ToastViewport.js +132 -3
  257. package/dist/ToggleButton/ToggleButton.d.ts +7 -9
  258. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  259. package/dist/ToggleButton/ToggleButton.js +34 -57
  260. package/dist/Tokenizer/Tokenizer.d.ts +12 -1
  261. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  262. package/dist/Tokenizer/Tokenizer.js +27 -5
  263. package/dist/Toolbar/Toolbar.d.ts +3 -3
  264. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  265. package/dist/Toolbar/Toolbar.js +51 -7
  266. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  267. package/dist/Tooltip/useTooltip.js +57 -9
  268. package/dist/TreeList/TreeList.d.ts +1 -1
  269. package/dist/TreeList/TreeList.d.ts.map +1 -1
  270. package/dist/TreeList/TreeList.js +72 -2
  271. package/dist/TreeList/TreeListItem.d.ts +11 -1
  272. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  273. package/dist/TreeList/TreeListItem.js +51 -12
  274. package/dist/TreeList/treeListItem.markers.stylex.d.ts +16 -0
  275. package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +1 -0
  276. package/dist/TreeList/treeListItem.markers.stylex.js +22 -0
  277. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  278. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  279. package/dist/Typeahead/BaseTypeahead.js +67 -6
  280. package/dist/Typeahead/Typeahead.d.ts +24 -1
  281. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  282. package/dist/Typeahead/Typeahead.js +29 -5
  283. package/dist/VisuallyHidden/VisuallyHidden.d.ts +61 -0
  284. package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  285. package/dist/VisuallyHidden/VisuallyHidden.js +65 -0
  286. package/dist/VisuallyHidden/index.d.ts +11 -0
  287. package/dist/VisuallyHidden/index.d.ts.map +1 -0
  288. package/dist/VisuallyHidden/index.js +13 -0
  289. package/dist/astryx.css +40 -1
  290. package/dist/astryx.umd.js +147 -0
  291. package/dist/astryx.umd.js.map +7 -0
  292. package/dist/hooks/focusableSelector.d.ts +22 -0
  293. package/dist/hooks/focusableSelector.d.ts.map +1 -0
  294. package/dist/hooks/focusableSelector.js +24 -0
  295. package/dist/hooks/index.d.ts +12 -2
  296. package/dist/hooks/index.d.ts.map +1 -1
  297. package/dist/hooks/index.js +7 -2
  298. package/dist/hooks/useAnnounce.d.ts +40 -0
  299. package/dist/hooks/useAnnounce.d.ts.map +1 -0
  300. package/dist/hooks/useAnnounce.js +142 -0
  301. package/dist/hooks/useClickableContainer.d.ts +16 -1
  302. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  303. package/dist/hooks/useClickableContainer.js +16 -4
  304. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  305. package/dist/hooks/useEntryAnimation.js +4 -4
  306. package/dist/hooks/useFocusTrap.d.ts +18 -0
  307. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  308. package/dist/hooks/useFocusTrap.js +87 -3
  309. package/dist/hooks/useGridFocus.d.ts +88 -1
  310. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  311. package/dist/hooks/useGridFocus.js +261 -62
  312. package/dist/hooks/useKeyboardHint.d.ts +82 -0
  313. package/dist/hooks/useKeyboardHint.d.ts.map +1 -0
  314. package/dist/hooks/useKeyboardHint.js +220 -0
  315. package/dist/hooks/useListFocus.d.ts +78 -7
  316. package/dist/hooks/useListFocus.d.ts.map +1 -1
  317. package/dist/hooks/useListFocus.js +305 -52
  318. package/dist/hooks/useLongPress.d.ts +21 -0
  319. package/dist/hooks/useLongPress.d.ts.map +1 -0
  320. package/dist/hooks/useLongPress.js +89 -0
  321. package/dist/hooks/useTreeFocus.d.ts +157 -0
  322. package/dist/hooks/useTreeFocus.d.ts.map +1 -0
  323. package/dist/hooks/useTreeFocus.js +337 -0
  324. package/dist/hooks/useTypeahead.d.ts +63 -0
  325. package/dist/hooks/useTypeahead.d.ts.map +1 -0
  326. package/dist/hooks/useTypeahead.js +117 -0
  327. package/dist/index.d.ts +1 -0
  328. package/dist/index.d.ts.map +1 -1
  329. package/dist/index.js +1 -0
  330. package/dist/theme/generateThemeRules.js +1 -1
  331. package/dist/theme/syntax/SyntaxTheme.d.ts +1 -1
  332. package/dist/theme/syntax/SyntaxTheme.js +1 -1
  333. package/dist/theme/syntax/defineSyntaxTheme.d.ts.map +1 -1
  334. package/dist/theme/syntax/defineSyntaxTheme.js +2 -2
  335. package/dist/theme/syntax/presets.d.ts +1 -1
  336. package/dist/theme/syntax/presets.d.ts.map +1 -1
  337. package/dist/theme/syntax/presets.js +1 -1
  338. package/dist/theme/syntax/tokens.d.ts +1 -1
  339. package/dist/theme/syntax/tokens.js +1 -1
  340. package/dist/theme/types.d.ts +1 -1
  341. package/dist/utils/dateTypes.d.ts +12 -0
  342. package/dist/utils/dateTypes.d.ts.map +1 -1
  343. package/dist/utils/dateTypes.js +41 -1
  344. package/dist/utils/index.d.ts +4 -0
  345. package/dist/utils/index.d.ts.map +1 -1
  346. package/dist/utils/index.js +3 -1
  347. package/dist/utils/inputAria.d.ts +29 -0
  348. package/dist/utils/inputAria.d.ts.map +1 -0
  349. package/dist/utils/inputAria.js +30 -0
  350. package/dist/utils/timeParser.d.ts.map +1 -1
  351. package/dist/utils/timeParser.js +5 -2
  352. package/package.json +15 -5
  353. package/src/Avatar/Avatar.test.tsx +37 -0
  354. package/src/Avatar/Avatar.tsx +10 -5
  355. package/src/AvatarGroup/AvatarGroupOverflow.tsx +3 -0
  356. package/src/Banner/Banner.doc.mjs +8 -0
  357. package/src/Banner/Banner.test.tsx +16 -7
  358. package/src/Banner/Banner.tsx +9 -2
  359. package/src/Breadcrumbs/BreadcrumbItem.tsx +25 -7
  360. package/src/Breadcrumbs/Breadcrumbs.test.tsx +31 -10
  361. package/src/Button/Button.doc.mjs +6 -0
  362. package/src/Button/Button.test.tsx +105 -11
  363. package/src/Button/Button.tsx +76 -21
  364. package/src/Calendar/Calendar.doc.mjs +4 -4
  365. package/src/Calendar/Calendar.test.tsx +168 -30
  366. package/src/Calendar/Calendar.tsx +121 -66
  367. package/src/Calendar/hooks/index.ts +0 -6
  368. package/src/Calendar/index.ts +1 -3
  369. package/src/Calendar/styles.ts +4 -9
  370. package/src/Carousel/Carousel.doc.mjs +7 -1
  371. package/src/Carousel/Carousel.test.tsx +20 -0
  372. package/src/Carousel/Carousel.tsx +16 -1
  373. package/src/Center/Center.doc.mjs +12 -2
  374. package/src/Center/Center.tsx +28 -3
  375. package/src/Chat/ChatComposer.doc.mjs +3 -0
  376. package/src/Chat/ChatComposerDrawer.doc.mjs +12 -0
  377. package/src/Chat/ChatMessageList.doc.mjs +8 -0
  378. package/src/Chat/ChatMessageList.test.tsx +18 -0
  379. package/src/Chat/ChatMessageList.tsx +16 -0
  380. package/src/Chat/useTriggerMenu.tsx +3 -0
  381. package/src/CheckboxInput/CheckboxInput.doc.mjs +18 -0
  382. package/src/CheckboxInput/CheckboxInput.test.tsx +172 -27
  383. package/src/CheckboxInput/CheckboxInput.tsx +78 -6
  384. package/src/CheckboxList/CheckboxList.doc.mjs +9 -0
  385. package/src/CheckboxList/CheckboxList.test.tsx +169 -16
  386. package/src/CheckboxList/CheckboxList.tsx +56 -3
  387. package/src/CheckboxList/CheckboxListContext.tsx +7 -0
  388. package/src/Citation/Citation.doc.mjs +10 -0
  389. package/src/Citation/Citation.test.tsx +117 -0
  390. package/src/Citation/Citation.tsx +9 -1
  391. package/src/ClickableCard/ClickableCard.doc.mjs +15 -0
  392. package/src/CodeBlock/CodeBlock.test.tsx +115 -0
  393. package/src/CodeBlock/CodeBlock.tsx +61 -33
  394. package/src/CommandPalette/CommandPalette.test.tsx +47 -1
  395. package/src/CommandPalette/CommandPalette.tsx +9 -21
  396. package/src/ContextMenu/ContextMenu.doc.mjs +4 -4
  397. package/src/ContextMenu/ContextMenu.test.tsx +155 -1
  398. package/src/ContextMenu/ContextMenu.tsx +113 -16
  399. package/src/DateInput/DateInput.doc.mjs +281 -56
  400. package/src/DateInput/DateInput.test.tsx +188 -2
  401. package/src/DateInput/DateInput.tsx +89 -10
  402. package/src/DateRangeInput/DateRangeInput.doc.mjs +251 -46
  403. package/src/DateRangeInput/DateRangeInput.test.tsx +182 -20
  404. package/src/DateRangeInput/DateRangeInput.tsx +63 -5
  405. package/src/DateTimeInput/DateTimeInput.doc.mjs +368 -67
  406. package/src/DateTimeInput/DateTimeInput.test.tsx +279 -11
  407. package/src/DateTimeInput/DateTimeInput.tsx +125 -17
  408. package/src/Dialog/Dialog.doc.mjs +2 -2
  409. package/src/Dialog/Dialog.tsx +12 -0
  410. package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -8
  411. package/src/DropdownMenu/DropdownMenu.test.tsx +56 -42
  412. package/src/DropdownMenu/DropdownMenu.tsx +52 -18
  413. package/src/EmptyState/EmptyState.test.tsx +4 -2
  414. package/src/EmptyState/EmptyState.tsx +6 -2
  415. package/src/Field/Field.test.tsx +32 -0
  416. package/src/Field/Field.tsx +23 -5
  417. package/src/Field/FieldLabel.tsx +55 -17
  418. package/src/FileInput/FileInput.doc.mjs +17 -0
  419. package/src/FileInput/FileInput.test.tsx +283 -49
  420. package/src/FileInput/FileInput.tsx +76 -4
  421. package/src/Grid/Grid.doc.mjs +14 -4
  422. package/src/Grid/Grid.tsx +20 -0
  423. package/src/HoverCard/HoverCard.doc.mjs +3 -0
  424. package/src/HoverCard/HoverCard.test.tsx +189 -2
  425. package/src/HoverCard/HoverCard.tsx +20 -16
  426. package/src/HoverCard/useHoverCard.tsx +13 -10
  427. package/src/Icon/Icon.doc.mjs +8 -0
  428. package/src/InputGroup/InputGroup.doc.mjs +2 -2
  429. package/src/InputGroup/InputGroup.test.tsx +98 -60
  430. package/src/InputGroup/InputGroup.tsx +21 -4
  431. package/src/InputGroup/InputGroupContext.ts +9 -4
  432. package/src/InputGroup/index.ts +1 -0
  433. package/src/Kbd/Kbd.test.tsx +22 -4
  434. package/src/Kbd/Kbd.tsx +37 -12
  435. package/src/Layer/anchorName.test.ts +146 -0
  436. package/src/Layer/anchorName.ts +46 -0
  437. package/src/Layer/layerAnimations.stylex.ts +20 -4
  438. package/src/Layer/useLayer.doc.mjs +12 -2
  439. package/src/Layer/useLayer.tsx +100 -15
  440. package/src/Layout/Layout.doc.mjs +1 -1
  441. package/src/Layout/Layout.tsx +8 -16
  442. package/src/Layout/LayoutFooter.doc.mjs +4 -4
  443. package/src/Layout/LayoutFooter.tsx +3 -3
  444. package/src/Layout/LayoutHeader.doc.mjs +4 -4
  445. package/src/Layout/LayoutHeader.tsx +3 -3
  446. package/src/Layout/LayoutPanel.tsx +3 -3
  447. package/src/Layout/padding.stylex.ts +52 -0
  448. package/src/Lightbox/Lightbox.doc.mjs +9 -2
  449. package/src/Lightbox/Lightbox.test.tsx +53 -5
  450. package/src/Lightbox/Lightbox.tsx +9 -4
  451. package/src/Link/Link.doc.mjs +14 -0
  452. package/src/Link/Link.test.tsx +52 -2
  453. package/src/Link/Link.tsx +42 -7
  454. package/src/Link/LinkProvider.tsx +1 -0
  455. package/src/List/List.doc.mjs +7 -0
  456. package/src/Markdown/Markdown.doc.mjs +2 -0
  457. package/src/Markdown/Markdown.test.tsx +28 -26
  458. package/src/Markdown/Markdown.tsx +21 -6
  459. package/src/MetadataList/MetadataListItem.doc.mjs +4 -0
  460. package/src/MobileNav/MobileNav.doc.mjs +8 -8
  461. package/src/MobileNav/MobileNav.tsx +13 -0
  462. package/src/MobileNav/MobileNavReopen.test.tsx +118 -0
  463. package/src/MoreMenu/MoreMenu.doc.mjs +2 -17
  464. package/src/MoreMenu/MoreMenu.tsx +0 -9
  465. package/src/MultiSelector/MultiSelector.doc.mjs +25 -0
  466. package/src/MultiSelector/MultiSelector.test.tsx +375 -8
  467. package/src/MultiSelector/MultiSelector.tsx +145 -21
  468. package/src/MultiSelector/hooks.ts +27 -0
  469. package/src/NavMenu/NavHeadingMenu.test.tsx +77 -1
  470. package/src/NavMenu/NavHeadingMenu.tsx +50 -3
  471. package/src/NumberInput/NumberInput.doc.mjs +17 -0
  472. package/src/NumberInput/NumberInput.test.tsx +242 -40
  473. package/src/NumberInput/NumberInput.tsx +89 -11
  474. package/src/OverflowList/OverflowList.doc.mjs +17 -0
  475. package/src/Pagination/Pagination.doc.mjs +3 -3
  476. package/src/Pagination/Pagination.test.tsx +250 -14
  477. package/src/Pagination/Pagination.tsx +54 -29
  478. package/src/Popover/usePopover.doc.mjs +7 -3
  479. package/src/Popover/usePopover.tsx +49 -6
  480. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  481. package/src/PowerSearch/PowerSearch.test.tsx +97 -1
  482. package/src/PowerSearch/PowerSearch.tsx +16 -3
  483. package/src/ProgressBar/ProgressBar.test.tsx +45 -27
  484. package/src/ProgressBar/ProgressBar.tsx +3 -4
  485. package/src/RadioList/RadioList.doc.mjs +9 -0
  486. package/src/RadioList/RadioList.test.tsx +252 -16
  487. package/src/RadioList/RadioList.tsx +166 -10
  488. package/src/RadioList/RadioListItem.tsx +14 -2
  489. package/src/Resizable/Resizable.doc.mjs +1 -1
  490. package/src/Section/Section.doc.mjs +11 -0
  491. package/src/SegmentedControl/SegmentedControl.doc.mjs +9 -0
  492. package/src/SegmentedControl/SegmentedControl.test.tsx +214 -42
  493. package/src/SegmentedControl/SegmentedControl.tsx +102 -54
  494. package/src/SegmentedControl/SegmentedControlContext.ts +7 -0
  495. package/src/SegmentedControl/SegmentedControlItem.tsx +14 -1
  496. package/src/SelectableCard/SelectableCard.doc.mjs +15 -0
  497. package/src/Selector/Selector.doc.mjs +25 -0
  498. package/src/Selector/Selector.test.tsx +322 -15
  499. package/src/Selector/Selector.tsx +126 -40
  500. package/src/Selector/hooks.ts +23 -0
  501. package/src/SideNav/SideNav.test.tsx +16 -0
  502. package/src/SideNav/SideNavItem.tsx +2 -4
  503. package/src/Skeleton/Skeleton.doc.mjs +11 -1
  504. package/src/Skeleton/Skeleton.test.tsx +25 -0
  505. package/src/Skeleton/Skeleton.tsx +11 -1
  506. package/src/Slider/Slider.doc.mjs +19 -0
  507. package/src/Slider/Slider.test.tsx +158 -18
  508. package/src/Slider/Slider.tsx +54 -6
  509. package/src/Spinner/Spinner.tsx +7 -1
  510. package/src/Stack/Stack.doc.mjs +77 -0
  511. package/src/Stack/Stack.test.tsx +55 -5
  512. package/src/Stack/Stack.tsx +73 -24
  513. package/src/Stack/StackItem.test.tsx +23 -0
  514. package/src/Stack/StackItem.tsx +20 -23
  515. package/src/Switch/Switch.doc.mjs +15 -0
  516. package/src/Switch/Switch.test.tsx +162 -27
  517. package/src/Switch/Switch.tsx +61 -21
  518. package/src/TabList/Tab.tsx +6 -0
  519. package/src/TabList/TabList.doc.mjs +12 -0
  520. package/src/TabList/TabList.test.tsx +208 -0
  521. package/src/TabList/TabList.tsx +102 -12
  522. package/src/TabList/TabListContext.ts +7 -3
  523. package/src/TabList/TabMenu.tsx +6 -3
  524. package/src/Table/BaseTable.tsx +72 -41
  525. package/src/Table/Table.doc.mjs +14 -0
  526. package/src/Table/Table.test.tsx +39 -30
  527. package/src/Table/Table.tsx +27 -1
  528. package/src/Table/TableCell.tsx +25 -23
  529. package/src/Table/TableHeaderCell.tsx +28 -29
  530. package/src/Table/TableRow.tsx +37 -7
  531. package/src/Table/index.ts +6 -2
  532. package/src/Table/plugins/pagination/paginateData.ts +10 -3
  533. package/src/Table/plugins/pagination/useTablePagination.test.tsx +16 -0
  534. package/src/Table/plugins/pagination/useTablePagination.tsx +8 -1
  535. package/src/Table/plugins/sortable/useTableSortable.test.tsx +91 -2
  536. package/src/Table/plugins/sortable/useTableSortable.tsx +53 -1
  537. package/src/Table/plugins/stickyColumns/index.ts +4 -0
  538. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +163 -0
  539. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +407 -0
  540. package/src/Table/tableContextMenu.test.tsx +157 -0
  541. package/src/Table/tableContextMenu.tsx +142 -0
  542. package/src/Table/types.ts +175 -4
  543. package/src/Table/useBaseTablePlugins.ts +1 -0
  544. package/src/Table/useTableColumnResize.doc.mjs +96 -0
  545. package/src/Table/useTableStickyColumns.doc.mjs +59 -0
  546. package/src/Text/Heading.doc.mjs +15 -2
  547. package/src/Text/Text.doc.mjs +7 -1
  548. package/src/Text/text.stylex.ts +2 -2
  549. package/src/TextArea/TextArea.doc.mjs +17 -0
  550. package/src/TextArea/TextArea.test.tsx +167 -35
  551. package/src/TextArea/TextArea.tsx +62 -15
  552. package/src/TextInput/TextInput.doc.mjs +17 -0
  553. package/src/TextInput/TextInput.test.tsx +168 -34
  554. package/src/TextInput/TextInput.tsx +77 -9
  555. package/src/TimeInput/TimeInput.doc.mjs +335 -60
  556. package/src/TimeInput/TimeInput.test.tsx +144 -7
  557. package/src/TimeInput/TimeInput.tsx +74 -5
  558. package/src/Timestamp/Timestamp.doc.mjs +9 -0
  559. package/src/Timestamp/Timestamp.test.tsx +44 -18
  560. package/src/Timestamp/Timestamp.tsx +30 -10
  561. package/src/Toast/Toast.tsx +14 -0
  562. package/src/Toast/ToastViewport.test.tsx +204 -0
  563. package/src/Toast/ToastViewport.tsx +167 -21
  564. package/src/ToggleButton/ToggleButton.doc.mjs +9 -3
  565. package/src/ToggleButton/ToggleButton.test.tsx +39 -18
  566. package/src/ToggleButton/ToggleButton.tsx +46 -83
  567. package/src/Tokenizer/Tokenizer.doc.mjs +15 -0
  568. package/src/Tokenizer/Tokenizer.test.tsx +105 -1
  569. package/src/Tokenizer/Tokenizer.tsx +41 -2
  570. package/src/Toolbar/Toolbar.doc.mjs +65 -5
  571. package/src/Toolbar/Toolbar.test.tsx +109 -6
  572. package/src/Toolbar/Toolbar.tsx +59 -5
  573. package/src/Tooltip/Tooltip.test.tsx +81 -0
  574. package/src/Tooltip/useTooltip.tsx +57 -11
  575. package/src/TreeList/TreeList.test.tsx +295 -6
  576. package/src/TreeList/TreeList.tsx +73 -1
  577. package/src/TreeList/TreeListItem.tsx +51 -5
  578. package/src/TreeList/treeListItem.markers.stylex.ts +20 -0
  579. package/src/Typeahead/BaseTypeahead.tsx +86 -9
  580. package/src/Typeahead/Typeahead.doc.mjs +136 -30
  581. package/src/Typeahead/Typeahead.test.tsx +331 -1
  582. package/src/Typeahead/Typeahead.tsx +52 -1
  583. package/src/VisuallyHidden/VisuallyHidden.doc.mjs +80 -0
  584. package/src/VisuallyHidden/VisuallyHidden.test.tsx +76 -0
  585. package/src/VisuallyHidden/VisuallyHidden.tsx +113 -0
  586. package/src/VisuallyHidden/index.ts +15 -0
  587. package/src/hooks/focusableSelector.ts +25 -0
  588. package/src/hooks/index.ts +28 -2
  589. package/src/hooks/useAnnounce.test.tsx +101 -0
  590. package/src/hooks/useAnnounce.ts +168 -0
  591. package/src/hooks/useClickableContainer.ts +16 -4
  592. package/src/hooks/useEntryAnimation.ts +16 -4
  593. package/src/hooks/useFocusTrap.test.tsx +132 -0
  594. package/src/hooks/useFocusTrap.ts +98 -4
  595. package/src/hooks/useGridFocus.doc.mjs +40 -2
  596. package/src/hooks/useGridFocus.test.tsx +132 -0
  597. package/src/hooks/useGridFocus.ts +343 -67
  598. package/src/hooks/useImageMode.test.ts +133 -0
  599. package/src/hooks/useKeyboardHint.doc.mjs +101 -0
  600. package/src/hooks/useKeyboardHint.test.tsx +70 -0
  601. package/src/hooks/useKeyboardHint.tsx +332 -0
  602. package/src/hooks/useListFocus.doc.mjs +50 -10
  603. package/src/hooks/useListFocus.test.tsx +375 -0
  604. package/src/hooks/useListFocus.ts +395 -57
  605. package/src/hooks/useLongPress.test.tsx +134 -0
  606. package/src/hooks/useLongPress.ts +119 -0
  607. package/src/hooks/useTreeFocus.doc.mjs +140 -0
  608. package/src/hooks/useTreeFocus.test.tsx +214 -0
  609. package/src/hooks/useTreeFocus.ts +559 -0
  610. package/src/hooks/useTypeahead.test.tsx +109 -0
  611. package/src/hooks/useTypeahead.ts +183 -0
  612. package/src/index.ts +1 -0
  613. package/src/theme/derivedVarRegistry.test.ts +4 -0
  614. package/src/theme/generateThemeRules.test.ts +7 -8
  615. package/src/theme/generateThemeRules.ts +1 -1
  616. package/src/theme/syntax/SyntaxTheme.tsx +1 -1
  617. package/src/theme/syntax/defineSyntaxTheme.ts +20 -11
  618. package/src/theme/syntax/presets.ts +5 -2
  619. package/src/theme/syntax/tokens.ts +1 -1
  620. package/src/theme/types.ts +1 -1
  621. package/src/utils/dateTypes.test.ts +40 -0
  622. package/src/utils/dateTypes.ts +40 -0
  623. package/src/utils/index.ts +9 -0
  624. package/src/utils/inputAria.test.ts +39 -0
  625. package/src/utils/inputAria.ts +58 -0
  626. package/src/utils/timeParser.test.ts +10 -0
  627. package/src/utils/timeParser.ts +5 -2
  628. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts +0 -57
  629. package/dist/Calendar/hooks/useCalendarRovingTabindex.d.ts.map +0 -1
  630. package/dist/Calendar/hooks/useCalendarRovingTabindex.js +0 -96
  631. 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
  });
@@ -38,6 +38,7 @@ 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';
42
43
  import {spacingVars, sizeVars} from '../theme/tokens.stylex';
43
44
  import {mergeProps} from '../utils';
@@ -101,6 +102,29 @@ export interface TypeaheadProps<T extends SearchableItem> extends Omit<
101
102
  emptySearchResultsText?: string;
102
103
  /** Whether the input is disabled. @default false */
103
104
  isDisabled?: boolean;
105
+ /**
106
+ * Explains why the input is disabled. When set together with `isDisabled`,
107
+ * the input shows a tooltip with this text on hover and keyboard focus, and
108
+ * the field stays focusable (via `aria-disabled`) so the reason is
109
+ * discoverable by keyboard and assistive technology. Editing and selection
110
+ * stay blocked.
111
+ *
112
+ * Use this instead of wrapping a disabled input in `Tooltip` — disabled
113
+ * controls don't emit the pointer events an external tooltip needs.
114
+ *
115
+ * @example
116
+ * ```
117
+ * <Typeahead
118
+ * label="Assignee"
119
+ * searchSource={userSource}
120
+ * value={assignee}
121
+ * onChange={setAssignee}
122
+ * isDisabled
123
+ * disabledMessage="You need the Editor role to change this"
124
+ * />
125
+ * ```
126
+ */
127
+ disabledMessage?: string;
104
128
  /** Show clear button. @default true */
105
129
  hasClear?: boolean;
106
130
  /** Auto-focus on mount. @default false */
@@ -209,6 +233,7 @@ export function Typeahead<T extends SearchableItem>({
209
233
  maxMenuItems,
210
234
  emptySearchResultsText,
211
235
  isDisabled = false,
236
+ disabledMessage,
212
237
  hasClear = true,
213
238
  hasAutoFocus,
214
239
  size: sizeProp,
@@ -230,6 +255,21 @@ export function Typeahead<T extends SearchableItem>({
230
255
  const inputRef = useRef<HTMLInputElement>(null);
231
256
  const tokenRef = useRef<HTMLElement>(null);
232
257
 
258
+ // Disabled-reason tooltip. Disabled controls swallow pointer events, so the
259
+ // tooltip listeners attach to the input wrapper (which already exists) and
260
+ // the input stays perceivable via aria-disabled + readOnly instead of the
261
+ // disabled attribute. Editing and selection stay blocked by the isDisabled
262
+ // guards (handleWrapperClick, handleEnterEditMode, and BaseTypeahead's own
263
+ // focus/change guards).
264
+ const showsDisabledMessage = isDisabled && !!disabledMessage;
265
+ const disabledMessageTooltip = useTooltip({
266
+ placement: 'above',
267
+ // The wrapper div is not naturally focusable; focusin bubbles up from the
268
+ // input, so always attach focus listeners.
269
+ focusTrigger: 'always',
270
+ isEnabled: showsDisabledMessage,
271
+ });
272
+
233
273
  // Edit mode: when the user clicks the token to edit the selected value
234
274
  const [isEditing, setIsEditing] = useState(false);
235
275
  const [editingValue, setEditingValue] = useState<T | null>(null);
@@ -339,6 +379,7 @@ export function Typeahead<T extends SearchableItem>({
339
379
  [
340
380
  description ? descriptionId : null,
341
381
  status?.message ? statusMessageId : null,
382
+ showsDisabledMessage ? disabledMessageTooltip.describedBy : null,
342
383
  ]
343
384
  .filter(Boolean)
344
385
  .join(' ') || undefined;
@@ -371,7 +412,13 @@ export function Typeahead<T extends SearchableItem>({
371
412
  className={className}
372
413
  style={style}>
373
414
  <div
374
- ref={wrapperRef}
415
+ ref={el => {
416
+ wrapperRef.current = el;
417
+ // Anchor + hover/focus listeners for the disabled-message tooltip.
418
+ // Handlers are gated internally by isEnabled, so attaching
419
+ // unconditionally is safe.
420
+ disabledMessageTooltip.ref(el);
421
+ }}
375
422
  data-testid={testId}
376
423
  onClick={handleWrapperClick}
377
424
  onBlur={handleBlur}
@@ -411,6 +458,7 @@ export function Typeahead<T extends SearchableItem>({
411
458
  emptySearchResultsText={emptySearchResultsText}
412
459
  isDisabled={isDisabled}
413
460
  hasAutoFocus={hasAutoFocus}
461
+ isFocusableDisabled={showsDisabledMessage}
414
462
  inputId={inputId}
415
463
  ariaDescribedBy={ariaDescribedBy}
416
464
  onChangeQuery={onChangeQuery}
@@ -432,6 +480,9 @@ export function Typeahead<T extends SearchableItem>({
432
480
  />
433
481
  )}
434
482
  </div>
483
+
484
+ {showsDisabledMessage &&
485
+ disabledMessageTooltip.renderTooltip(disabledMessage)}
435
486
  </Field>
436
487
  );
437
488
  }
@@ -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
+ };
@@ -0,0 +1,76 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file VisuallyHidden.test.tsx
5
+ * @input Uses vitest, @testing-library/react, VisuallyHidden component
6
+ * @output Unit tests for VisuallyHidden rendering + a11y behavior
7
+ * @position Testing; validates VisuallyHidden.tsx implementation
8
+ *
9
+ * SYNC: When VisuallyHidden.tsx changes, update tests to match new behavior
10
+ */
11
+
12
+ import {describe, it, expect} from 'vitest';
13
+ import {render, screen} from '@testing-library/react';
14
+ import {createRef} from 'react';
15
+ import {VisuallyHidden} from './VisuallyHidden';
16
+
17
+ describe('VisuallyHidden', () => {
18
+ it('renders its children in the accessibility tree', () => {
19
+ render(<VisuallyHidden>Delete incident</VisuallyHidden>);
20
+ // Present in the a11y tree (getByText finds it — it is not display:none).
21
+ expect(screen.getByText('Delete incident')).toBeInTheDocument();
22
+ });
23
+
24
+ it('renders a <span> by default', () => {
25
+ render(<VisuallyHidden>Label</VisuallyHidden>);
26
+ expect(screen.getByText('Label').tagName).toBe('SPAN');
27
+ });
28
+
29
+ it('renders a custom element via the `as` prop', () => {
30
+ render(
31
+ <VisuallyHidden as="div" aria-live="polite">
32
+ Moved task to Done
33
+ </VisuallyHidden>,
34
+ );
35
+ const el = screen.getByText('Moved task to Done');
36
+ expect(el.tagName).toBe('DIV');
37
+ expect(el).toHaveAttribute('aria-live', 'polite');
38
+ });
39
+
40
+ it('applies the clip styles that hide it visually', () => {
41
+ render(<VisuallyHidden>Hidden</VisuallyHidden>);
42
+ const el = screen.getByText('Hidden');
43
+ // jsdom does not apply the stylesheet, but the StyleX class is attached.
44
+ expect(el.getAttribute('class')).toBeTruthy();
45
+ // It must NOT be display:none / hidden — it stays in the a11y tree.
46
+ expect(el).not.toHaveAttribute('hidden');
47
+ });
48
+
49
+ it('forwards a ref to the rendered element', () => {
50
+ const ref = createRef<HTMLElement>();
51
+ render(<VisuallyHidden ref={ref}>Ref target</VisuallyHidden>);
52
+ expect(ref.current).toBeInstanceOf(HTMLElement);
53
+ expect(ref.current?.textContent).toBe('Ref target');
54
+ });
55
+
56
+ it('passes through arbitrary BaseProps (id, role, data-*)', () => {
57
+ render(
58
+ <VisuallyHidden id="announcer" role="status" data-testid="vh">
59
+ Status
60
+ </VisuallyHidden>,
61
+ );
62
+ const el = screen.getByTestId('vh');
63
+ expect(el).toHaveAttribute('id', 'announcer');
64
+ expect(el).toHaveAttribute('role', 'status');
65
+ });
66
+
67
+ it('gives an icon-only control an accessible name', () => {
68
+ render(
69
+ <button type="button">
70
+ <span aria-hidden="true">🗑️</span>
71
+ <VisuallyHidden>Delete</VisuallyHidden>
72
+ </button>,
73
+ );
74
+ expect(screen.getByRole('button', {name: 'Delete'})).toBeInTheDocument();
75
+ });
76
+ });