@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
@@ -61,14 +61,7 @@ export const docs = {
61
61
  type: 'boolean',
62
62
  description: 'Whether to show a chevron icon on the trigger button. Set to false for icon-only triggers.',
63
63
  default: 'true',
64
- },
65
- {
66
- name: 'hasAutoFocus',
67
- type: 'boolean',
68
- description: 'Whether to auto-focus the first menu item when the menu opens. Set to false for inline showcases or documentation previews.',
69
- default: 'true',
70
- },
71
- {
64
+ }, {
72
65
  name: 'children',
73
66
  type: '(item: DropdownMenuItemData) => ReactNode',
74
67
  description: 'Custom render function for each item in the list.',
@@ -45,30 +45,43 @@ beforeEach(() => {
45
45
  describe('DropdownMenu', () => {
46
46
  it('renders trigger button with label', () => {
47
47
  render(
48
- <DropdownMenu
49
- button={{label: 'Actions'}}
50
- items={[{label: 'Item 1'}]}
51
- />,
48
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
52
49
  );
53
50
  expect(screen.getByRole('button', {name: /Actions/})).toBeInTheDocument();
54
51
  });
55
52
 
56
53
  it('renders menu with role="menu"', () => {
57
54
  render(
58
- <DropdownMenu
59
- button={{label: 'Actions'}}
60
- items={[{label: 'Item 1'}]}
61
- />,
55
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
62
56
  );
63
57
  expect(screen.getByRole('menu', {hidden: true})).toBeInTheDocument();
64
58
  });
65
59
 
60
+ it('names the menu from the trigger label (menus-13)', () => {
61
+ render(
62
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
63
+ );
64
+ expect(
65
+ screen.getByRole('menu', {name: 'Actions', hidden: true}),
66
+ ).toBeInTheDocument();
67
+ });
68
+
69
+ it('does not wrap the menu in a role="dialog" aria-modal element', () => {
70
+ render(
71
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
72
+ );
73
+ // The popup exposes its own role="menu"; it must not be nested inside a
74
+ // modal dialog, which would announce an unnamed dialog around the menu
75
+ // while focus stays on the trigger.
76
+ expect(screen.queryByRole('dialog', {hidden: true})).not.toBeInTheDocument();
77
+ expect(
78
+ document.querySelector('[aria-modal="true"]'),
79
+ ).not.toBeInTheDocument();
80
+ });
81
+
66
82
  it('defaults menu placement below', () => {
67
83
  render(
68
- <DropdownMenu
69
- button={{label: 'Actions'}}
70
- items={[{label: 'Item 1'}]}
71
- />,
84
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
72
85
  );
73
86
  const popover = screen
74
87
  .getByRole('menu', {hidden: true})
@@ -96,10 +109,7 @@ describe('DropdownMenu', () => {
96
109
 
97
110
  it('has aria-haspopup and aria-expanded attributes', () => {
98
111
  render(
99
- <DropdownMenu
100
- button={{label: 'Actions'}}
101
- items={[{label: 'Item 1'}]}
102
- />,
112
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
103
113
  );
104
114
  const button = screen.getByRole('button', {name: /Actions/});
105
115
  expect(button).toHaveAttribute('aria-haspopup', 'menu');
@@ -107,16 +117,43 @@ describe('DropdownMenu', () => {
107
117
  });
108
118
 
109
119
  it('opens menu when button is clicked', async () => {
120
+ const user = userEvent.setup();
121
+ render(
122
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
123
+ );
124
+
125
+ await user.click(screen.getByRole('button', {name: /Actions/}));
126
+ expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
127
+ });
128
+
129
+ it('closes the menu when Tab is pressed inside it (APG menu-button)', async () => {
130
+ const user = userEvent.setup();
131
+ render(
132
+ <DropdownMenu button={{label: 'Actions'}} items={[{label: 'Item 1'}]} />,
133
+ );
134
+
135
+ await user.click(screen.getByRole('button', {name: /Actions/}));
136
+ expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
137
+
138
+ const menu = screen.getByRole('menu', {hidden: true});
139
+ fireEvent.keyDown(menu, {key: 'Tab'});
140
+ expect(HTMLElement.prototype.hidePopover).toHaveBeenCalled();
141
+ });
142
+
143
+ it('typeahead focuses the item matching the typed character (menus-11)', async () => {
110
144
  const user = userEvent.setup();
111
145
  render(
112
146
  <DropdownMenu
113
147
  button={{label: 'Actions'}}
114
- items={[{label: 'Item 1'}]}
148
+ items={[{label: 'Cut'}, {label: 'Copy'}, {label: 'Delete'}]}
115
149
  />,
116
150
  );
117
-
118
151
  await user.click(screen.getByRole('button', {name: /Actions/}));
119
- expect(HTMLElement.prototype.showPopover).toHaveBeenCalled();
152
+ const menu = screen.getByRole('menu', {hidden: true});
153
+ fireEvent.keyDown(menu, {key: 'd'});
154
+ expect(
155
+ screen.getByRole('menuitem', {name: 'Delete', hidden: true}),
156
+ ).toHaveFocus();
120
157
  });
121
158
 
122
159
  it('calls onClick callback when button is clicked', async () => {
@@ -212,29 +249,6 @@ describe('DropdownMenu controlled mode', () => {
212
249
  });
213
250
  });
214
251
 
215
- describe('DropdownMenu hasAutoFocus', () => {
216
- it('does not focus menu items when hasAutoFocus is false and isMenuOpen is true', () => {
217
- const focusSpy = vi.spyOn(HTMLElement.prototype, 'focus');
218
- render(
219
- <DropdownMenu
220
- button={{label: 'Actions'}}
221
- items={[{label: 'Edit'}, {label: 'Delete'}]}
222
- isMenuOpen={true}
223
- hasAutoFocus={false}
224
- onOpenChange={() => {}}
225
- />,
226
- );
227
-
228
- const menuItems = screen.getAllByRole('menuitem', {hidden: true});
229
- const menuItemFocusCalls = focusSpy.mock.calls.filter((_, i) => {
230
- const ctx = focusSpy.mock.contexts[i];
231
- return menuItems.includes(ctx as HTMLElement);
232
- });
233
- expect(menuItemFocusCalls).toHaveLength(0);
234
- focusSpy.mockRestore();
235
- });
236
- });
237
-
238
252
  describe('DropdownMenu items', () => {
239
253
  it('renders items with labels', () => {
240
254
  render(
@@ -43,6 +43,7 @@ import {
43
43
  type DropdownMenuContextValue,
44
44
  } from './DropdownMenuContext';
45
45
  import {useListFocus} from '../hooks/useListFocus';
46
+ import {useTypeahead} from '../hooks/useTypeahead';
46
47
  import {layerAnimations} from '../Layer/layerAnimations.stylex';
47
48
  import type {LayerPlacement} from '../Layer/useLayer';
48
49
  import {
@@ -134,13 +135,6 @@ interface DropdownMenuBaseProps extends BaseProps {
134
135
  */
135
136
  placement?: LayerPlacement;
136
137
 
137
- /**
138
- * Whether to auto-focus the first menu item when the menu opens.
139
- * Set to `false` for inline showcases or documentation previews
140
- * where stealing focus is undesirable.
141
- * @default true
142
- */
143
- hasAutoFocus?: boolean;
144
138
  'data-testid'?: string;
145
139
  }
146
140
 
@@ -192,7 +186,6 @@ export function DropdownMenu({
192
186
  onClick,
193
187
  hasChevron = true,
194
188
  placement = 'below',
195
- hasAutoFocus = true,
196
189
  className,
197
190
  style,
198
191
  xstyle,
@@ -249,6 +242,9 @@ export function DropdownMenu({
249
242
  hasLightDismiss: true,
250
243
  hasCloseButton: false,
251
244
  hasAutoFocus: false,
245
+ // The popup's own role="menu" is the exposed semantics; wrapping it in a
246
+ // modal dialog would announce an unnamed dialog around the menu.
247
+ role: 'none',
252
248
  });
253
249
 
254
250
  const closeMenu = useCallback(() => {
@@ -260,27 +256,49 @@ export function DropdownMenu({
260
256
  listRef,
261
257
  handleKeyDown: listNavKeyDown,
262
258
  focusFirst,
259
+ focusItem,
263
260
  } = useListFocus<HTMLDivElement>({
264
261
  itemSelector: '[role="menuitem"]:not([aria-disabled="true"])',
265
262
  wrap: false,
266
263
  onEscape: closeMenu,
267
264
  });
268
265
 
266
+ // First-character typeahead over the (enabled) menu items — jump to the next
267
+ // item whose label starts with the typed text (menus-11).
268
+ const getMenuItems = useCallback(
269
+ (): HTMLElement[] =>
270
+ listRef.current
271
+ ? Array.from(
272
+ listRef.current.querySelectorAll<HTMLElement>(
273
+ '[role="menuitem"]:not([aria-disabled="true"])',
274
+ ),
275
+ )
276
+ : [],
277
+ [listRef],
278
+ );
279
+ const typeahead = useTypeahead({
280
+ getItemLabels: () => getMenuItems().map(el => el.textContent),
281
+ onMatch: focusItem,
282
+ getCurrentIndex: () =>
283
+ getMenuItems().findIndex(
284
+ el =>
285
+ el === document.activeElement || el.contains(document.activeElement),
286
+ ),
287
+ });
288
+
269
289
  // Sync controlled open state → popover, and focus first item on open
270
290
  useEffect(() => {
271
291
  if (isControlled) {
272
292
  if (controlledIsOpen && !popover.isOpen) {
273
293
  popover.show();
274
- if (hasAutoFocus) {
275
- requestAnimationFrame(() => focusFirst());
276
- }
294
+ requestAnimationFrame(() => focusFirst());
277
295
  } else if (!controlledIsOpen && popover.isOpen) {
278
296
  popover.hide();
279
297
  }
280
298
  }
281
- }, [controlledIsOpen, isControlled, popover, hasAutoFocus, focusFirst]);
299
+ }, [controlledIsOpen, isControlled, popover, focusFirst]);
282
300
 
283
- // Extend useListFocus with Enter/Space activation
301
+ // Extend useListFocus with Enter/Space activation + typeahead
284
302
  const listKeyDown = useCallback(
285
303
  (e: React.KeyboardEvent) => {
286
304
  if (e.key === 'Enter' || e.key === ' ') {
@@ -291,17 +309,29 @@ export function DropdownMenu({
291
309
  }
292
310
  return;
293
311
  }
312
+ // APG menu-button pattern: Tab closes the menu. Menu items are
313
+ // tabIndex={-1} so the focus trap has nothing trappable and Tab would
314
+ // otherwise leak into the page while the menu stayed open (menus-5).
315
+ // Do NOT preventDefault — closing restores focus to the trigger, and the
316
+ // browser's default Tab then continues from there to the next element.
317
+ if (e.key === 'Tab') {
318
+ closeMenu();
319
+ return;
320
+ }
321
+ // Type-to-focus next; if it consumed a printable key, stop here.
322
+ if (typeahead.onKeyDown(e)) {
323
+ e.preventDefault();
324
+ return;
325
+ }
294
326
  listNavKeyDown(e);
295
327
  },
296
- [listNavKeyDown],
328
+ [listNavKeyDown, closeMenu, typeahead],
297
329
  );
298
330
 
299
331
  const openAndFocus = useCallback(() => {
300
332
  popover.show();
301
- if (hasAutoFocus) {
302
- requestAnimationFrame(() => focusFirst());
303
- }
304
- }, [popover, hasAutoFocus, focusFirst]);
333
+ requestAnimationFrame(() => focusFirst());
334
+ }, [popover, focusFirst]);
305
335
 
306
336
  const handleButtonClick = useCallback(() => {
307
337
  // If the menu was just closed by light dismiss (e.g. iOS Safari fires
@@ -399,6 +429,10 @@ export function DropdownMenu({
399
429
  ref={listRef}
400
430
  id={menuId}
401
431
  role="menu"
432
+ // Give the menu an accessible name from its trigger's label, so
433
+ // screen readers announce e.g. "Actions menu" rather than an unnamed
434
+ // menu (menus-13).
435
+ aria-label={button.label}
402
436
  onKeyDown={listKeyDown}
403
437
  {...mergeProps(
404
438
  themeProps('dropdown-menu'),
@@ -45,11 +45,13 @@ describe('EmptyState', () => {
45
45
  />,
46
46
  );
47
47
  expect(screen.getByText('Try adjusting your search.')).toBeInTheDocument();
48
+ // Description renders as <div> (never <p>) so block content composes safely.
49
+ expect(screen.getByText('Try adjusting your search.').tagName).toBe('DIV');
48
50
  });
49
51
 
50
52
  it('does not render description when not provided', () => {
51
- const {container} = render(<EmptyState title="No results" />);
52
- expect(container.querySelector('p')).not.toBeInTheDocument();
53
+ render(<EmptyState title="No results" />);
54
+ expect(screen.queryByText('Try adjusting your search.')).toBeNull();
53
55
  });
54
56
 
55
57
  it('renders with icon', () => {
@@ -174,13 +174,17 @@ export function EmptyState({
174
174
  title,
175
175
  )}
176
176
  {description != null && (
177
- <p
177
+ // Rendered as <div> (not <p>): description accepts ReactNode and a
178
+ // <p> cannot legally contain block children, which causes hydration
179
+ // mismatches. The StyleX style sets margin: 0, so appearance is
180
+ // unchanged.
181
+ <div
178
182
  {...stylex.props(
179
183
  styles.description,
180
184
  isCompact && styles.descriptionCompact,
181
185
  )}>
182
186
  {description}
183
- </p>
187
+ </div>
184
188
  )}
185
189
  </div>
186
190
  {actions != null && (
@@ -93,6 +93,38 @@ describe('Field', () => {
93
93
  expect(label).toBeVisible();
94
94
  });
95
95
 
96
+ it('renders a single-control label as a <label> with htmlFor', () => {
97
+ render(
98
+ <Field label="Email" inputID="email-input">
99
+ <input id="email-input" />
100
+ </Field>,
101
+ );
102
+ const labelEl = screen.getByText('Email');
103
+ expect(labelEl.tagName).toBe('LABEL');
104
+ expect(labelEl).toHaveAttribute('for', 'email-input');
105
+ });
106
+
107
+ it('renders a group label as a <span> (not <label>) with no htmlFor', () => {
108
+ render(
109
+ <Field
110
+ label="Plan"
111
+ inputID="plan-group"
112
+ labelID="plan-label"
113
+ isGroupLabel>
114
+ <div role="radiogroup" aria-labelledby="plan-label" />
115
+ </Field>,
116
+ );
117
+ const labelEl = screen.getByText('Plan');
118
+ // A group's accessible-name element must not be a literal <label>.
119
+ expect(labelEl.tagName).toBe('SPAN');
120
+ expect(labelEl.closest('label')).toBeNull();
121
+ expect(labelEl).not.toHaveAttribute('for');
122
+ // labelID is applied to the label element and referenced by the group.
123
+ expect(labelEl).toHaveAttribute('id', 'plan-label');
124
+ const group = screen.getByRole('radiogroup', {name: 'Plan'});
125
+ expect(group.getAttribute('aria-labelledby')).toBe(labelEl.id);
126
+ });
127
+
96
128
  it('forwards ref correctly', () => {
97
129
  const ref = vi.fn();
98
130
  render(
@@ -99,9 +99,26 @@ export interface FieldProps extends Omit<
99
99
  */
100
100
  description?: string;
101
101
  /**
102
- * ID for the input element (used for label's htmlFor attribute).
102
+ * ID of the input element this label points AT (used as the label's
103
+ * `htmlFor`). This is the id of the *control*, not of the label element —
104
+ * see `labelID` for the latter.
103
105
  */
104
106
  inputID: string;
107
+ /**
108
+ * The `id` applied TO the label element itself (distinct from `inputID`,
109
+ * which is the control the label points at). A grouping control
110
+ * (radiogroup, checkbox group) references this via `aria-labelledby` to take
111
+ * the label as its accessible name. Pair with `isGroupLabel`.
112
+ */
113
+ labelID?: string;
114
+ /**
115
+ * When the field wraps a group of controls rather than a single input, set
116
+ * this so the label renders as a non-`<label>` element (a `<span>`): a
117
+ * `<label>` semantically names one control and can't be associated with a
118
+ * group. Pair with `labelID` + `aria-labelledby` on the group.
119
+ * @default false
120
+ */
121
+ isGroupLabel?: boolean;
105
122
  /**
106
123
  * ID for the description element (use for aria-describedby on the input).
107
124
  */
@@ -172,6 +189,8 @@ export function Field({
172
189
  isLabelHidden = false,
173
190
  description,
174
191
  inputID,
192
+ labelID,
193
+ isGroupLabel = false,
175
194
  descriptionID,
176
195
  isOptional = false,
177
196
  isRequired = false,
@@ -206,6 +225,8 @@ export function Field({
206
225
  <FieldLabel
207
226
  label={label}
208
227
  inputID={inputID}
228
+ labelID={labelID}
229
+ isGroupLabel={isGroupLabel}
209
230
  isLabelHidden={isLabelHidden}
210
231
  isDisabled={isDisabled}
211
232
  isOptional={isOptional}
@@ -245,10 +266,7 @@ export function Field({
245
266
  <div {...stylex.props(styles.horizontalLabelAlign)}>{labelNode}</div>
246
267
  <div {...stylex.props(styles.inputStatusWrapper)}>
247
268
  {description && (
248
- <Text
249
- type="supporting"
250
- display="block"
251
- id={resolvedDescriptionID}>
269
+ <Text type="supporting" display="block" id={resolvedDescriptionID}>
252
270
  {description}
253
271
  </Text>
254
272
  )}
@@ -35,6 +35,7 @@ const styles = stylex.create({
35
35
  gap: spacingVars['--spacing-1'],
36
36
  fontFamily: typographyVars['--font-family-body'],
37
37
  fontSize: typeScaleVars['--text-label-size'],
38
+ lineHeight: typeScaleVars['--text-label-leading'],
38
39
  fontWeight: fontWeightVars['--font-weight-medium'],
39
40
  color: colorVars['--color-text-secondary'],
40
41
  cursor: 'pointer',
@@ -61,6 +62,7 @@ const styles = stylex.create({
61
62
  optionalRequired: {
62
63
  fontWeight: fontWeightVars['--font-weight-normal'],
63
64
  fontSize: typeScaleVars['--text-supporting-size'],
65
+ lineHeight: typeScaleVars['--text-supporting-leading'],
64
66
  color: colorVars['--color-text-secondary'],
65
67
  },
66
68
  description: {
@@ -80,9 +82,28 @@ export interface FieldLabelProps extends BaseProps<HTMLLabelElement> {
80
82
  */
81
83
  label: string;
82
84
  /**
83
- * ID of the input element this label is for.
85
+ * ID of the input element this label points AT (rendered as `htmlFor` on the
86
+ * label). This is *not* the id of the label element itself — see
87
+ * `labelID` for that.
84
88
  */
85
89
  inputID: string;
90
+ /**
91
+ * The `id` applied TO the label element itself (not the element it points
92
+ * at — that's `inputID`). A grouping control (e.g. `role="radiogroup"`) can
93
+ * reference this via `aria-labelledby` to take the label as its accessible
94
+ * name.
95
+ */
96
+ labelID?: string;
97
+ /**
98
+ * When true, the field wraps a *group* of controls (e.g. a radiogroup)
99
+ * rather than a single input. In that case the label is rendered as a
100
+ * `<span>` instead of a `<label>` — a `<label>` semantically names one form
101
+ * control and can't be associated with a group, so it must not be a literal
102
+ * label element. The group takes the label as its name via
103
+ * `labelID` + `aria-labelledby`.
104
+ * @default false
105
+ */
106
+ isGroupLabel?: boolean;
86
107
  /**
87
108
  * Whether to visually hide the label and description (still accessible
88
109
  * to screen readers). When hidden, the entire label group is rendered
@@ -140,6 +161,8 @@ export interface FieldLabelProps extends BaseProps<HTMLLabelElement> {
140
161
  export function FieldLabel({
141
162
  label,
142
163
  inputID,
164
+ labelID,
165
+ isGroupLabel = false,
143
166
  isLabelHidden = false,
144
167
  isDisabled = false,
145
168
  isOptional = false,
@@ -152,11 +175,38 @@ export function FieldLabel({
152
175
  }: FieldLabelProps) {
153
176
  const statusText = isOptional ? 'Optional' : isRequired ? 'Required' : null;
154
177
 
178
+ // A group label (e.g. for a radiogroup) must not be a literal `<label>`
179
+ // element: a `<label>` semantically names a single form control and can't be
180
+ // associated with a group. Render it as a `<span>` instead, keeping all the
181
+ // label styling and slots. The group references it via `aria-labelledby`.
182
+ const LabelElement = isGroupLabel ? 'span' : 'label';
183
+
184
+ const labelContent = (
185
+ <>
186
+ {labelIcon && renderIconSlot(labelIcon, {size: 'sm', color: 'inherit'})}
187
+ {label}
188
+ {statusText && (
189
+ <span {...stylex.props(styles.optionalRequired)}>
190
+ <span aria-hidden="true"> ∙ </span>
191
+ {statusText}
192
+ </span>
193
+ )}
194
+ {labelTooltip && (
195
+ <Tooltip content={labelTooltip} placement="above">
196
+ <Icon icon="info" size="sm" color="inherit" />
197
+ </Tooltip>
198
+ )}
199
+ </>
200
+ );
201
+
155
202
  return (
156
203
  <>
157
- <label
204
+ <LabelElement
158
205
  ref={ref}
159
- htmlFor={inputID}
206
+ id={labelID}
207
+ // `htmlFor` only applies to a real `<label>` associating with a single
208
+ // control; a group label (span) has no `htmlFor`.
209
+ htmlFor={isGroupLabel ? undefined : inputID}
160
210
  {...mergeProps(
161
211
  themeProps('field-label'),
162
212
  stylex.props(
@@ -165,20 +215,8 @@ export function FieldLabel({
165
215
  isLabelHidden && styles.srOnly,
166
216
  ),
167
217
  )}>
168
- {labelIcon && renderIconSlot(labelIcon, {size: 'sm', color: 'inherit'})}
169
- {label}
170
- {statusText && (
171
- <span {...stylex.props(styles.optionalRequired)}>
172
- <span aria-hidden="true"> ∙ </span>
173
- {statusText}
174
- </span>
175
- )}
176
- {labelTooltip && (
177
- <Tooltip content={labelTooltip} placement="above">
178
- <Icon icon="info" size="sm" color="inherit" />
179
- </Tooltip>
180
- )}
181
- </label>
218
+ {labelContent}
219
+ </LabelElement>
182
220
  {description && (
183
221
  <span
184
222
  id={descriptionID}
@@ -88,6 +88,12 @@ export const docs = {
88
88
  'Disables the input, preventing interaction and dimming the element.',
89
89
  default: 'false',
90
90
  },
91
+ {
92
+ name: 'disabledMessage',
93
+ type: 'string',
94
+ description:
95
+ 'Explains why the input is disabled. With isDisabled, shows a tooltip on hover/keyboard focus and keeps the trigger focusable via aria-disabled (opening the file picker stays blocked). Use this instead of wrapping a disabled FileInput in Tooltip — disabled controls swallow the hover events an external Tooltip needs.',
96
+ },
91
97
  {
92
98
  name: 'isLoading',
93
99
  type: 'boolean',
@@ -135,6 +141,7 @@ export const docs = {
135
141
  {guidance: true, description: 'Use changeAction for immediate upload workflows that benefit from optimistic UI.'},
136
142
  {guidance: false, description: "Don't use FileInput for directory or folder uploads; that is not supported in v1."},
137
143
  {guidance: false, description: "Don't avoid dropzone mode unless space is constrained; drag-and-drop is the expected interaction for file uploads."},
144
+ {guidance: false, description: "Don't wrap a disabled FileInput in Tooltip to explain why it's disabled; disabled controls swallow the hover events the wrapper needs. Use the disabledMessage prop instead."},
138
145
  ],
139
146
  anatomy: [
140
147
  {name: 'Label', required: true, description: 'Text that identifies the field. Always rendered for accessibility even when visually hidden.'},
@@ -211,6 +218,12 @@ export const docsZh = {
211
218
  description: '禁用输入框,阻止交互并使元素变暗。',
212
219
  default: 'false',
213
220
  },
221
+ {
222
+ name: 'disabledMessage',
223
+ type: 'string',
224
+ description:
225
+ '说明输入框被禁用的原因。与 isDisabled 一起使用时,在悬停/键盘聚焦时显示工具提示,并通过 aria-disabled 保持触发器可聚焦(打开文件选择器仍被阻止)。请使用此属性,而不是用 Tooltip 包裹已禁用的 FileInput。',
226
+ },
214
227
  {
215
228
  name: 'isLoading',
216
229
  type: 'boolean',
@@ -248,6 +261,7 @@ export const docsZh = {
248
261
  {guidance: true, description: 'Add a description to communicate constraints.'},
249
262
  {guidance: false, description: "Don't use FileInput for directory uploads."},
250
263
  {guidance: false, description: "Don't use mode='input' unless space is constrained; dropzone mode provides a better experience."},
264
+ {guidance: false, description: "Don't wrap a disabled FileInput in Tooltip to explain the disabled state; use the disabledMessage prop instead."},
251
265
  ],
252
266
  anatomy: [
253
267
  {name: 'Label', required: true, description: 'Text identifying the field.'},
@@ -272,6 +286,7 @@ export const docsDense = {
272
286
  {guidance: true, description: 'Use changeAction for immediate upload workflows that benefit from optimistic UI.'},
273
287
  {guidance: false, description: "Don't use FileInput for directory uploads."},
274
288
  {guidance: false, description: "Don't use mode='input' unless space is constrained; dropzone mode provides a better experience."},
289
+ {guidance: false, description: "Don't wrap a disabled FileInput in Tooltip to explain the disabled state; use the disabledMessage prop instead."},
275
290
  ],
276
291
  anatomy: [
277
292
  {name: 'Label', required: true, description: 'Text identifying the field.'},
@@ -295,6 +310,8 @@ export const docsDense = {
295
310
  isOptional: 'Shows "Optional" indicator.',
296
311
  isRequired: 'Shows "Required" indicator+sets aria-required.',
297
312
  isDisabled: 'Disables input, prevents interaction.',
313
+ disabledMessage:
314
+ 'Explains why input is disabled. With isDisabled, shows tooltip on hover/focus + keeps trigger focusable via aria-disabled (opening picker stays blocked). Use instead of wrapping a disabled FileInput in Tooltip.',
298
315
  isLoading: 'Loading state w/ spinner+aria-busy.',
299
316
  placeholder: 'Placeholder when no files selected.',
300
317
  mode: "Visual mode: 'input' (compact) or 'dropzone' (drag-and-drop).",