@astryxdesign/core 0.1.9 → 0.2.0-canary.136a00f

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 (560) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/Avatar/Avatar.d.ts +42 -3
  3. package/dist/Avatar/Avatar.d.ts.map +1 -1
  4. package/dist/Avatar/Avatar.js +190 -59
  5. package/dist/Avatar/AvatarStatusDot.d.ts +6 -4
  6. package/dist/Avatar/AvatarStatusDot.d.ts.map +1 -1
  7. package/dist/AvatarGroup/AvatarGroup.d.ts +7 -1
  8. package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
  9. package/dist/AvatarGroup/AvatarGroup.js +58 -6
  10. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
  11. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  12. package/dist/AvatarGroup/AvatarGroupOverflow.js +7 -2
  13. package/dist/Banner/Banner.js +2 -2
  14. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  15. package/dist/Breadcrumbs/BreadcrumbItem.js +11 -4
  16. package/dist/Breadcrumbs/index.d.ts +1 -0
  17. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/index.js +2 -1
  19. package/dist/Button/Button.d.ts +1 -1
  20. package/dist/Button/Button.d.ts.map +1 -1
  21. package/dist/Button/Button.js +6 -4
  22. package/dist/Calendar/Calendar.d.ts.map +1 -1
  23. package/dist/Calendar/Calendar.js +50 -9
  24. package/dist/Calendar/dayCellUtils.d.ts +4 -4
  25. package/dist/Calendar/dayCellUtils.js +2 -2
  26. package/dist/Calendar/styles.d.ts +30 -31
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +30 -31
  29. package/dist/Card/Card.js +1 -1
  30. package/dist/Carousel/Carousel.d.ts.map +1 -1
  31. package/dist/Carousel/Carousel.js +22 -12
  32. package/dist/Chat/ChatComposerInput.js +1 -1
  33. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatDictationButton.js +3 -1
  35. package/dist/Chat/ChatLayout.js +4 -4
  36. package/dist/Chat/ChatMessageBubble.d.ts.map +1 -1
  37. package/dist/Chat/ChatMessageBubble.js +8 -8
  38. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  39. package/dist/Chat/ChatToolCalls.js +10 -3
  40. package/dist/Chat/useTriggerMenu.js +2 -2
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +6 -6
  43. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  44. package/dist/CheckboxList/CheckboxListItem.js +19 -4
  45. package/dist/Citation/Citation.d.ts +22 -1
  46. package/dist/Citation/Citation.d.ts.map +1 -1
  47. package/dist/Citation/Citation.js +20 -6
  48. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  49. package/dist/CodeBlock/CodeBlock.js +16 -10
  50. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  51. package/dist/Collapsible/Collapsible.js +3 -1
  52. package/dist/CommandPalette/CommandPalette.d.ts +1 -1
  53. package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
  54. package/dist/CommandPalette/CommandPalette.js +42 -3
  55. package/dist/CommandPalette/CommandPaletteItem.js +1 -1
  56. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  57. package/dist/ContextMenu/ContextMenu.js +23 -5
  58. package/dist/ContextMenu/index.d.ts +1 -0
  59. package/dist/ContextMenu/index.d.ts.map +1 -1
  60. package/dist/ContextMenu/index.js +3 -1
  61. package/dist/DateInput/DateInput.d.ts +10 -2
  62. package/dist/DateInput/DateInput.d.ts.map +1 -1
  63. package/dist/DateInput/DateInput.js +33 -19
  64. package/dist/DateRangeInput/DateRangeInput.d.ts +10 -2
  65. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  66. package/dist/DateRangeInput/DateRangeInput.js +23 -20
  67. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  68. package/dist/DateTimeInput/DateTimeInput.js +23 -18
  69. package/dist/Divider/Divider.d.ts +2 -2
  70. package/dist/Divider/Divider.d.ts.map +1 -1
  71. package/dist/Divider/Divider.js +13 -0
  72. package/dist/DropdownMenu/DropdownMenu.d.ts +6 -0
  73. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  74. package/dist/DropdownMenu/DropdownMenu.js +22 -5
  75. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +4 -1
  77. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuItem.js +5 -2
  79. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +8 -11
  80. package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
  81. package/dist/DropdownMenu/DropdownMenuRadioGroup.js +3 -1
  82. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
  83. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuRadioItem.js +5 -2
  85. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts +104 -0
  86. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -0
  87. package/dist/DropdownMenu/DropdownMenuSubMenu.js +427 -0
  88. package/dist/DropdownMenu/index.d.ts +10 -0
  89. package/dist/DropdownMenu/index.d.ts.map +1 -1
  90. package/dist/DropdownMenu/index.js +13 -0
  91. package/dist/DropdownMenu/menuItemHover.d.ts +23 -0
  92. package/dist/DropdownMenu/menuItemHover.d.ts.map +1 -0
  93. package/dist/DropdownMenu/menuItemHover.js +33 -0
  94. package/dist/DropdownMenu/menuItemRoles.d.ts +8 -0
  95. package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -1
  96. package/dist/DropdownMenu/menuItemRoles.js +10 -1
  97. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  98. package/dist/DropdownMenu/renderDropdownItems.js +21 -6
  99. package/dist/Field/Field.d.ts +3 -1
  100. package/dist/Field/Field.d.ts.map +1 -1
  101. package/dist/Field/Field.js +4 -1
  102. package/dist/Field/FieldLabel.d.ts +2 -1
  103. package/dist/Field/FieldLabel.d.ts.map +1 -1
  104. package/dist/Field/FieldLabel.js +9 -5
  105. package/dist/Field/inputStyles.stylex.d.ts +1 -0
  106. package/dist/Field/inputStyles.stylex.d.ts.map +1 -1
  107. package/dist/Field/inputStyles.stylex.js +1 -0
  108. package/dist/FieldStatus/FieldStatus.d.ts +10 -0
  109. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  110. package/dist/FieldStatus/FieldStatus.js +39 -3
  111. package/dist/FileInput/FileInput.d.ts +10 -2
  112. package/dist/FileInput/FileInput.d.ts.map +1 -1
  113. package/dist/FileInput/FileInput.js +93 -65
  114. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  115. package/dist/HoverCard/useHoverCard.js +10 -4
  116. package/dist/Icon/Icon.d.ts +15 -1
  117. package/dist/Icon/Icon.d.ts.map +1 -1
  118. package/dist/Icon/Icon.js +19 -3
  119. package/dist/Item/Item.d.ts +16 -3
  120. package/dist/Item/Item.d.ts.map +1 -1
  121. package/dist/Item/Item.js +31 -6
  122. package/dist/Layer/layerAnimations.stylex.d.ts.map +1 -1
  123. package/dist/Layer/layerAnimations.stylex.js +10 -2
  124. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  125. package/dist/Lightbox/Lightbox.js +19 -13
  126. package/dist/List/List.d.ts.map +1 -1
  127. package/dist/List/List.js +6 -3
  128. package/dist/List/ListItem.d.ts +11 -1
  129. package/dist/List/ListItem.d.ts.map +1 -1
  130. package/dist/List/ListItem.js +2 -0
  131. package/dist/Markdown/Markdown.d.ts.map +1 -1
  132. package/dist/Markdown/Markdown.js +7 -0
  133. package/dist/MetadataList/MetadataList.d.ts +1 -1
  134. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  135. package/dist/MetadataList/MetadataList.js +4 -2
  136. package/dist/MultiSelector/MultiSelector.d.ts +9 -1
  137. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  138. package/dist/MultiSelector/MultiSelector.js +80 -42
  139. package/dist/NavMenu/NavHeadingMenuItem.js +1 -1
  140. package/dist/NumberInput/NumberInput.d.ts +10 -2
  141. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  142. package/dist/NumberInput/NumberInput.js +22 -17
  143. package/dist/Outline/Outline.js +2 -2
  144. package/dist/Pagination/Pagination.d.ts.map +1 -1
  145. package/dist/Pagination/Pagination.js +11 -3
  146. package/dist/PowerSearch/PowerSearch.d.ts +9 -2
  147. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  148. package/dist/PowerSearch/PowerSearch.js +2 -0
  149. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  150. package/dist/ProgressBar/ProgressBar.js +6 -1
  151. package/dist/RadioList/RadioList.d.ts +2 -2
  152. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  153. package/dist/RadioList/RadioListItem.js +5 -5
  154. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  155. package/dist/Resizable/ResizeHandle.js +49 -19
  156. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  157. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  158. package/dist/SegmentedControl/SegmentedControlItem.js +5 -3
  159. package/dist/Selector/Selector.d.ts +8 -0
  160. package/dist/Selector/Selector.d.ts.map +1 -1
  161. package/dist/Selector/Selector.js +66 -17
  162. package/dist/SideNav/SideNav.d.ts.map +1 -1
  163. package/dist/SideNav/SideNav.js +5 -5
  164. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  165. package/dist/SideNav/SideNavCollapseButton.js +13 -10
  166. package/dist/SideNav/SideNavItem.d.ts +1 -1
  167. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  168. package/dist/SideNav/SideNavItem.js +12 -10
  169. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  170. package/dist/Skeleton/Skeleton.js +2 -2
  171. package/dist/Slider/Slider.d.ts.map +1 -1
  172. package/dist/Slider/Slider.js +87 -34
  173. package/dist/Spinner/Spinner.d.ts +3 -2
  174. package/dist/Spinner/Spinner.d.ts.map +1 -1
  175. package/dist/Spinner/Spinner.js +11 -2
  176. package/dist/Switch/Switch.d.ts +8 -1
  177. package/dist/Switch/Switch.d.ts.map +1 -1
  178. package/dist/Switch/Switch.js +171 -91
  179. package/dist/TabList/Tab.js +2 -2
  180. package/dist/TabList/TabList.d.ts +2 -11
  181. package/dist/TabList/TabList.d.ts.map +1 -1
  182. package/dist/TabList/TabList.js +5 -9
  183. package/dist/TabList/TabListContext.d.ts +0 -5
  184. package/dist/TabList/TabListContext.d.ts.map +1 -1
  185. package/dist/TabList/TabListContext.js +0 -5
  186. package/dist/TabList/TabMenu.js +1 -1
  187. package/dist/Table/BaseTable.d.ts.map +1 -1
  188. package/dist/Table/BaseTable.js +30 -4
  189. package/dist/Table/TableCell.js +3 -3
  190. package/dist/Table/TableHeaderCell.js +3 -3
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +15 -11
  193. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  194. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +37 -27
  195. package/dist/Table/plugins/selection/useTableSelection.d.ts.map +1 -1
  196. package/dist/Table/plugins/selection/useTableSelection.js +3 -0
  197. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  198. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +15 -7
  199. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  200. package/dist/Table/plugins/tree/useTableTreeData.js +29 -23
  201. package/dist/Table/types.d.ts +32 -0
  202. package/dist/Table/types.d.ts.map +1 -1
  203. package/dist/TextArea/TextArea.d.ts +10 -1
  204. package/dist/TextArea/TextArea.d.ts.map +1 -1
  205. package/dist/TextArea/TextArea.js +18 -19
  206. package/dist/TextInput/TextInput.d.ts +10 -2
  207. package/dist/TextInput/TextInput.d.ts.map +1 -1
  208. package/dist/TextInput/TextInput.js +20 -17
  209. package/dist/Thumbnail/Thumbnail.js +2 -2
  210. package/dist/TimeInput/TimeInput.d.ts +10 -2
  211. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  212. package/dist/TimeInput/TimeInput.js +17 -17
  213. package/dist/Timestamp/Timestamp.d.ts +31 -3
  214. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  215. package/dist/Timestamp/Timestamp.js +47 -76
  216. package/dist/Timestamp/formatInstant.d.ts +41 -0
  217. package/dist/Timestamp/formatInstant.d.ts.map +1 -0
  218. package/dist/Timestamp/formatInstant.js +177 -0
  219. package/dist/Timestamp/index.d.ts +1 -0
  220. package/dist/Timestamp/index.d.ts.map +1 -1
  221. package/dist/Timestamp/tooltipEntries.d.ts +56 -0
  222. package/dist/Timestamp/tooltipEntries.d.ts.map +1 -0
  223. package/dist/Timestamp/tooltipEntries.js +163 -0
  224. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  225. package/dist/Toast/ToastViewport.js +61 -9
  226. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  227. package/dist/ToggleButton/ToggleButton.js +3 -1
  228. package/dist/Token/Token.d.ts +34 -4
  229. package/dist/Token/Token.d.ts.map +1 -1
  230. package/dist/Token/Token.js +85 -45
  231. package/dist/Token/TokenLink.d.ts +62 -0
  232. package/dist/Token/TokenLink.d.ts.map +1 -0
  233. package/dist/Token/TokenLink.js +76 -0
  234. package/dist/Token/index.d.ts +1 -1
  235. package/dist/Token/index.d.ts.map +1 -1
  236. package/dist/Tokenizer/Tokenizer.d.ts +9 -2
  237. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  238. package/dist/Tokenizer/Tokenizer.js +4 -2
  239. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  240. package/dist/Tooltip/useTooltip.js +17 -1
  241. package/dist/TopNav/TopNavItem.d.ts +3 -1
  242. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  243. package/dist/TopNav/TopNavItem.js +30 -3
  244. package/dist/TreeList/TreeList.d.ts.map +1 -1
  245. package/dist/TreeList/TreeList.js +1 -0
  246. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  247. package/dist/TreeList/TreeListBranches.js +8 -6
  248. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  249. package/dist/TreeList/TreeListItem.js +32 -14
  250. package/dist/Typeahead/BaseTypeahead.d.ts +9 -0
  251. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  252. package/dist/Typeahead/BaseTypeahead.js +22 -11
  253. package/dist/Typeahead/Typeahead.d.ts +9 -2
  254. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  255. package/dist/Typeahead/Typeahead.js +11 -2
  256. package/dist/Typeahead/TypeaheadItem.d.ts +1 -1
  257. package/dist/Typeahead/TypeaheadItem.d.ts.map +1 -1
  258. package/dist/Typeahead/TypeaheadItem.js +20 -10
  259. package/dist/astryx.css +70 -30
  260. package/dist/astryx.umd.js +51 -51
  261. package/dist/astryx.umd.js.map +4 -4
  262. package/dist/hooks/index.d.ts +2 -0
  263. package/dist/hooks/index.d.ts.map +1 -1
  264. package/dist/hooks/index.js +1 -0
  265. package/dist/hooks/isRtlElement.d.ts +18 -0
  266. package/dist/hooks/isRtlElement.d.ts.map +1 -0
  267. package/dist/hooks/isRtlElement.js +27 -0
  268. package/dist/hooks/useAnnounce.d.ts +3 -0
  269. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  270. package/dist/hooks/useAnnounce.js +39 -1
  271. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  272. package/dist/hooks/useFocusTrap.js +52 -11
  273. package/dist/hooks/useGridFocus.d.ts +6 -1
  274. package/dist/hooks/useGridFocus.d.ts.map +1 -1
  275. package/dist/hooks/useGridFocus.js +12 -9
  276. package/dist/hooks/useHotkeys.d.ts +9 -0
  277. package/dist/hooks/useHotkeys.d.ts.map +1 -1
  278. package/dist/hooks/useHotkeys.js +9 -0
  279. package/dist/hooks/useInputStatusIcon.d.ts +38 -0
  280. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -0
  281. package/dist/hooks/useInputStatusIcon.js +158 -0
  282. package/dist/hooks/useKeyboardHint.d.ts +1 -1
  283. package/dist/hooks/useKeyboardHint.d.ts.map +1 -1
  284. package/dist/hooks/useKeyboardHint.js +4 -2
  285. package/dist/hooks/useListFocus.d.ts +39 -1
  286. package/dist/hooks/useListFocus.d.ts.map +1 -1
  287. package/dist/hooks/useListFocus.js +54 -9
  288. package/dist/i18n/InternationalizationContext.d.ts +1 -0
  289. package/dist/i18n/InternationalizationContext.d.ts.map +1 -1
  290. package/dist/i18n/InternationalizationContext.js +3 -0
  291. package/dist/i18n/InternationalizationProvider.d.ts +8 -1
  292. package/dist/i18n/InternationalizationProvider.d.ts.map +1 -1
  293. package/dist/i18n/InternationalizationProvider.js +5 -1
  294. package/dist/i18n/getLocaleDirection.d.ts +18 -0
  295. package/dist/i18n/getLocaleDirection.d.ts.map +1 -0
  296. package/dist/i18n/getLocaleDirection.js +29 -0
  297. package/dist/i18n/index.d.ts +6 -0
  298. package/dist/i18n/index.d.ts.map +1 -1
  299. package/dist/i18n/index.js +7 -1
  300. package/dist/i18n/useDirection.d.ts +2 -0
  301. package/dist/i18n/useDirection.d.ts.map +1 -0
  302. package/dist/i18n/useDirection.js +27 -0
  303. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  304. package/dist/theme/derivedVarRegistry.js +13 -0
  305. package/dist/utils/index.d.ts +1 -0
  306. package/dist/utils/index.d.ts.map +1 -1
  307. package/dist/utils/index.js +2 -1
  308. package/dist/utils/plainDate.d.ts +17 -0
  309. package/dist/utils/plainDate.d.ts.map +1 -1
  310. package/dist/utils/plainDate.js +23 -4
  311. package/dist/utils/rtlStyles.d.ts +24 -0
  312. package/dist/utils/rtlStyles.d.ts.map +1 -0
  313. package/dist/utils/rtlStyles.js +27 -0
  314. package/locales/en.json +112 -0
  315. package/locales/pseudo.json +84 -0
  316. package/package.json +6 -1
  317. package/src/Avatar/Avatar.doc.mjs +50 -3
  318. package/src/Avatar/Avatar.test.tsx +218 -0
  319. package/src/Avatar/Avatar.tsx +285 -79
  320. package/src/Avatar/AvatarStatusDot.doc.mjs +4 -4
  321. package/src/Avatar/AvatarStatusDot.tsx +6 -4
  322. package/src/AvatarGroup/AvatarGroup.doc.mjs +2 -0
  323. package/src/AvatarGroup/AvatarGroup.test.tsx +162 -2
  324. package/src/AvatarGroup/AvatarGroup.tsx +58 -3
  325. package/src/AvatarGroup/AvatarGroupOverflow.tsx +5 -2
  326. package/src/Banner/Banner.tsx +6 -6
  327. package/src/Breadcrumbs/BreadcrumbItem.tsx +10 -10
  328. package/src/Breadcrumbs/Breadcrumbs.test.tsx +52 -1
  329. package/src/Breadcrumbs/index.ts +4 -0
  330. package/src/Button/Button.doc.mjs +0 -4
  331. package/src/Button/Button.test.tsx +16 -0
  332. package/src/Button/Button.tsx +6 -4
  333. package/src/Calendar/Calendar.test.tsx +170 -20
  334. package/src/Calendar/Calendar.tsx +73 -16
  335. package/src/Calendar/dayCellUtils.test.ts +18 -18
  336. package/src/Calendar/dayCellUtils.ts +2 -2
  337. package/src/Calendar/styles.ts +33 -39
  338. package/src/Card/Card.tsx +1 -1
  339. package/src/Carousel/Carousel.tsx +25 -6
  340. package/src/Chat/ChatComposer.doc.mjs +2 -2
  341. package/src/Chat/ChatComposerInput.doc.mjs +1 -1
  342. package/src/Chat/ChatComposerInput.tsx +2 -2
  343. package/src/Chat/ChatDictationButton.tsx +7 -1
  344. package/src/Chat/ChatLayout.tsx +4 -4
  345. package/src/Chat/ChatMessageBubble.tsx +15 -10
  346. package/src/Chat/ChatSystemMessage.test.tsx +5 -0
  347. package/src/Chat/ChatToolCalls.tsx +9 -2
  348. package/src/Chat/useTriggerMenu.tsx +1 -1
  349. package/src/CheckboxInput/CheckboxInput.doc.mjs +6 -0
  350. package/src/CheckboxInput/CheckboxInput.test.tsx +23 -0
  351. package/src/CheckboxInput/CheckboxInput.tsx +21 -6
  352. package/src/CheckboxList/CheckboxList.doc.mjs +6 -0
  353. package/src/CheckboxList/CheckboxList.test.tsx +94 -3
  354. package/src/CheckboxList/CheckboxListItem.tsx +17 -10
  355. package/src/Citation/Citation.doc.mjs +4 -3
  356. package/src/Citation/Citation.test.tsx +97 -0
  357. package/src/Citation/Citation.tsx +43 -11
  358. package/src/CodeBlock/CodeBlock.test.tsx +17 -0
  359. package/src/CodeBlock/CodeBlock.tsx +38 -11
  360. package/src/Collapsible/Collapsible.doc.mjs +1 -0
  361. package/src/Collapsible/Collapsible.test.tsx +7 -0
  362. package/src/Collapsible/Collapsible.tsx +16 -6
  363. package/src/CommandPalette/CommandPalette.test.tsx +95 -1
  364. package/src/CommandPalette/CommandPalette.tsx +41 -2
  365. package/src/CommandPalette/CommandPaletteItem.tsx +1 -1
  366. package/src/ContextMenu/ContextMenu.test.tsx +59 -2
  367. package/src/ContextMenu/ContextMenu.tsx +22 -11
  368. package/src/ContextMenu/index.ts +5 -0
  369. package/src/DateInput/DateInput.doc.mjs +25 -0
  370. package/src/DateInput/DateInput.test.tsx +230 -0
  371. package/src/DateInput/DateInput.tsx +51 -29
  372. package/src/DateRangeInput/DateRangeInput.doc.mjs +16 -0
  373. package/src/DateRangeInput/DateRangeInput.test.tsx +140 -0
  374. package/src/DateRangeInput/DateRangeInput.tsx +42 -30
  375. package/src/DateTimeInput/DateTimeInput.doc.mjs +22 -1
  376. package/src/DateTimeInput/DateTimeInput.test.tsx +112 -0
  377. package/src/DateTimeInput/DateTimeInput.tsx +44 -39
  378. package/src/Divider/Divider.test.tsx +32 -8
  379. package/src/Divider/Divider.tsx +14 -1
  380. package/src/DropdownMenu/DropdownMenu.test.tsx +60 -0
  381. package/src/DropdownMenu/DropdownMenu.tsx +31 -12
  382. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +8 -2
  383. package/src/DropdownMenu/DropdownMenuItem.tsx +9 -3
  384. package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +4 -12
  385. package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +9 -10
  386. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +9 -3
  387. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +20 -2
  388. package/src/DropdownMenu/DropdownMenuSubMenu.doc.mjs +88 -0
  389. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +635 -0
  390. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +553 -0
  391. package/src/DropdownMenu/index.ts +16 -0
  392. package/src/DropdownMenu/menuItemHover.ts +39 -0
  393. package/src/DropdownMenu/menuItemRoles.ts +9 -0
  394. package/src/DropdownMenu/renderDropdownItems.tsx +29 -15
  395. package/src/Field/Field.tsx +14 -9
  396. package/src/Field/FieldLabel.test.tsx +25 -0
  397. package/src/Field/FieldLabel.tsx +12 -3
  398. package/src/Field/inputStyles.stylex.ts +2 -0
  399. package/src/FieldStatus/FieldStatus.test.tsx +72 -0
  400. package/src/FieldStatus/FieldStatus.tsx +48 -2
  401. package/src/FileInput/FileInput.doc.mjs +23 -2
  402. package/src/FileInput/FileInput.test.tsx +104 -13
  403. package/src/FileInput/FileInput.tsx +106 -79
  404. package/src/HoverCard/useHoverCard.tsx +15 -3
  405. package/src/Icon/Icon.doc.mjs +13 -0
  406. package/src/Icon/Icon.test.tsx +89 -0
  407. package/src/Icon/Icon.tsx +51 -3
  408. package/src/Item/Item.doc.mjs +5 -0
  409. package/src/Item/Item.test.tsx +88 -0
  410. package/src/Item/Item.tsx +57 -11
  411. package/src/Layer/layerAnimations.stylex.ts +23 -0
  412. package/src/Lightbox/Lightbox.tsx +15 -7
  413. package/src/List/List.test.tsx +17 -29
  414. package/src/List/List.tsx +6 -1
  415. package/src/List/ListItem.doc.mjs +12 -0
  416. package/src/List/ListItem.tsx +13 -0
  417. package/src/Markdown/Markdown.test.tsx +21 -0
  418. package/src/Markdown/Markdown.tsx +4 -0
  419. package/src/MetadataList/MetadataList.test.tsx +24 -0
  420. package/src/MetadataList/MetadataList.tsx +6 -2
  421. package/src/MultiSelector/MultiSelector.doc.mjs +20 -0
  422. package/src/MultiSelector/MultiSelector.test.tsx +402 -0
  423. package/src/MultiSelector/MultiSelector.tsx +120 -59
  424. package/src/NavMenu/NavHeadingMenuItem.tsx +1 -1
  425. package/src/NumberInput/NumberInput.doc.mjs +21 -0
  426. package/src/NumberInput/NumberInput.test.tsx +71 -0
  427. package/src/NumberInput/NumberInput.tsx +35 -26
  428. package/src/Outline/Outline.test.tsx +12 -12
  429. package/src/Outline/Outline.tsx +2 -2
  430. package/src/Pagination/Pagination.tsx +11 -3
  431. package/src/PowerSearch/PowerSearch.doc.mjs +15 -0
  432. package/src/PowerSearch/PowerSearch.test.tsx +23 -0
  433. package/src/PowerSearch/PowerSearch.tsx +10 -1
  434. package/src/ProgressBar/ProgressBar.test.tsx +34 -0
  435. package/src/ProgressBar/ProgressBar.tsx +16 -1
  436. package/src/RadioList/RadioList.doc.mjs +6 -0
  437. package/src/RadioList/RadioList.test.tsx +26 -4
  438. package/src/RadioList/RadioList.tsx +2 -2
  439. package/src/RadioList/RadioListItem.tsx +12 -5
  440. package/src/Resizable/ResizeHandle.test.tsx +94 -0
  441. package/src/Resizable/ResizeHandle.tsx +70 -20
  442. package/src/SegmentedControl/SegmentedControl.test.tsx +28 -0
  443. package/src/SegmentedControl/SegmentedControlItem.tsx +22 -2
  444. package/src/Selector/Selector.doc.mjs +15 -0
  445. package/src/Selector/Selector.test.tsx +370 -0
  446. package/src/Selector/Selector.tsx +88 -20
  447. package/src/SideNav/SideNav.tsx +1 -8
  448. package/src/SideNav/SideNavCollapseButton.tsx +9 -7
  449. package/src/SideNav/SideNavItem.tsx +5 -3
  450. package/src/Skeleton/Skeleton.test.tsx +16 -0
  451. package/src/Skeleton/Skeleton.tsx +13 -1
  452. package/src/Slider/Slider.doc.mjs +6 -0
  453. package/src/Slider/Slider.test.tsx +240 -12
  454. package/src/Slider/Slider.tsx +96 -23
  455. package/src/Spinner/Spinner.test.tsx +10 -8
  456. package/src/Spinner/Spinner.tsx +15 -5
  457. package/src/Switch/Switch.doc.mjs +142 -42
  458. package/src/Switch/Switch.test.tsx +111 -0
  459. package/src/Switch/Switch.tsx +149 -32
  460. package/src/TabList/Tab.tsx +2 -2
  461. package/src/TabList/TabList.doc.mjs +0 -6
  462. package/src/TabList/TabList.test.tsx +3 -15
  463. package/src/TabList/TabList.tsx +6 -17
  464. package/src/TabList/TabListContext.ts +0 -6
  465. package/src/TabList/TabMenu.tsx +2 -2
  466. package/src/Table/BaseTable.tsx +34 -2
  467. package/src/Table/Table.doc.mjs +11 -0
  468. package/src/Table/Table.test.tsx +85 -0
  469. package/src/Table/TableCell.tsx +3 -3
  470. package/src/Table/TableHeaderCell.tsx +3 -3
  471. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +9 -6
  472. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +19 -0
  473. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +27 -18
  474. package/src/Table/plugins/selection/useTableSelection.test.tsx +61 -3
  475. package/src/Table/plugins/selection/useTableSelection.tsx +3 -0
  476. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +38 -9
  477. package/src/Table/plugins/tree/useTableTreeData.tsx +17 -13
  478. package/src/Table/types.ts +33 -0
  479. package/src/Table/useTableRowStatus.doc.mjs +1 -1
  480. package/src/TextArea/TextArea.doc.mjs +21 -0
  481. package/src/TextArea/TextArea.test.tsx +23 -0
  482. package/src/TextArea/TextArea.tsx +27 -26
  483. package/src/TextInput/TextInput.doc.mjs +21 -0
  484. package/src/TextInput/TextInput.test.tsx +150 -0
  485. package/src/TextInput/TextInput.tsx +28 -25
  486. package/src/Thumbnail/Thumbnail.doc.mjs +1 -1
  487. package/src/Thumbnail/Thumbnail.tsx +1 -1
  488. package/src/TimeInput/TimeInput.doc.mjs +21 -0
  489. package/src/TimeInput/TimeInput.test.tsx +23 -0
  490. package/src/TimeInput/TimeInput.tsx +24 -25
  491. package/src/Timestamp/Timestamp.doc.mjs +32 -6
  492. package/src/Timestamp/Timestamp.test.tsx +371 -0
  493. package/src/Timestamp/Timestamp.tsx +96 -93
  494. package/src/Timestamp/formatInstant.ts +218 -0
  495. package/src/Timestamp/index.ts +4 -0
  496. package/src/Timestamp/tooltipEntries.test.ts +552 -0
  497. package/src/Timestamp/tooltipEntries.ts +210 -0
  498. package/src/Toast/ToastViewport.test.tsx +218 -8
  499. package/src/Toast/ToastViewport.tsx +71 -12
  500. package/src/ToggleButton/ToggleButton.test.tsx +24 -0
  501. package/src/ToggleButton/ToggleButton.tsx +19 -1
  502. package/src/Token/Token.test.tsx +166 -15
  503. package/src/Token/Token.tsx +104 -47
  504. package/src/Token/TokenLink.tsx +107 -0
  505. package/src/Token/index.ts +1 -1
  506. package/src/Tokenizer/Tokenizer.doc.mjs +23 -1
  507. package/src/Tokenizer/Tokenizer.test.tsx +23 -0
  508. package/src/Tokenizer/Tokenizer.tsx +14 -2
  509. package/src/Tooltip/Tooltip.test.tsx +46 -0
  510. package/src/Tooltip/useTooltip.tsx +20 -0
  511. package/src/TopNav/TopNav.test.tsx +93 -2
  512. package/src/TopNav/TopNavItem.doc.mjs +3 -3
  513. package/src/TopNav/TopNavItem.tsx +35 -6
  514. package/src/TopNav/TopNavMegaMenuFeaturedCard.doc.mjs +1 -1
  515. package/src/TreeList/TreeList.doc.mjs +6 -0
  516. package/src/TreeList/TreeList.test.tsx +50 -10
  517. package/src/TreeList/TreeList.tsx +5 -0
  518. package/src/TreeList/TreeListBranches.tsx +8 -6
  519. package/src/TreeList/TreeListItem.tsx +33 -11
  520. package/src/Typeahead/BaseTypeahead.tsx +42 -15
  521. package/src/Typeahead/Typeahead.doc.mjs +13 -0
  522. package/src/Typeahead/Typeahead.test.tsx +112 -0
  523. package/src/Typeahead/Typeahead.tsx +17 -1
  524. package/src/Typeahead/TypeaheadItem.tsx +2 -1
  525. package/src/__tests__/forcedColors.ts +60 -0
  526. package/src/__tests__/inputWidthContract.test.tsx +62 -0
  527. package/src/doc-mjs.d.ts +19 -0
  528. package/src/hooks/index.ts +6 -0
  529. package/src/hooks/isRtlElement.ts +27 -0
  530. package/src/hooks/useAnnounce.test.tsx +75 -2
  531. package/src/hooks/useAnnounce.ts +47 -1
  532. package/src/hooks/useFocusTrap.test.tsx +104 -0
  533. package/src/hooks/useFocusTrap.ts +61 -12
  534. package/src/hooks/useGridFocus.doc.mjs +3 -3
  535. package/src/hooks/useGridFocus.test.tsx +41 -0
  536. package/src/hooks/useGridFocus.ts +21 -10
  537. package/src/hooks/useHotkeys.ts +9 -0
  538. package/src/hooks/useInputStatusIcon.test.tsx +162 -0
  539. package/src/hooks/useInputStatusIcon.tsx +224 -0
  540. package/src/hooks/useKeyboardHint.test.tsx +22 -0
  541. package/src/hooks/useKeyboardHint.tsx +6 -2
  542. package/src/hooks/useListFocus.doc.mjs +25 -3
  543. package/src/hooks/useListFocus.test.tsx +153 -1
  544. package/src/hooks/useListFocus.ts +102 -9
  545. package/src/i18n/InternationalizationContext.ts +4 -0
  546. package/src/i18n/InternationalizationProvider.tsx +12 -2
  547. package/src/i18n/__tests__/getLocaleDirection.test.ts +40 -0
  548. package/src/i18n/__tests__/useDirection.test.tsx +66 -0
  549. package/src/i18n/getLocaleDirection.ts +29 -0
  550. package/src/i18n/index.ts +6 -0
  551. package/src/i18n/useDirection.ts +29 -0
  552. package/src/reset.css +0 -26
  553. package/src/reset.test.ts +60 -0
  554. package/src/theme/derivedVarRegistry.test.ts +1 -0
  555. package/src/theme/derivedVarRegistry.ts +6 -0
  556. package/src/theme/generateThemeRules.test.ts +42 -0
  557. package/src/utils/index.ts +2 -0
  558. package/src/utils/plainDate.test.ts +24 -6
  559. package/src/utils/plainDate.ts +18 -4
  560. package/src/utils/rtlStyles.ts +27 -0
@@ -15,7 +15,7 @@
15
15
  * - /packages/cli/templates/blocks/components/Divider/ (showcase blocks)
16
16
  */
17
17
 
18
- import type {ReactNode} from 'react';
18
+ import {useId, type ReactNode} from 'react';
19
19
  import type {BaseProps} from '../BaseProps';
20
20
  import * as stylex from '@stylexjs/stylex';
21
21
 
@@ -165,9 +165,19 @@ export function Divider({
165
165
  className,
166
166
  style,
167
167
  ref,
168
+ 'aria-label': ariaLabel,
169
+ 'aria-labelledby': ariaLabelledBy,
168
170
  ...props
169
171
  }: DividerProps) {
170
172
  const isHorizontal = orientation === 'horizontal';
173
+ const labelId = useId();
174
+
175
+ // A separator does not derive its accessible name from its content, so the
176
+ // rendered label must be referenced explicitly via aria-labelledby to be
177
+ // exposed as the separator's name (WCAG 1.3.1). An explicit aria-label or
178
+ // aria-labelledby from the consumer takes precedence.
179
+ const resolvedLabelledBy =
180
+ ariaLabelledBy ?? (label && ariaLabel == null ? labelId : undefined);
171
181
 
172
182
  return (
173
183
  <div
@@ -175,6 +185,8 @@ export function Divider({
175
185
  {...props}
176
186
  role="separator"
177
187
  aria-orientation={orientation}
188
+ aria-label={ariaLabel}
189
+ aria-labelledby={resolvedLabelledBy}
178
190
  {...mergeProps(
179
191
  themeProps('divider', {variant, orientation}),
180
192
  stylex.props(
@@ -196,6 +208,7 @@ export function Divider({
196
208
  />
197
209
  {label && (
198
210
  <div
211
+ id={labelId}
199
212
  {...stylex.props(
200
213
  labelStyles.label,
201
214
  !isHorizontal && labelStyles.verticalLabel,
@@ -781,4 +781,64 @@ describe('DropdownMenu keyboard access for menuitemradio/menuitemcheckbox (#3829
781
781
  screen.getByRole('menuitemradio', {name: 'Oldest', hidden: true}),
782
782
  ).toHaveFocus();
783
783
  });
784
+
785
+ it('moves focus to the item the mouse hovers, keeping a single highlight', async () => {
786
+ const user = userEvent.setup();
787
+ render(
788
+ <DropdownMenu button={{label: 'Actions'}}>
789
+ <DropdownMenuItem label="Edit" onClick={() => {}} />
790
+ <DropdownMenuItem label="Duplicate" onClick={() => {}} />
791
+ <DropdownMenuItem label="Delete" onClick={() => {}} />
792
+ </DropdownMenu>,
793
+ );
794
+
795
+ await user.click(screen.getByRole('button', {name: /Actions/}));
796
+ // Keyboard focus starts on the first item.
797
+ const edit = screen.getByRole('menuitem', {name: 'Edit', hidden: true});
798
+ const del = screen.getByRole('menuitem', {name: 'Delete', hidden: true});
799
+ edit.focus();
800
+ expect(edit).toHaveFocus();
801
+
802
+ // A mouse hover over another item moves focus to it, so the single
803
+ // focus-driven highlight follows the pointer instead of leaving two.
804
+ fireEvent.pointerMove(del, {pointerType: 'mouse'});
805
+ expect(del).toHaveFocus();
806
+ expect(edit).not.toHaveFocus();
807
+ });
808
+
809
+ it('does not move focus on hover for a disabled item', async () => {
810
+ const user = userEvent.setup();
811
+ render(
812
+ <DropdownMenu button={{label: 'Actions'}}>
813
+ <DropdownMenuItem label="Edit" onClick={() => {}} />
814
+ <DropdownMenuItem label="Delete" isDisabled onClick={() => {}} />
815
+ </DropdownMenu>,
816
+ );
817
+
818
+ await user.click(screen.getByRole('button', {name: /Actions/}));
819
+ const edit = screen.getByRole('menuitem', {name: 'Edit', hidden: true});
820
+ const del = screen.getByRole('menuitem', {name: 'Delete', hidden: true});
821
+ edit.focus();
822
+
823
+ fireEvent.pointerMove(del, {pointerType: 'mouse'});
824
+ expect(edit).toHaveFocus();
825
+ });
826
+
827
+ it('does not move focus for a non-mouse (touch) pointer', async () => {
828
+ const user = userEvent.setup();
829
+ render(
830
+ <DropdownMenu button={{label: 'Actions'}}>
831
+ <DropdownMenuItem label="Edit" onClick={() => {}} />
832
+ <DropdownMenuItem label="Delete" onClick={() => {}} />
833
+ </DropdownMenu>,
834
+ );
835
+
836
+ await user.click(screen.getByRole('button', {name: /Actions/}));
837
+ const edit = screen.getByRole('menuitem', {name: 'Edit', hidden: true});
838
+ const del = screen.getByRole('menuitem', {name: 'Delete', hidden: true});
839
+ edit.focus();
840
+
841
+ fireEvent.pointerMove(del, {pointerType: 'touch'});
842
+ expect(edit).toHaveFocus();
843
+ });
784
844
  });
@@ -38,7 +38,11 @@ import {Icon} from '../Icon';
38
38
  import type {IconType} from '../Icon';
39
39
 
40
40
  import {renderDropdownItems} from './renderDropdownItems';
41
- import {MENU_ITEM_ROLES, MENU_ITEM_SELECTOR} from './menuItemRoles';
41
+ import {
42
+ MENU_ITEM_ROLES,
43
+ MENU_ITEM_SELECTOR,
44
+ MENU_BOUNDARY_SELECTOR,
45
+ } from './menuItemRoles';
42
46
  import {
43
47
  DropdownMenuContext,
44
48
  type DropdownMenuContextValue,
@@ -100,6 +104,12 @@ export interface DropdownMenuItemData {
100
104
  onClick?: () => void;
101
105
  isDisabled?: boolean;
102
106
  icon?: ReactNode | IconType;
107
+ /**
108
+ * Nested submenu entries. When present, this row becomes a submenu (a
109
+ * flyout revealing `items`) instead of a leaf action — no separate item
110
+ * "type" is needed. Data-mode parity for the compound DropdownMenuSubMenu API.
111
+ */
112
+ items?: DropdownMenuOption[];
103
113
  }
104
114
 
105
115
  export interface DropdownMenuDivider {
@@ -200,6 +210,14 @@ export function DropdownMenu({
200
210
  const items = ('items' in props ? props.items : undefined) ?? [];
201
211
  const children = props.children;
202
212
 
213
+ // Extract BaseProps pass-throughs (aria-*, id, event handlers) from the
214
+ // discriminated-union rest bag so they can be forwarded to the menu element.
215
+ const {
216
+ items: _items,
217
+ children: _children,
218
+ ...rest
219
+ } = props as Record<string, unknown>;
220
+
203
221
  const menuId = useId();
204
222
  const menuSize = button.size ?? 'md';
205
223
  const buttonRef = useRef<HTMLButtonElement>(null);
@@ -265,23 +283,18 @@ export function DropdownMenu({
265
283
  handleKeyDown: listNavKeyDown,
266
284
  focusFirst,
267
285
  focusItem,
286
+ ownsEvent,
287
+ getItems: getMenuItems,
268
288
  } = useListFocus<HTMLDivElement>({
269
289
  itemSelector: MENU_ITEM_SELECTOR,
290
+ boundarySelector: MENU_BOUNDARY_SELECTOR,
270
291
  wrap: false,
271
292
  onEscape: closeMenu,
272
293
  });
273
294
 
274
295
  // First-character typeahead over the (enabled) menu items — jump to the next
275
- // item whose label starts with the typed text (menus-11).
276
- const getMenuItems = useCallback(
277
- (): HTMLElement[] =>
278
- listRef.current
279
- ? Array.from(
280
- listRef.current.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR),
281
- )
282
- : [],
283
- [listRef],
284
- );
296
+ // item whose label starts with the typed text (menus-11). Reuses the hook's
297
+ // scoped item collection so an inline submenu flyout's items aren't swept in.
285
298
  const typeahead = useTypeahead({
286
299
  getItemLabels: () => getMenuItems().map(el => el.textContent),
287
300
  onMatch: focusItem,
@@ -307,6 +320,11 @@ export function DropdownMenu({
307
320
  // Extend useListFocus with Enter/Space activation + typeahead
308
321
  const listKeyDown = useCallback(
309
322
  (e: React.KeyboardEvent) => {
323
+ // A submenu flyout renders inline inside this menu; its key events bubble
324
+ // up here. Let that level own them — only handle events from this level.
325
+ if (!ownsEvent(e)) {
326
+ return;
327
+ }
310
328
  if (e.key === 'Enter' || e.key === ' ') {
311
329
  e.preventDefault();
312
330
  const focused = document.activeElement as HTMLElement | null;
@@ -334,7 +352,7 @@ export function DropdownMenu({
334
352
  }
335
353
  listNavKeyDown(e);
336
354
  },
337
- [listNavKeyDown, closeMenu, typeahead],
355
+ [listNavKeyDown, closeMenu, typeahead, ownsEvent],
338
356
  );
339
357
 
340
358
  const openAndFocus = useCallback(() => {
@@ -435,6 +453,7 @@ export function DropdownMenu({
435
453
 
436
454
  {popover.render(
437
455
  <div
456
+ {...rest}
438
457
  ref={listRef}
439
458
  id={menuId}
440
459
  role="menu"
@@ -23,11 +23,12 @@
23
23
  * checkmark uses the `check` icon from the active theme's icon registry.
24
24
  */
25
25
 
26
- import {useCallback, type ReactNode} from 'react';
26
+ import {useCallback, type PointerEvent, type ReactNode} from 'react';
27
27
  import * as stylex from '@stylexjs/stylex';
28
28
  import {Icon, renderIconSlot, type IconType} from '../Icon';
29
29
  import {Item} from '../Item';
30
30
  import {useDropdownMenuContext} from './DropdownMenuContext';
31
+ import {focusMenuItemOnHover} from './menuItemHover';
31
32
  import {
32
33
  colorVars,
33
34
  spacingVars,
@@ -47,7 +48,6 @@ const styles = stylex.create({
47
48
  backgroundColor: {
48
49
  default: 'transparent',
49
50
  ':focus': colorVars['--color-overlay-hover'],
50
- ':hover': colorVars['--color-overlay-hover'],
51
51
  },
52
52
  cursor: 'pointer',
53
53
  outline: 'none',
@@ -190,12 +190,18 @@ export function DropdownMenuCheckboxItem({
190
190
  }
191
191
  }, [isDisabled, onChange, value, hasCloseOnSelect, ctx]);
192
192
 
193
+ const handlePointerMove = useCallback(
194
+ (e: PointerEvent<HTMLElement>) => focusMenuItemOnHover(e, isDisabled),
195
+ [isDisabled],
196
+ );
197
+
193
198
  return (
194
199
  <Item
195
200
  {...rest}
196
201
  role="menuitemcheckbox"
197
202
  aria-checked={value}
198
203
  tabIndex={isDisabled ? undefined : -1}
204
+ onPointerMove={handlePointerMove}
199
205
  marker={
200
206
  <span
201
207
  aria-hidden="true"
@@ -23,7 +23,7 @@
23
23
  * - /packages/cli/templates/blocks/components/DropdownMenu/ (showcase blocks)
24
24
  */
25
25
 
26
- import {useCallback, type ReactNode} from 'react';
26
+ import {useCallback, type PointerEvent, type ReactNode} from 'react';
27
27
  import * as stylex from '@stylexjs/stylex';
28
28
  import {renderIconSlot, type IconType} from '../Icon';
29
29
  import {Item} from '../Item';
@@ -36,6 +36,7 @@ import {
36
36
  import {mergeProps} from '../utils';
37
37
  import type {BaseProps} from '../BaseProps';
38
38
  import {useDropdownMenuContext} from './DropdownMenuContext';
39
+ import {focusMenuItemOnHover} from './menuItemHover';
39
40
  import {themeProps} from '../utils/themeProps';
40
41
 
41
42
  const menuItemStyles = stylex.create({
@@ -51,11 +52,10 @@ const menuItemStyles = stylex.create({
51
52
  backgroundColor: {
52
53
  default: 'transparent',
53
54
  ':focus': colorVars['--color-overlay-hover'],
54
- ':hover': colorVars['--color-overlay-hover'],
55
55
  },
56
56
  border: 'none',
57
57
  cursor: 'pointer',
58
- textAlign: 'left',
58
+ textAlign: 'start',
59
59
  outline: 'none',
60
60
  },
61
61
  disabled: {
@@ -129,10 +129,16 @@ export function DropdownMenuItem({
129
129
  ctx?.closeMenu();
130
130
  }, [isDisabled, onClick, ctx]);
131
131
 
132
+ const handlePointerMove = useCallback(
133
+ (e: PointerEvent<HTMLElement>) => focusMenuItemOnHover(e, isDisabled),
134
+ [isDisabled],
135
+ );
136
+
132
137
  return (
133
138
  <Item
134
139
  role="menuitem"
135
140
  tabIndex={isDisabled ? undefined : -1}
141
+ onPointerMove={handlePointerMove}
136
142
  startContent={
137
143
  icon
138
144
  ? renderIconSlot(icon, {size: 'sm', color: 'secondary'})
@@ -22,16 +22,10 @@ export const docs = {
22
22
  description: 'Callback fired when the selected value changes.',
23
23
  },
24
24
  {
25
- name: 'aria-label',
25
+ name: 'label',
26
26
  type: 'string',
27
27
  description:
28
- 'Accessible label for the group. Required (or aria-labelledby) so screen readers announce the radios as a named set, e.g. "Sort by".',
29
- },
30
- {
31
- name: 'aria-labelledby',
32
- type: 'string',
33
- description:
34
- 'The id of an element that labels the group, as an alternative to aria-label.',
28
+ 'Accessible name for the group, applied as aria-label so screen readers announce the radios as a named set, e.g. "Sort by". Required. Pass aria-labelledby (via base props) instead when the name already exists as a visible element.',
35
29
  },
36
30
  {
37
31
  name: 'hasCloseOnSelect',
@@ -57,8 +51,7 @@ export const docsZh = {
57
51
  propDescriptions: {
58
52
  value: '组中当前选中的值。尚无选中项时传 undefined。',
59
53
  onChange: '所选值变化时触发的回调。',
60
- 'aria-label': '组的无障碍标签。必需(或 aria-labelledby),以便屏幕阅读器将单选项作为命名集合朗读。',
61
- 'aria-labelledby': '标记该组的元素 id,作为 aria-label 的替代。',
54
+ label: '组的无障碍名称,作为 aria-label 应用,以便屏幕阅读器将单选项作为命名集合朗读,例如“Sort by”。必需。当名称已作为可见元素存在时,可改用 aria-labelledby(通过基础属性)。',
62
55
  hasCloseOnSelect: '选择某值是否关闭菜单。默认关闭。',
63
56
  children: '组成该组的 DropdownMenuRadioItem。',
64
57
  },
@@ -72,8 +65,7 @@ export const docsDense = {
72
65
  propDescriptions: {
73
66
  value: 'selected value (undefined = none)',
74
67
  onChange: 'fired when selected value changes',
75
- 'aria-label': 'accessible group name (or aria-labelledby); required',
76
- 'aria-labelledby': 'id of labelling element (alt to aria-label)',
68
+ label: 'accessible group name (applied as aria-label); required',
77
69
  hasCloseOnSelect: 'close menu on select (default true)',
78
70
  children: 'the DropdownMenuRadioItems',
79
71
  },
@@ -49,16 +49,13 @@ export interface DropdownMenuRadioGroupProps extends Omit<
49
49
  */
50
50
  onChange: (value: string) => void;
51
51
  /**
52
- * Accessible label for the group. Required (together with `aria-labelledby`,
53
- * one of the two) so screen readers announce the radios as a named set,
54
- * e.g. "Sort by".
52
+ * Accessible name for the group, announced by screen readers so the radios
53
+ * read as a named set (e.g. "Sort by"). Applied as the group's `aria-label`.
54
+ * Required -- an unnamed radio group is an accessibility defect. Pass
55
+ * `aria-labelledby` (via base props) instead if the name already exists as a
56
+ * visible element on the page.
55
57
  */
56
- 'aria-label'?: string;
57
- /**
58
- * The id of an element that labels the group, as an alternative to
59
- * `aria-label`.
60
- */
61
- 'aria-labelledby'?: string;
58
+ label: string;
62
59
  /**
63
60
  * Whether selecting a value closes the menu. Radio items default to closing
64
61
  * on selection (a single-choice commit), unlike checkbox items which stay
@@ -82,7 +79,7 @@ export interface DropdownMenuRadioGroupProps extends Omit<
82
79
  * DropdownMenuRadioItem,
83
80
  * } from '@astryxdesign/core/DropdownMenu';
84
81
  * <DropdownMenu button={{label: 'Sort'}}>
85
- * <DropdownMenuRadioGroup value={sort} onChange={setSort} aria-label="Sort by">
82
+ * <DropdownMenuRadioGroup value={sort} onChange={setSort} label="Sort by">
86
83
  * <DropdownMenuRadioItem value="newest" label="Newest" />
87
84
  * <DropdownMenuRadioItem value="oldest" label="Oldest" />
88
85
  * </DropdownMenuRadioGroup>
@@ -92,6 +89,7 @@ export interface DropdownMenuRadioGroupProps extends Omit<
92
89
  export function DropdownMenuRadioGroup({
93
90
  value,
94
91
  onChange,
92
+ label,
95
93
  hasCloseOnSelect = true,
96
94
  children,
97
95
  className,
@@ -107,6 +105,7 @@ export function DropdownMenuRadioGroup({
107
105
  <div
108
106
  {...rest}
109
107
  role="group"
108
+ aria-label={label}
110
109
  {...mergeProps(stylex.props(styles.group), {className, style})}>
111
110
  <DropdownMenuRadioGroupContext value={contextValue}>
112
111
  {children}
@@ -21,11 +21,12 @@
21
21
  * has no registry glyph).
22
22
  */
23
23
 
24
- import {useCallback, type ReactNode} from 'react';
24
+ import {useCallback, type PointerEvent, type ReactNode} from 'react';
25
25
  import * as stylex from '@stylexjs/stylex';
26
26
  import {renderIconSlot, type IconType} from '../Icon';
27
27
  import {Item} from '../Item';
28
28
  import {useDropdownMenuContext} from './DropdownMenuContext';
29
+ import {focusMenuItemOnHover} from './menuItemHover';
29
30
  import {
30
31
  colorVars,
31
32
  spacingVars,
@@ -45,7 +46,6 @@ const styles = stylex.create({
45
46
  backgroundColor: {
46
47
  default: 'transparent',
47
48
  ':focus': colorVars['--color-overlay-hover'],
48
- ':hover': colorVars['--color-overlay-hover'],
49
49
  },
50
50
  cursor: 'pointer',
51
51
  outline: 'none',
@@ -145,7 +145,7 @@ export interface DropdownMenuRadioItemProps extends Omit<
145
145
  *
146
146
  * @example
147
147
  * ```
148
- * <DropdownMenuRadioGroup value={sort} onChange={setSort} aria-label="Sort by">
148
+ * <DropdownMenuRadioGroup value={sort} onChange={setSort} label="Sort by">
149
149
  * <DropdownMenuRadioItem value="newest" label="Newest" />
150
150
  * <DropdownMenuRadioItem value="oldest" label="Oldest" icon="clock" />
151
151
  * </DropdownMenuRadioGroup>
@@ -184,12 +184,18 @@ export function DropdownMenuRadioItem({
184
184
  }
185
185
  }, [isDisabled, groupCtx, value, menuCtx]);
186
186
 
187
+ const handlePointerMove = useCallback(
188
+ (e: PointerEvent<HTMLElement>) => focusMenuItemOnHover(e, isDisabled),
189
+ [isDisabled],
190
+ );
191
+
187
192
  return (
188
193
  <Item
189
194
  {...rest}
190
195
  role="menuitemradio"
191
196
  aria-checked={isChecked}
192
197
  tabIndex={isDisabled ? undefined : -1}
198
+ onPointerMove={handlePointerMove}
193
199
  marker={
194
200
  <span
195
201
  aria-hidden="true"
@@ -110,7 +110,7 @@ describe('DropdownMenuRadioGroup / RadioItem', () => {
110
110
  <DropdownMenuRadioGroup
111
111
  value="newest"
112
112
  onChange={() => {}}
113
- aria-label="Sort by">
113
+ label="Sort by">
114
114
  <DropdownMenuRadioItem value="newest" label="Newest" />
115
115
  <DropdownMenuRadioItem value="oldest" label="Oldest" />
116
116
  </DropdownMenuRadioGroup>
@@ -136,7 +136,7 @@ describe('DropdownMenuRadioGroup / RadioItem', () => {
136
136
  <DropdownMenuRadioGroup
137
137
  value="newest"
138
138
  onChange={onChange}
139
- aria-label="Sort by">
139
+ label="Sort by">
140
140
  <DropdownMenuRadioItem value="newest" label="Newest" />
141
141
  <DropdownMenuRadioItem value="oldest" label="Oldest" />
142
142
  </DropdownMenuRadioGroup>
@@ -149,6 +149,24 @@ describe('DropdownMenuRadioGroup / RadioItem', () => {
149
149
  expect(onChange).toHaveBeenCalledWith('oldest');
150
150
  });
151
151
 
152
+ it('names the group from the required label prop', async () => {
153
+ const user = userEvent.setup();
154
+ render(
155
+ <DropdownMenu button={{label: 'Sort'}}>
156
+ <DropdownMenuRadioGroup
157
+ value="newest"
158
+ onChange={() => {}}
159
+ label="Sort by">
160
+ <DropdownMenuRadioItem value="newest" label="Newest" />
161
+ </DropdownMenuRadioGroup>
162
+ </DropdownMenu>,
163
+ );
164
+ await user.click(screen.getByRole('button', {name: /Sort/}));
165
+ expect(
166
+ screen.getByRole('group', {name: 'Sort by', hidden: true}),
167
+ ).toBeInTheDocument();
168
+ });
169
+
152
170
  it('throws when a radio item is used outside a group', () => {
153
171
  const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
154
172
  expect(() =>
@@ -0,0 +1,88 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../docs-types').ComponentDoc} */
4
+
5
+ export const docs = {
6
+ name: 'DropdownMenuSubMenu',
7
+ subComponentOf: 'DropdownMenu',
8
+ displayName: 'Dropdown Menu Submenu',
9
+ isHiddenFromOverview: true,
10
+ description:
11
+ 'A single menu row that reveals a nested flyout of its own items. The row adopts DropdownMenuItem semantics (label / icon / description / isDisabled); its children become the flyout content. Opens inline-end with viewport auto-flip; Right/Enter/Space opens and focuses the first item, Left/Escape closes and returns focus to the trigger (Right/Left swap in RTL). For data-driven menus, give a menu item a nested `items` array instead of using this component directly.',
12
+ playground: {
13
+ defaults: {label: 'Move to'},
14
+ },
15
+ props: [
16
+ {
17
+ name: 'label',
18
+ type: 'ReactNode',
19
+ description: 'Primary label text for the trigger row.',
20
+ },
21
+ {
22
+ name: 'icon',
23
+ type: 'IconType',
24
+ description:
25
+ 'Icon to display before the label. See `astryx docs icons` for valid semantic names.',
26
+ },
27
+ {
28
+ name: 'description',
29
+ type: 'ReactNode',
30
+ description: 'Secondary description text displayed below the label.',
31
+ },
32
+ {
33
+ name: 'children',
34
+ type: 'ReactNode',
35
+ description:
36
+ 'The flyout menu items: the same components used at the top level (DropdownMenuItem, nested DropdownMenuSubMenu, selectable items).',
37
+ },
38
+ {
39
+ name: 'isDisabled',
40
+ type: 'boolean',
41
+ default: 'false',
42
+ description:
43
+ 'A disabled submenu renders its trigger row but never opens the flyout.',
44
+ },
45
+ {
46
+ name: 'hasSpinner',
47
+ type: 'boolean',
48
+ default: 'false',
49
+ description:
50
+ 'Show a spinner in place of the caret, e.g. while a lazy submenu\'s children are loading.',
51
+ },
52
+ {
53
+ name: 'menuWidth',
54
+ type: 'number | string',
55
+ description: 'Fixed flyout width. Defaults to sizing to its content (min 160px).',
56
+ },
57
+ {
58
+ name: 'onOpenChange',
59
+ type: '(isOpen: boolean) => void',
60
+ description: 'Called when the flyout opens or closes.',
61
+ },
62
+ {
63
+ name: 'xstyle',
64
+ type: 'StyleXStyles',
65
+ description:
66
+ 'StyleX styles for the trigger row. Must be a stylex.create() value: not an inline style object like style={{}}.',
67
+ },
68
+ ],
69
+ };
70
+
71
+ export const docsDense = {
72
+ name: 'DropdownMenuSubMenu',
73
+ isHiddenFromOverview: true,
74
+ displayName: 'Dropdown Menu Submenu',
75
+ description:
76
+ 'menu row that reveals a nested flyout of its own children/items (single component, not Sub/SubTrigger/SubContent)',
77
+ propDescriptions: {
78
+ label: 'primary label text for the trigger row',
79
+ icon: 'icon before label',
80
+ description: 'secondary text below label',
81
+ children: 'flyout menu items (same components as top level)',
82
+ isDisabled: 'renders trigger but never opens',
83
+ hasSpinner: 'spinner instead of caret for async children',
84
+ menuWidth: 'fixed flyout width (default: content, min 160px)',
85
+ onOpenChange: 'called when flyout opens/closes',
86
+ xstyle: 'StyleX styles for the trigger row',
87
+ },
88
+ };