@astryxdesign/core 0.7.1 → 0.7.2-canary.020e1fe

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 (377) hide show
  1. package/dist/AppShell/AppShell.d.ts.map +1 -1
  2. package/dist/AppShell/AppShell.js +40 -1
  3. package/dist/AspectRatio/AspectRatio.d.ts +6 -8
  4. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  5. package/dist/AspectRatio/AspectRatio.js +44 -3
  6. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  7. package/dist/Breadcrumbs/BreadcrumbItem.js +5 -2
  8. package/dist/Button/Button.d.ts.map +1 -1
  9. package/dist/Button/Button.js +38 -14
  10. package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
  11. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  12. package/dist/CheckboxInput/CheckboxInput.js +3 -1
  13. package/dist/CheckboxList/CheckboxListItem.d.ts +9 -1
  14. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  15. package/dist/CheckboxList/CheckboxListItem.js +4 -2
  16. package/dist/CommandPalette/CommandPaletteInput.d.ts +1 -1
  17. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  18. package/dist/CommandPalette/CommandPaletteInput.js +37 -5
  19. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  20. package/dist/ContextMenu/ContextMenu.js +5 -2
  21. package/dist/Dialog/Dialog.d.ts.map +1 -1
  22. package/dist/Dialog/Dialog.js +20 -8
  23. package/dist/Drawer/Drawer.d.ts +191 -0
  24. package/dist/Drawer/Drawer.d.ts.map +1 -0
  25. package/dist/Drawer/Drawer.js +382 -0
  26. package/dist/Drawer/DrawerHeader.d.ts +85 -0
  27. package/dist/Drawer/DrawerHeader.d.ts.map +1 -0
  28. package/dist/Drawer/DrawerHeader.js +134 -0
  29. package/dist/Drawer/index.d.ts +11 -0
  30. package/dist/Drawer/index.d.ts.map +1 -0
  31. package/dist/Drawer/index.js +11 -0
  32. package/dist/Drawer/useDrawerDialogPresence.d.ts +42 -0
  33. package/dist/Drawer/useDrawerDialogPresence.d.ts.map +1 -0
  34. package/dist/Drawer/useDrawerDialogPresence.js +236 -0
  35. package/dist/DropdownMenu/DropdownMenu.d.ts +1 -1
  36. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  37. package/dist/DropdownMenu/DropdownMenu.js +83 -11
  38. package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -0
  39. package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
  40. package/dist/DropdownMenu/DropdownMenuContext.js +21 -0
  41. package/dist/DropdownMenu/DropdownMenuDivider.d.ts +5 -3
  42. package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -1
  43. package/dist/DropdownMenu/DropdownMenuDivider.js +7 -3
  44. package/dist/DropdownMenu/DropdownMenuGroup.d.ts +6 -4
  45. package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
  46. package/dist/DropdownMenu/DropdownMenuGroup.js +4 -3
  47. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  48. package/dist/DropdownMenu/DropdownMenuSubMenu.js +19 -8
  49. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +24 -0
  50. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -0
  51. package/dist/DropdownMenu/lazyMenuBottomSheet.js +46 -0
  52. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  53. package/dist/EmptyState/EmptyState.js +4 -10
  54. package/dist/Field/Field.d.ts +1 -1
  55. package/dist/Field/Field.d.ts.map +1 -1
  56. package/dist/Field/Field.js +8 -4
  57. package/dist/Grid/GridSpan.d.ts.map +1 -1
  58. package/dist/Grid/GridSpan.js +1 -0
  59. package/dist/Heading/Heading.d.ts.map +1 -1
  60. package/dist/Heading/Heading.js +4 -2
  61. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  62. package/dist/HoverCard/useHoverCard.js +10 -2
  63. package/dist/Item/Item.d.ts +6 -2
  64. package/dist/Item/Item.d.ts.map +1 -1
  65. package/dist/Item/Item.js +19 -4
  66. package/dist/Layer/layerViewportInset.stylex.d.ts +6 -2
  67. package/dist/Layer/layerViewportInset.stylex.d.ts.map +1 -1
  68. package/dist/Layer/layerViewportInset.stylex.js +6 -2
  69. package/dist/Layer/useLayer.d.ts.map +1 -1
  70. package/dist/Layer/useLayer.js +13 -7
  71. package/dist/Layer/useLayerDismissal.d.ts +1 -1
  72. package/dist/Layer/useLayerDismissal.js +1 -1
  73. package/dist/Layout/container.stylex.d.ts +5 -5
  74. package/dist/Layout/container.stylex.js +4 -4
  75. package/dist/List/List.d.ts +46 -19
  76. package/dist/List/List.d.ts.map +1 -1
  77. package/dist/List/List.js +28 -10
  78. package/dist/List/ListContext.d.ts +8 -3
  79. package/dist/List/ListContext.d.ts.map +1 -1
  80. package/dist/List/ListContext.js +7 -2
  81. package/dist/List/ListItem.d.ts +13 -5
  82. package/dist/List/ListItem.d.ts.map +1 -1
  83. package/dist/List/ListItem.js +21 -8
  84. package/dist/List/index.d.ts +1 -1
  85. package/dist/List/index.d.ts.map +1 -1
  86. package/dist/Markdown/Markdown.d.ts.map +1 -1
  87. package/dist/Markdown/Markdown.js +8 -1
  88. package/dist/Markdown/plugins/frontmatter.d.ts.map +1 -1
  89. package/dist/Markdown/plugins/frontmatter.js +7 -3
  90. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  91. package/dist/MetadataList/MetadataList.js +5 -1
  92. package/dist/MetadataList/MetadataListContext.d.ts +1 -1
  93. package/dist/MetadataList/MetadataListContext.d.ts.map +1 -1
  94. package/dist/MobileNav/MobileNav.d.ts +9 -0
  95. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  96. package/dist/MobileNav/MobileNav.js +30 -3
  97. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  98. package/dist/MultiSelector/MultiSelector.js +22 -8
  99. package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +100 -2
  100. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  101. package/dist/MultiSelector/hooks.js +3 -1
  102. package/dist/Pagination/Pagination.d.ts +8 -5
  103. package/dist/Pagination/Pagination.d.ts.map +1 -1
  104. package/dist/Pagination/Pagination.js +21 -8
  105. package/dist/Popover/Popover.js +2 -2
  106. package/dist/PowerSearch/PowerSearch.js +1 -1
  107. package/dist/RadioList/RadioList.d.ts +1 -1
  108. package/dist/RadioList/RadioList.d.ts.map +1 -1
  109. package/dist/RadioList/RadioList.js +6 -2
  110. package/dist/RadioList/RadioListItem.d.ts +9 -1
  111. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  112. package/dist/RadioList/RadioListItem.js +2 -0
  113. package/dist/Selector/hooks.d.ts.map +1 -1
  114. package/dist/Selector/hooks.js +6 -2
  115. package/dist/SideNav/SideNavItem.d.ts +8 -1
  116. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  117. package/dist/SideNav/SideNavItem.js +71 -9
  118. package/dist/Switch/Switch.d.ts +1 -1
  119. package/dist/Switch/Switch.d.ts.map +1 -1
  120. package/dist/Switch/Switch.js +13 -1
  121. package/dist/TabList/TabMenu.d.ts +2 -2
  122. package/dist/TabList/TabMenu.d.ts.map +1 -1
  123. package/dist/TabList/TabMenu.js +6 -3
  124. package/dist/Text/Text.d.ts.map +1 -1
  125. package/dist/Text/Text.js +4 -2
  126. package/dist/Text/useTruncation.d.ts +7 -5
  127. package/dist/Text/useTruncation.d.ts.map +1 -1
  128. package/dist/Text/useTruncation.js +16 -5
  129. package/dist/ToggleButton/ToggleButton.d.ts +3 -2
  130. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  131. package/dist/ToggleButton/ToggleButton.js +12 -1
  132. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  133. package/dist/Tooltip/useTooltip.js +11 -0
  134. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  135. package/dist/TopNav/TopNavMegaMenu.js +3 -3
  136. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  137. package/dist/TopNav/TopNavMenu.js +3 -3
  138. package/dist/TreeList/TreeList.d.ts.map +1 -1
  139. package/dist/TreeList/TreeList.js +1 -0
  140. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  141. package/dist/TreeList/TreeListBranches.js +2 -2
  142. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  143. package/dist/TreeList/TreeListItem.js +57 -9
  144. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  145. package/dist/Typeahead/BaseTypeahead.js +26 -0
  146. package/dist/astryx.css +47 -13
  147. package/dist/hooks/useEntryAnimation.d.ts +2 -1
  148. package/dist/hooks/useEntryAnimation.d.ts.map +1 -1
  149. package/dist/hooks/useEntryAnimation.js +14 -12
  150. package/dist/hooks/useIsHydrating.d.ts +6 -0
  151. package/dist/hooks/useIsHydrating.d.ts.map +1 -0
  152. package/dist/hooks/useIsHydrating.js +30 -0
  153. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  154. package/dist/hooks/useMenuHover.js +18 -13
  155. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  156. package/dist/i18n/generated-locales/en.generated.js +112 -1
  157. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  158. package/dist/i18n/generated-locales/pseudo.generated.js +112 -1
  159. package/dist/index.d.ts +1 -0
  160. package/dist/index.d.ts.map +1 -1
  161. package/dist/index.js +1 -0
  162. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  163. package/dist/theme/derivedVarRegistry.js +0 -3
  164. package/dist/utils/focusOutline.stylex.d.ts +4 -0
  165. package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
  166. package/dist/utils/focusOutline.stylex.js +4 -0
  167. package/dist/utils/plainDate.d.ts.map +1 -1
  168. package/dist/utils/plainDate.js +15 -9
  169. package/dist/utils/sharedMutationObserver.d.ts +21 -0
  170. package/dist/utils/sharedMutationObserver.d.ts.map +1 -0
  171. package/dist/utils/sharedMutationObserver.js +69 -0
  172. package/locales/en.json +444 -0
  173. package/locales/pseudo.json +333 -0
  174. package/package.json +9 -4
  175. package/scripts/agent-doc-state.mjs +38 -0
  176. package/scripts/postinstall.mjs +1 -1
  177. package/src/AppShell/AppShell.tsx +45 -1
  178. package/src/AspectRatio/AspectRatio.test.tsx +44 -43
  179. package/src/AspectRatio/AspectRatio.tsx +38 -18
  180. package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +157 -0
  181. package/src/Avatar/Avatar.doc.mjs +5 -3
  182. package/src/AvatarGroup/AvatarGroup.doc.mjs +3 -2
  183. package/src/Breadcrumbs/BreadcrumbItem.tsx +4 -1
  184. package/src/Breadcrumbs/Breadcrumbs.test.tsx +30 -1
  185. package/src/Button/Button.doc.mjs +2 -2
  186. package/src/Button/Button.test.tsx +155 -9
  187. package/src/Button/Button.tsx +39 -16
  188. package/src/Button/__tests__/Button.a11y.known-failures.ts +1 -62
  189. package/src/Button/__tests__/Button.a11y.states.ts +0 -10
  190. package/src/Card/Card.doc.mjs +1 -1
  191. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +3 -1
  192. package/src/CheckboxInput/CheckboxInput.test.tsx +34 -0
  193. package/src/CheckboxInput/CheckboxInput.tsx +3 -1
  194. package/src/CheckboxList/CheckboxList.test.tsx +22 -0
  195. package/src/CheckboxList/CheckboxListItem.doc.mjs +16 -0
  196. package/src/CheckboxList/CheckboxListItem.tsx +10 -0
  197. package/src/Code/Code.doc.mjs +1 -0
  198. package/src/CommandPalette/CommandPalette.doc.mjs +2 -0
  199. package/src/CommandPalette/CommandPaletteInput.test.tsx +53 -4
  200. package/src/CommandPalette/CommandPaletteInput.tsx +33 -3
  201. package/src/CommandPalette/__tests__/CommandPaletteInputAudit.a11y.chromium.spec.ts +335 -0
  202. package/src/CommandPalette/__tests__/CommandPaletteItemAudit.a11y.chromium.spec.ts +292 -0
  203. package/src/ContextMenu/ContextMenu.doc.mjs +2 -3
  204. package/src/ContextMenu/ContextMenu.tsx +4 -1
  205. package/src/ContextMenu/__tests__/ContextMenuAudit.a11y.chromium.spec.ts +438 -0
  206. package/src/Dialog/Dialog.test.tsx +46 -0
  207. package/src/Dialog/Dialog.tsx +20 -7
  208. package/src/Dialog/__tests__/Dialog.a11y.known-failures.ts +0 -14
  209. package/src/Drawer/Drawer.doc.mjs +473 -0
  210. package/src/Drawer/Drawer.spec.md +357 -0
  211. package/src/Drawer/Drawer.test.tsx +1300 -0
  212. package/src/Drawer/Drawer.tsx +613 -0
  213. package/src/Drawer/DrawerDirection.test.tsx +127 -0
  214. package/src/Drawer/DrawerHeader.doc.mjs +293 -0
  215. package/src/Drawer/DrawerHeader.test.tsx +102 -0
  216. package/src/Drawer/DrawerHeader.tsx +235 -0
  217. package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +401 -0
  218. package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +297 -0
  219. package/src/Drawer/index.ts +13 -0
  220. package/src/Drawer/useDrawerDialogPresence.ts +284 -0
  221. package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +57 -0
  222. package/src/DropdownMenu/DropdownMenu.test.tsx +79 -18
  223. package/src/DropdownMenu/DropdownMenu.tsx +83 -12
  224. package/src/DropdownMenu/DropdownMenuContext.tsx +24 -0
  225. package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +2 -2
  226. package/src/DropdownMenu/DropdownMenuDivider.spec.md +192 -0
  227. package/src/DropdownMenu/DropdownMenuDivider.tsx +9 -5
  228. package/src/DropdownMenu/DropdownMenuGroup.doc.mjs +169 -10
  229. package/src/DropdownMenu/DropdownMenuGroup.tsx +6 -4
  230. package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +126 -0
  231. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +185 -0
  232. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +19 -5
  233. package/src/DropdownMenu/__tests__/DropdownMenuDividerAudit.a11y.chromium.spec.ts +280 -0
  234. package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +334 -0
  235. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +145 -0
  236. package/src/DropdownMenu/__tests__/Menu.a11y.known-failures.ts +26 -0
  237. package/src/DropdownMenu/__tests__/Menu.a11y.renders.tsx +65 -0
  238. package/src/DropdownMenu/__tests__/Menu.a11y.states.ts +138 -0
  239. package/src/DropdownMenu/__tests__/Menu.a11y.test.tsx +136 -0
  240. package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +43 -0
  241. package/src/DropdownMenu/lazyMenuBottomSheet.ts +71 -0
  242. package/src/EmptyState/EmptyState.doc.mjs +3 -2
  243. package/src/EmptyState/EmptyState.spec.md +189 -0
  244. package/src/EmptyState/EmptyState.test.tsx +74 -3
  245. package/src/EmptyState/EmptyState.tsx +4 -8
  246. package/src/Field/Field.doc.mjs +14 -8
  247. package/src/Field/Field.spec.md +217 -0
  248. package/src/Field/Field.test.tsx +60 -2
  249. package/src/Field/Field.tsx +17 -8
  250. package/src/Field/FieldLabel.doc.mjs +133 -26
  251. package/src/Field/FieldLabel.spec.md +172 -0
  252. package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +296 -0
  253. package/src/Grid/GridSpan.tsx +3 -0
  254. package/src/Heading/Heading.test.tsx +57 -0
  255. package/src/Heading/Heading.tsx +6 -2
  256. package/src/HoverCard/HoverCard.test.tsx +58 -0
  257. package/src/HoverCard/useHoverCard.tsx +10 -2
  258. package/src/Icon/Icon.doc.mjs +36 -0
  259. package/src/IconButton/IconButton.doc.mjs +2 -2
  260. package/src/IconButton/IconButton.test.tsx +3 -1
  261. package/src/Item/Item.doc.mjs +12 -8
  262. package/src/Item/Item.spec.md +226 -0
  263. package/src/Item/Item.test.tsx +61 -0
  264. package/src/Item/Item.tsx +40 -4
  265. package/src/Layer/layerViewportInset.source-build.test.mjs +90 -0
  266. package/src/Layer/layerViewportInset.stylex.ts +6 -2
  267. package/src/Layer/layerViewportInset.test.ts +1 -0
  268. package/src/Layer/useLayer.tsx +24 -13
  269. package/src/Layer/useLayerDismissal.ts +1 -1
  270. package/src/Layout/LayoutHeader.doc.mjs +5 -0
  271. package/src/Layout/container.stylex.ts +6 -6
  272. package/src/List/List.doc.mjs +14 -1
  273. package/src/List/List.spec.md +129 -77
  274. package/src/List/List.test.tsx +163 -94
  275. package/src/List/List.tsx +74 -18
  276. package/src/List/ListContext.tsx +9 -3
  277. package/src/List/ListItem.doc.mjs +24 -5
  278. package/src/List/ListItem.tsx +56 -27
  279. package/src/List/index.ts +1 -1
  280. package/src/Markdown/Markdown.tsx +14 -1
  281. package/src/Markdown/plugins/frontmatter.test.tsx +25 -0
  282. package/src/Markdown/plugins/frontmatter.ts +7 -3
  283. package/src/MetadataList/MetadataList.doc.mjs +3 -3
  284. package/src/MetadataList/MetadataList.test.tsx +38 -0
  285. package/src/MetadataList/MetadataList.tsx +8 -1
  286. package/src/MetadataList/MetadataListContext.tsx +1 -1
  287. package/src/MobileNav/MobileNav.tsx +30 -3
  288. package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +173 -15
  289. package/src/MoreMenu/MoreMenu.test.tsx +5 -1
  290. package/src/MultiSelector/MultiSelector.test.tsx +159 -0
  291. package/src/MultiSelector/MultiSelector.tsx +40 -7
  292. package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +112 -2
  293. package/src/MultiSelector/hooks.ts +3 -1
  294. package/src/MultiSelector/useMultiCombobox.doc.mjs +267 -0
  295. package/src/Pagination/Pagination.doc.mjs +7 -7
  296. package/src/Pagination/Pagination.spec.md +9 -9
  297. package/src/Pagination/Pagination.test.tsx +242 -0
  298. package/src/Pagination/Pagination.tsx +27 -11
  299. package/src/Popover/Popover.test.tsx +2 -2
  300. package/src/Popover/Popover.tsx +4 -4
  301. package/src/PowerSearch/PowerSearch.test.tsx +85 -0
  302. package/src/PowerSearch/PowerSearch.tsx +2 -2
  303. package/src/ProgressBar/ProgressBar.doc.mjs +1 -0
  304. package/src/RadioList/RadioList.fr10.test.tsx +45 -2
  305. package/src/RadioList/RadioList.test.tsx +22 -0
  306. package/src/RadioList/RadioList.tsx +4 -1
  307. package/src/RadioList/RadioListItem.doc.mjs +16 -0
  308. package/src/RadioList/RadioListItem.tsx +10 -1
  309. package/src/Resizable/useResizable.doc.mjs +2 -0
  310. package/src/ScrollableArea/ScrollableArea.doc.mjs +1 -1
  311. package/src/Selector/hooks.ts +6 -2
  312. package/src/Selector/useCombobox.doc.mjs +237 -0
  313. package/src/SideNav/SideNav.spec.md +227 -0
  314. package/src/SideNav/SideNav.test.tsx +179 -0
  315. package/src/SideNav/SideNavItem.doc.mjs +2 -2
  316. package/src/SideNav/SideNavItem.tsx +96 -17
  317. package/src/Slider/Slider.spec.md +39 -18
  318. package/src/Spinner/Spinner.doc.mjs +1 -1
  319. package/src/Switch/Switch.test.tsx +49 -0
  320. package/src/Switch/Switch.tsx +11 -0
  321. package/src/TabList/TabList.spec.md +57 -44
  322. package/src/TabList/TabList.test.tsx +67 -0
  323. package/src/TabList/TabMenu.doc.mjs +4 -3
  324. package/src/TabList/TabMenu.tsx +7 -4
  325. package/src/Text/Text.test.tsx +57 -0
  326. package/src/Text/Text.tsx +6 -2
  327. package/src/Text/useTruncation.test.tsx +69 -0
  328. package/src/Text/useTruncation.ts +16 -5
  329. package/src/TimeInput/TimeInput.fr10.test.tsx +15 -0
  330. package/src/Timestamp/Timestamp.doc.mjs +1 -1
  331. package/src/ToggleButton/ToggleButton.test.tsx +95 -0
  332. package/src/ToggleButton/ToggleButton.tsx +12 -1
  333. package/src/ToggleButton/ToggleButtonGroup.doc.mjs +4 -0
  334. package/src/Tokenizer/Tokenizer.test.tsx +54 -1
  335. package/src/Toolbar/Toolbar.test.tsx +0 -8
  336. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +163 -0
  337. package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +63 -0
  338. package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +142 -0
  339. package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +126 -0
  340. package/src/Tooltip/Tooltip.test.tsx +31 -0
  341. package/src/Tooltip/useTooltip.test.tsx +13 -0
  342. package/src/Tooltip/useTooltip.tsx +11 -0
  343. package/src/TopNav/TopNavMegaMenu.test.tsx +22 -0
  344. package/src/TopNav/TopNavMegaMenu.tsx +7 -3
  345. package/src/TopNav/TopNavMenu.test.tsx +19 -0
  346. package/src/TopNav/TopNavMenu.tsx +11 -3
  347. package/src/TreeList/TreeList.doc.mjs +1 -0
  348. package/src/TreeList/TreeList.spec.md +29 -17
  349. package/src/TreeList/TreeList.test.tsx +327 -6
  350. package/src/TreeList/TreeList.tsx +5 -1
  351. package/src/TreeList/TreeListBranches.tsx +6 -0
  352. package/src/TreeList/TreeListItem.tsx +88 -13
  353. package/src/Typeahead/BaseTypeahead.tsx +26 -0
  354. package/src/Typeahead/Typeahead.test.tsx +260 -2
  355. package/src/docPropLiterals.test.ts +36 -10
  356. package/src/hooks/useEntryAnimation.doc.mjs +4 -4
  357. package/src/hooks/useEntryAnimation.ssr.test.tsx +143 -0
  358. package/src/hooks/useEntryAnimation.ts +15 -16
  359. package/src/hooks/useIsHydrating.test.tsx +70 -0
  360. package/src/hooks/useIsHydrating.ts +31 -0
  361. package/src/hooks/useMenuHover.ts +18 -13
  362. package/src/i18n/InternationalizationProvider.doc.mjs +8 -0
  363. package/src/i18n/InternationalizationProvider.spec.md +252 -0
  364. package/src/i18n/__tests__/InternationalizationProvider.test.tsx +128 -0
  365. package/src/i18n/generated-locales/en.generated.ts +121 -0
  366. package/src/i18n/generated-locales/pseudo.generated.ts +121 -0
  367. package/src/i18n/useTranslator.test.tsx +37 -0
  368. package/src/index.ts +1 -0
  369. package/src/postinstall.test.mjs +46 -0
  370. package/src/reset.css +0 -29
  371. package/src/theme/derivedVarRegistry.test.ts +7 -3
  372. package/src/theme/derivedVarRegistry.ts +0 -3
  373. package/src/utils/focusOutline.stylex.ts +4 -0
  374. package/src/utils/plainDate.test.ts +37 -1
  375. package/src/utils/plainDate.ts +15 -5
  376. package/src/utils/sharedMutationObserver.test.ts +53 -0
  377. package/src/utils/sharedMutationObserver.ts +78 -0
@@ -9,21 +9,31 @@
9
9
  * SYNC: When DropdownMenu.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
12
+ import {
13
+ describe,
14
+ it,
15
+ expect,
16
+ vi,
17
+ beforeAll,
18
+ beforeEach,
19
+ afterEach,
20
+ } from 'vitest';
13
21
  import {readFileSync} from 'node:fs';
14
22
  import {render, screen, fireEvent, waitFor, act} from '@testing-library/react';
15
23
  import userEvent from '@testing-library/user-event';
16
- import {useState} from 'react';
24
+ import {createRef, useState} from 'react';
17
25
  import * as stylex from '@stylexjs/stylex';
18
26
  import {DropdownMenu, type DropdownMenuTriggerProps} from './DropdownMenu';
19
27
  import {DropdownMenuItem} from './DropdownMenuItem';
20
28
  import {DropdownMenuDivider} from './DropdownMenuDivider';
21
29
  import {DropdownMenuGroup} from './DropdownMenuGroup';
30
+ import {warmMenuBottomSheet} from './__tests__/warmMenuBottomSheet';
22
31
  import {DropdownMenuSubMenu} from './DropdownMenuSubMenu';
23
32
  import {Divider} from '../Divider';
24
33
  import {rtlStyles} from '../utils';
25
34
  import {__resetInteractionModalityForTest} from '../utils/interactionModality';
26
35
  import {focusOutlineStyles} from '../utils/focusOutline.stylex';
36
+ import {spacingVars} from '../theme/tokens.stylex';
27
37
 
28
38
  // Mock showPopover and hidePopover methods since they're not implemented in jsdom
29
39
  beforeEach(() => {
@@ -73,6 +83,10 @@ beforeEach(() => {
73
83
  };
74
84
  });
75
85
 
86
+ // The touch sheet is loaded lazily; resolve it once so sheets below render
87
+ // synchronously.
88
+ beforeAll(warmMenuBottomSheet, 20000);
89
+
76
90
  describe('DropdownMenu', () => {
77
91
  it('renders trigger button with label', () => {
78
92
  render(
@@ -1352,18 +1366,6 @@ describe('DropdownMenu items', () => {
1352
1366
  );
1353
1367
  expect(handleClick).not.toHaveBeenCalled();
1354
1368
  });
1355
-
1356
- it('has aria-disabled when disabled', () => {
1357
- render(
1358
- <DropdownMenu
1359
- button={{label: 'Actions'}}
1360
- items={[{label: 'Edit', isDisabled: true}]}
1361
- />,
1362
- );
1363
- expect(
1364
- screen.getByRole('menuitem', {name: 'Edit', hidden: true}),
1365
- ).toHaveAttribute('aria-disabled', 'true');
1366
- });
1367
1369
  });
1368
1370
 
1369
1371
  describe('DropdownMenu sections', () => {
@@ -1565,10 +1567,9 @@ describe('DropdownMenuItem host attributes', () => {
1565
1567
 
1566
1568
  describe('DropdownMenuGroup (compound mode)', () => {
1567
1569
  it('a focusable node in the heading is not reachable by the arrow keys', async () => {
1568
- // A heading is not a menu row, so a control inside one lands in the
1569
- // group but outside the roving focus order. The type does not prevent
1570
- // this — a rich heading is a legitimate need — so the behavior is
1571
- // pinned rather than discovered.
1570
+ // The public ReactNode type permits a focusable descendant, but the menu's
1571
+ // roving focus model cannot reach it. Pin that shipped gap while the public
1572
+ // title contract remains unsettled.
1572
1573
  const user = userEvent.setup();
1573
1574
  render(
1574
1575
  <DropdownMenu button={{label: 'Actions'}}>
@@ -2520,6 +2521,66 @@ describe('DropdownMenu data/compound parity', () => {
2520
2521
  expect(fromCompound).toHaveClass('astryx-dropdown-menu-divider');
2521
2522
  });
2522
2523
 
2524
+ it('forwards host attributes to a compound divider while keeping its role', () => {
2525
+ render(
2526
+ <DropdownMenu button={{label: 'Actions'}}>
2527
+ <DropdownMenuItem label="Edit" />
2528
+ <DropdownMenuDivider
2529
+ id="menu-sep"
2530
+ data-testid="menu-separator"
2531
+ aria-label="Danger zone"
2532
+ // @ts-expect-error role is component-owned
2533
+ role="presentation"
2534
+ />
2535
+ <DropdownMenuItem label="Delete" />
2536
+ </DropdownMenu>,
2537
+ );
2538
+ const separator = screen.getByTestId('menu-separator');
2539
+ expect(separator).toHaveAttribute('id', 'menu-sep');
2540
+ expect(separator).toHaveAttribute('aria-label', 'Danger zone');
2541
+ expect(separator).toHaveAttribute('role', 'separator');
2542
+ expect(separator).toHaveClass('astryx-dropdown-menu-divider');
2543
+ });
2544
+
2545
+ it('composes the complete public root surface on the separator', () => {
2546
+ const ref = createRef<HTMLDivElement>();
2547
+ const onClick = vi.fn();
2548
+ const customStyles = stylex.create({
2549
+ divider: {marginBlock: spacingVars['--spacing-5']},
2550
+ });
2551
+ const customClass = stylex
2552
+ .props(customStyles.divider)
2553
+ .className!.split(' ')
2554
+ .pop()!;
2555
+
2556
+ render(
2557
+ <DropdownMenu button={{label: 'Actions'}}>
2558
+ <DropdownMenuItem label="Edit" />
2559
+ <DropdownMenuDivider
2560
+ ref={ref}
2561
+ data-testid="menu-separator"
2562
+ className="consumer-divider"
2563
+ style={{backgroundColor: 'rgb(1, 2, 3)'}}
2564
+ xstyle={customStyles.divider}
2565
+ onClick={onClick}
2566
+ />
2567
+ <DropdownMenuItem label="Delete" />
2568
+ </DropdownMenu>,
2569
+ );
2570
+
2571
+ const separator = screen.getByTestId('menu-separator');
2572
+ expect(ref.current).toBe(separator);
2573
+ expect(separator).toHaveClass(
2574
+ 'astryx-dropdown-menu-divider',
2575
+ 'astryx-divider',
2576
+ 'consumer-divider',
2577
+ customClass,
2578
+ );
2579
+ expect(separator).toHaveStyle({backgroundColor: 'rgb(1, 2, 3)'});
2580
+ fireEvent.click(separator);
2581
+ expect(onClick).toHaveBeenCalledTimes(1);
2582
+ });
2583
+
2523
2584
  it('skips a compound divider in the arrow-key order', async () => {
2524
2585
  const user = userEvent.setup();
2525
2586
  render(
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file DropdownMenu.tsx
7
- * @input Uses React, StyleX, usePopover, MenuBottomSheet, Button, List,
7
+ * @input Uses React, StyleX, usePopover, MenuBottomSheet (lazy), Button, List,
8
8
  * useListFocus, and the shared viewport-safe menu-width resolver
9
9
  * @output Exports DropdownMenu with caller-selected popover or bottom-sheet
10
10
  * presentation
@@ -60,6 +60,7 @@ import {
60
60
  } from './menuItemRoles';
61
61
  import {
62
62
  DropdownMenuContext,
63
+ DropdownMenuRootOpenContext,
63
64
  type DropdownMenuContextValue,
64
65
  } from './DropdownMenuContext';
65
66
  import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
@@ -80,8 +81,11 @@ import {
80
81
  useAdaptivePresentation,
81
82
  type AdaptivePresentation,
82
83
  } from '../hooks/useAdaptivePresentation';
83
- import {MenuBottomSheet} from './MenuBottomSheet';
84
- import {MenuBottomSheetActionList} from './MenuBottomSheetActionList';
84
+ // Loaded on demand: only the bottom-sheet presentation renders the sheet.
85
+ import {
86
+ MenuBottomSheet,
87
+ MenuBottomSheetActionList,
88
+ } from './lazyMenuBottomSheet';
85
89
  import {layerAnimations} from '../Layer/layerAnimations.stylex';
86
90
  import type {LayerAlignment, LayerPlacement} from '../Layer/useLayer';
87
91
  import {
@@ -229,8 +233,8 @@ const styles = stylex.create({
229
233
  WebkitTouchCallout: 'none',
230
234
  userSelect: 'none',
231
235
  maxInlineSize: stylex.firstThatWorks(
232
- layerViewportInset.maxInlineSize,
233
- layerViewportInset.maxInlineSizeFallback,
236
+ `calc(${layerViewportInset.maxInlineSize})`,
237
+ `calc(${layerViewportInset.maxInlineSizeFallback})`,
234
238
  ),
235
239
  maxHeight: stylex.firstThatWorks(
236
240
  MENU_MAX_BLOCK_SIZE,
@@ -758,12 +762,37 @@ function DropdownMenuBottomSheet({
758
762
  if (!isOpen || openModalityRef.current !== 'keyboard') {
759
763
  return;
760
764
  }
761
- const frame = requestAnimationFrame(() => {
762
- actionListRef.current
763
- ?.querySelector<HTMLElement>('button:not(:disabled), a[href]')
764
- ?.focus({preventScroll: true});
765
+ const findFirstAction = () =>
766
+ actionListRef.current?.querySelector<HTMLElement>(
767
+ 'button:not(:disabled), a[href]',
768
+ ) ?? null;
769
+ let observer: MutationObserver | null = null;
770
+ let frame = requestAnimationFrame(() => {
771
+ const action = findFirstAction();
772
+ if (action != null) {
773
+ action.focus({preventScroll: true});
774
+ return;
775
+ }
776
+ // The sheet loads on demand, so a keyboard open can arrive before its
777
+ // rows exist. Wait for them, then focus a frame later so the sheet's
778
+ // own open-focus has already run.
779
+ observer = new MutationObserver(() => {
780
+ const loaded = findFirstAction();
781
+ if (loaded == null) {
782
+ return;
783
+ }
784
+ observer?.disconnect();
785
+ observer = null;
786
+ frame = requestAnimationFrame(() => {
787
+ loaded.focus({preventScroll: true});
788
+ });
789
+ });
790
+ observer.observe(document.body, {childList: true, subtree: true});
765
791
  });
766
- return () => cancelAnimationFrame(frame);
792
+ return () => {
793
+ cancelAnimationFrame(frame);
794
+ observer?.disconnect();
795
+ };
767
796
  }, [currentItems, isOpen]);
768
797
 
769
798
  useEffect(() => {
@@ -987,6 +1016,43 @@ function DropdownMenuPopover({
987
1016
  // above it ends the gesture in flight through this ref.
988
1017
  const cancelMenuPressRef = useRef<() => void>(() => {});
989
1018
 
1019
+ // Submenu flyouts are `popover="manual"` elements nested inside the menu's
1020
+ // `popover="auto"` element, so the browser's light dismiss of the menu never
1021
+ // reaches them: without a sweep they keep their open state and their
1022
+ // top-layer entry while the menu is hidden, and re-anchor to the viewport
1023
+ // corner when the menu reopens (#6893). Closing them through the popover
1024
+ // API lets useLayer's toggle handling reset their state. Only flyouts that
1025
+ // are actually open are touched: `:popover-open` ships wherever
1026
+ // `hidePopover` does (they are one API), and the display fallback keeps its
1027
+ // own open signal (`display: block`) so it is guarded the same way.
1028
+ const hideOpenFlyouts = useCallback(() => {
1029
+ const menuPopover = contentRef.current;
1030
+ if (!menuPopover) {
1031
+ return;
1032
+ }
1033
+ // Innermost first: hiding a flyout may unmount the flyouts nested inside
1034
+ // it, and a detached element no longer has the toggle listener that
1035
+ // resets its open state.
1036
+ const flyouts = [
1037
+ ...menuPopover.querySelectorAll<HTMLElement>('[popover]'),
1038
+ ].reverse();
1039
+ for (const flyout of flyouts) {
1040
+ if (typeof flyout.hidePopover === 'function') {
1041
+ if (flyout.matches(':popover-open')) {
1042
+ flyout.hidePopover();
1043
+ }
1044
+ } else if (flyout.style.display === 'block') {
1045
+ flyout.style.display = 'none';
1046
+ const toggle = new Event('toggle', {bubbles: false});
1047
+ Object.defineProperty(toggle, 'newState', {
1048
+ value: 'closed',
1049
+ configurable: true,
1050
+ });
1051
+ flyout.dispatchEvent(toggle);
1052
+ }
1053
+ }
1054
+ }, []);
1055
+
990
1056
  // Focus lands somewhere stated when the menu closes. A press outside that
991
1057
  // landed on a focusable control has already moved focus there; leave it.
992
1058
  // Otherwise — a row acted, Escape, a press on nothing — focus returns to
@@ -994,6 +1060,9 @@ function DropdownMenuPopover({
994
1060
  // keyboard (the modality tracker decides; the suppressed style hides the
995
1061
  // pointer case, which Safari would otherwise paint after a touch pick).
996
1062
  const handleLayerHide = useCallback(() => {
1063
+ // Whatever closed the menu — light dismiss, the dismissal stack, Escape
1064
+ // or a controlled close — its flyouts close with it.
1065
+ hideOpenFlyouts();
997
1066
  if (suppressControlledRollbackHideRef.current) {
998
1067
  suppressControlledRollbackHideRef.current = false;
999
1068
  return;
@@ -1024,7 +1093,7 @@ function DropdownMenuPopover({
1024
1093
  // also reaches a control the caller renders, which the browser would
1025
1094
  // otherwise ring (Chromium after a mouse pick, WebKit after a touch).
1026
1095
  focusForLastInput(trigger, {preventScroll: true});
1027
- }, [isControlled, onOpenChange, prepareFocusReturn]);
1096
+ }, [hideOpenFlyouts, isControlled, onOpenChange, prepareFocusReturn]);
1028
1097
 
1029
1098
  // Defer item focus until the layer has committed open, so focus restore
1030
1099
  // captures the trigger instead of the first menu item.
@@ -1694,7 +1763,9 @@ function DropdownMenuPopover({
1694
1763
  )}>
1695
1764
  <MenuRegionsContext value={hasRegions}>
1696
1765
  <DropdownMenuContext value={contextValue}>
1697
- {menuContent}
1766
+ <DropdownMenuRootOpenContext value={isOpen}>
1767
+ {menuContent}
1768
+ </DropdownMenuRootOpenContext>
1698
1769
  </DropdownMenuContext>
1699
1770
  </MenuRegionsContext>
1700
1771
  </div>,
@@ -65,6 +65,30 @@ export interface DropdownMenuContextValue {
65
65
  menuLabel?: string;
66
66
  }
67
67
 
68
+ /**
69
+ * Whether the root menu of this layer tree is open; internal companion to
70
+ * `DropdownMenuContext`, on its own unexported context so the public
71
+ * `DropdownMenuContextValue` surface stays unchanged. A sub-menu's hover
72
+ * intent must die with the menu: a hover-open scheduled on its trigger
73
+ * before the menu closed from elsewhere would otherwise fire into the
74
+ * hidden menu and reopen the flyout behind it (#6893). `null` means no
75
+ * signal, treated as open, matching providers that have no state to give.
76
+ * @internal
77
+ */
78
+ export const DropdownMenuRootOpenContext = createLayerScopedContext<
79
+ boolean | null
80
+ >(null);
81
+ DropdownMenuRootOpenContext.displayName = 'DropdownMenuRootOpenContext';
82
+
83
+ /**
84
+ * Whether the root menu backing these compound items is open. True when no
85
+ * provider gave a signal.
86
+ * @internal
87
+ */
88
+ export function useDropdownMenuRootOpen(): boolean {
89
+ return use(DropdownMenuRootOpenContext) !== false;
90
+ }
91
+
68
92
  export const DropdownMenuContext =
69
93
  createLayerScopedContext<DropdownMenuContextValue | null>(null);
70
94
  DropdownMenuContext.displayName = 'DropdownMenuContext';
@@ -8,7 +8,7 @@ export const docs = {
8
8
  displayName: 'Dropdown Menu Divider',
9
9
  isHiddenFromOverview: true,
10
10
  description:
11
- 'A horizontal rule separating groups of rows in a compound menu. Renders role="separator", so it is never a stop in the arrow-key order. The data-driven equivalent is the `{type: "divider"}` entry in `items`; both modes render this component, so they look and theme identically.',
11
+ 'A horizontal rule separating groups of rows in a compound menu. Renders role="separator", so it is never a stop in the arrow-key order. The data-driven equivalent is the `{type: "divider"}` entry in `items`; both modes render this component, so they look and theme identically. Host attributes and DOM handlers (id, data-*, aria-*) reach the separator; its role and orientation stay component-owned.',
12
12
  props: [
13
13
  {
14
14
  name: 'xstyle',
@@ -27,7 +27,7 @@ export const docsDense = {
27
27
  isHiddenFromOverview: true,
28
28
  displayName: 'Dropdown Menu Divider',
29
29
  description:
30
- 'separator row for compound menus; compound peer of the data API\'s {type: "divider"}',
30
+ 'separator row for compound menus; compound peer of the data API\'s {type: "divider"}; host attributes reach the separator, role/orientation stay owned',
31
31
  propDescriptions: {
32
32
  xstyle: 'StyleX styles applied after the menu spacing',
33
33
  },
@@ -0,0 +1,192 @@
1
+ ---
2
+ schema_version: 3
3
+ template_version: 7
4
+ kind: component
5
+ id: component:DropdownMenuDivider
6
+ authority: draft
7
+ archive_reason: null
8
+ superseded_by: null
9
+ approved_by: null
10
+ approved_at: null
11
+ owners: [cixzhang]
12
+ review_triggers: [public-api, behavior, theming, accessibility, testing]
13
+ verified_by:
14
+ [
15
+ packages/core/src/DropdownMenu/DropdownMenu.test.tsx,
16
+ packages/core/src/ContextMenu/ContextMenu.test.tsx,
17
+ packages/core/src/Breadcrumbs/Breadcrumbs.test.tsx,
18
+ packages/core/src/theme/themingTargets.test.ts,
19
+ apps/storybook/stories/DropdownMenuDivider.stories.tsx,
20
+ scripts/check-knowledge.mjs,
21
+ ]
22
+ modules: []
23
+ families: []
24
+ design_specs: []
25
+ architecture:
26
+ [
27
+ architecture:public-component-api,
28
+ architecture:component-theming-surface,
29
+ architecture:component-test-sufficiency,
30
+ ]
31
+ contributing: []
32
+ system_specs: [spec:AST-020, spec:AST-029]
33
+ ---
34
+
35
+ # DropdownMenuDivider component contract
36
+
37
+ ## Contract at a glance
38
+
39
+ | Area | Contract |
40
+ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | Public contract | `DropdownMenuDivider` / `DropdownMenuDividerProps`, plus the `ContextMenuDivider` and `BreadcrumbMenuDivider` aliases and their props; `BaseProps<HTMLDivElement>` minus caller-owned `role` and `aria-orientation`; a root `ref`. |
42
+ | Behavior | One unlabelled horizontal separator divides pointer-menu rows and stays out of arrow-key order; data and compound pointer modes render the same component. |
43
+ | End-user impact | Pointer-menu sections remain visually separated without adding a focus stop or changing row operation. |
44
+ | Builder impact | No new caller choice. Host attributes, handlers, and styling inputs continue to compose on the separator. |
45
+ | Compatibility/readiness | Patch-compatible observational contract. Runtime behavior, exports, defaults, and target names remain unchanged; authority stays `draft` until exact-head owner approval. |
46
+ | Review checks | Reject a focusable separator, changed role/orientation, pointer data/compound divergence, dropped root inputs, or a target moved off the visible separator. |
47
+ | Governing rules | `architecture:public-component-api/INV1,INV5–INV9`; `architecture:component-theming-surface/INV3–INV6`; WCAG 2.2 SC 1.3.1 and 4.1.2 through `spec:AST-020/FR1`. |
48
+
49
+ This table is a review projection; the body below is authoritative.
50
+
51
+ ## Intent
52
+
53
+ DropdownMenuDivider provides the visual and programmatic boundary between groups
54
+ of rows in pointer-menu presentations. It ships directly under the DropdownMenu
55
+ name and through ContextMenu and Breadcrumbs aliases. It owns the separator
56
+ semantics, menu spacing, root input composition, and the
57
+ `dropdown-menu-divider` theme target. The surrounding DropdownMenu, ContextMenu,
58
+ or BreadcrumbItem owns menu lifecycle and row navigation; Divider supplies the
59
+ base rule geometry and paint.
60
+
61
+ Consumer syntax and examples belong in `DropdownMenuDivider.doc.mjs`.
62
+
63
+ ## Compatibility and migration
64
+
65
+ - Released default preserved: yes — one horizontal, unlabelled separator.
66
+ - Compatibility class: observational patch; no behavior or public surface changes.
67
+ - Controlled/uncontrolled behavior: not applicable.
68
+ - Migration decision: none.
69
+
70
+ Consumer migration instructions belong in consumer docs and release notes.
71
+
72
+ ## Ownership boundary
73
+
74
+ **Owns**
75
+
76
+ - One horizontal separator root and its public ref, DOM/data/ARIA, class, style,
77
+ event, and `xstyle` composition.
78
+ - Menu-specific block-axis spacing.
79
+ - The `dropdown-menu-divider` target on the same visible root.
80
+ - Pointer-menu data/compound rendering parity for the divider row across
81
+ DropdownMenu, ContextMenu, and breadcrumb menus.
82
+
83
+ **Does not own / non-goals**
84
+
85
+ - Menu open state, focus entry, row activation, or arrow-key navigation — owned by
86
+ DropdownMenu, ContextMenu, BreadcrumbItem, and their shared hooks.
87
+ - Touch and bottom-sheet data dividers — those presentations render Divider
88
+ directly and are owned by their parent menu surface.
89
+ - Base separator rule geometry and token paint — delegated to Divider.
90
+ - Caller-authored grouping meaning — owned by the product callsite.
91
+
92
+ ## Public concepts
93
+
94
+ | Concept | Closed values or states | Meaning | Availability by variant/orientation/state | Default | Owner | Stability | Invalid-value behavior |
95
+ | ------------- | --------------------------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------- | ---------------------- | -------------------- | --------- | ------------------------------------------------------------------------------- |
96
+ | Separator row | one horizontal unlabelled separator | Divides adjacent pointer-menu row groups without becoming a row or focus stop. | Data and compound pointer-menu modes | present where authored | component | stable | Role and orientation are excluded from caller props and remain component-owned. |
97
+ | Root surface | ref; DOM/data/ARIA; class/style/xstyle/events | Extends the separator root while preserving component semantics. | Every separator row | omitted | public component API | stable | Accepted inputs compose; role and orientation remain authoritative. |
98
+
99
+ ## Behavioral and layout contract
100
+
101
+ Draft requirements identify their basis so observed code is not mistaken for an
102
+ intentional decision.
103
+
104
+ | ID | Candidate invariant | Basis | Draft review state |
105
+ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------ |
106
+ | FR1 | The component MUST render one unlabelled horizontal separator with pointer-menu-specific block spacing. | Released source/docs and focused tests | verify |
107
+ | FR2 | The root MUST expose `role="separator"` and `aria-orientation="horizontal"`, remain outside menu-item collection and arrow-key order, and never become focusable. | WCAG 2.2 SC 1.3.1 and 4.1.2; focused tests | verify |
108
+ | FR3 | In pointer-menu presentation, `{type: "divider"}` in the data API and the compound divider component or alias MUST render the same component DOM, spacing, semantics, and targets. | Released docs/source; data/compound parity test | verify |
109
+ | FR4 | The visible root MUST carry both the delegated `divider` target and the menu-specific `dropdown-menu-divider` target; menu spacing applies before caller `xstyle`. | `architecture:component-theming-surface/INV3–INV6`; source/tests | verify |
110
+ | FR5 | Accepted ref, DOM/data/ARIA, class, style, events, and `xstyle` MUST reach or compose on the separator root without replacing component-owned role or orientation. | `architecture:public-component-api/INV5–INV8` | verify |
111
+
112
+ ### Allowed variation
113
+
114
+ - **AV1 — Theme paint.** The delegated Divider and menu-specific targets may restyle
115
+ their owned surface without changing separator semantics or focus behavior.
116
+ - **AV2 — Caller root inputs.** Supported host attributes, handlers, and styling
117
+ inputs may vary while the owned role and orientation remain fixed.
118
+
119
+ ### Representative states
120
+
121
+ | State | Required invariant | Allowed variation |
122
+ | ------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------- |
123
+ | Pointer data-mode divider | One horizontal separator between data-driven rows. | Adjacent row content and active theme may vary. |
124
+ | Pointer compound divider | DOM and behavior match the pointer data-mode divider. | Host attributes and supported styling inputs may be supplied. |
125
+ | Public aliases | ContextMenuDivider and BreadcrumbMenuDivider render this component. | The containing menu owns lifecycle and navigation. |
126
+ | Touch data divider | Outside this component; the parent surface renders Divider directly. | Touch-menu layout follows the parent contract. |
127
+ | RTL | The horizontal, full-width rule remains direction-neutral. | Surrounding text and menu alignment may mirror. |
128
+ | Narrow viewport | The rule remains inside the menu without horizontal overflow. | Menu width follows its owner. |
129
+
130
+ ### Transformation and precedence order
131
+
132
+ - **ORD1 — Root composition.** Apply menu spacing, then caller `xstyle`; combine the
133
+ menu-specific target class with caller `className`; preserve caller `style` and
134
+ host props; keep role and orientation component-owned.
135
+
136
+ ### Performance and resources
137
+
138
+ - **PR1 — No external lifecycle.** DropdownMenuDivider installs no listener,
139
+ observer, timer, request, state machine, or Effect.
140
+
141
+ ## Accessibility contract
142
+
143
+ - **AR1 — Separator semantics.** The root exposes a horizontal separator and no
144
+ interactive role or tab stop.
145
+ - **AR2 — Navigation exclusion.** Menu navigation collects menu-item roles only, so
146
+ ArrowDown and ArrowUp pass across the separator without focusing it.
147
+
148
+ ## Design relationships
149
+
150
+ | Anatomy or state | Design requirement | Representation authority | Hierarchy role | Component contract |
151
+ | ---------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------- | ------------------ |
152
+ | Menu separator | Visually separates adjacent menu row groups without competing with row content. | Current source and delegated Divider tokens; no component-local visual decision added | supporting | FR1–FR4 |
153
+
154
+ No component-local visual decision is settled by this draft.
155
+
156
+ ## Family and system relationships
157
+
158
+ - `architecture:public-component-api` owns exports and root input composition.
159
+ - `architecture:component-theming-surface` owns target placement and composed target
160
+ ownership.
161
+ - `architecture:component-test-sufficiency` owns proportionate, observable evidence.
162
+ - DropdownMenu exports the implementation directly. ContextMenu and Breadcrumbs
163
+ re-export it as `ContextMenuDivider` and `BreadcrumbMenuDivider`, with matching
164
+ props aliases; their pointer presentations retain the same component contract.
165
+ - Touch and bottom-sheet data dividers bypass this component and delegate directly
166
+ to Divider under the parent menu contract.
167
+ - Divider provides the base separator implementation; its draft record is
168
+ observational context, not authority for this contract.
169
+ - `spec:AST-029` keeps this observational draft separate from the audit receipt.
170
+
171
+ ## Verification map
172
+
173
+ | Contract | Verification | Representative states | Mutation or failure expectation | Audit section |
174
+ | ---------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
175
+ | FR1–FR3, AR1–AR2 | `DropdownMenu.test.tsx`; ContextMenu and Breadcrumbs alias tests; dedicated Storybook state; scoped axe | pointer data/compound, aliases, open menu, keyboard traversal | Pointer DOM modes drift, an alias disappears, the role/orientation changes, or the separator enters arrow-key order | `audit:DropdownMenuDivider/§1,§3,§4,§6` |
176
+ | FR4 | Focused root-composition test; exact-head Neutral/Butter browser frames | light/dark themes; menu and delegated targets | A target leaves the visible root or caller `xstyle` stops composing after menu spacing | `audit:DropdownMenuDivider/§2,§5` |
177
+ | FR5 | Focused root-composition test; lint and typecheck | ref, host attributes, class/style/xstyle/events | An accepted input is dropped or owned semantics are overwritten | `audit:DropdownMenuDivider/§3` |
178
+ | PR1 | Source review | every state | A lifecycle mechanism appears without a resource contract | `audit:DropdownMenuDivider/§7` |
179
+
180
+ ## Decision log
181
+
182
+ None. This draft records current facts and introduces no product decision.
183
+
184
+ ## Open questions
185
+
186
+ None.
187
+
188
+ ## Content boundary
189
+
190
+ This file does not duplicate consumer prop tables/examples, current audit scores,
191
+ screenshot matrices, implementation steps, or system rules. The audit pull request
192
+ owns the closed run evidence required by `spec:AST-029/FR5`.
@@ -4,7 +4,7 @@
4
4
 
5
5
  /**
6
6
  * @file DropdownMenuDivider.tsx
7
- * @input Uses Divider, spacing tokens, themeProps
7
+ * @input Uses Divider, spacing tokens, themeProps; forwards host props to Divider
8
8
  * @output Exports DropdownMenuDivider component and DropdownMenuDividerProps
9
9
  * @position Sub-component; used inside DropdownMenu
10
10
  *
@@ -37,9 +37,9 @@ const styles = stylex.create({
37
37
 
38
38
  const THEME_CLASS_NAME = themeProps('dropdown-menu-divider').className;
39
39
 
40
- export interface DropdownMenuDividerProps extends Pick<
41
- BaseProps,
42
- 'xstyle' | 'className' | 'style'
40
+ export interface DropdownMenuDividerProps extends Omit<
41
+ BaseProps<HTMLDivElement>,
42
+ 'role' | 'aria-orientation'
43
43
  > {
44
44
  /** Ref forwarded to the separator element. */
45
45
  ref?: React.Ref<HTMLDivElement>;
@@ -49,7 +49,9 @@ export interface DropdownMenuDividerProps extends Pick<
49
49
  * A horizontal rule separating groups of menu rows.
50
50
  *
51
51
  * Renders `role="separator"`, so it is never a stop in the menu's arrow-key
52
- * order. Equivalent to `{type: 'divider'}` in the `items` data API.
52
+ * order. Equivalent to `{type: 'divider'}` in the `items` data API. Host
53
+ * attributes (`id`, `data-*`, `aria-*`) and DOM handlers reach the separator;
54
+ * its role and orientation stay component-owned.
53
55
  *
54
56
  * @example
55
57
  * ```
@@ -65,9 +67,11 @@ export function DropdownMenuDivider({
65
67
  className,
66
68
  style,
67
69
  ref,
70
+ ...rest
68
71
  }: DropdownMenuDividerProps) {
69
72
  return (
70
73
  <Divider
74
+ {...rest}
71
75
  ref={ref}
72
76
  xstyle={[styles.divider, xstyle]}
73
77
  className={