@astryxdesign/core 0.3.0 → 0.4.0

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 (551) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/dist/AlertDialog/AlertDialog.d.ts +10 -2
  3. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  4. package/dist/AlertDialog/AlertDialog.js +22 -3
  5. package/dist/AppShell/AppShell.d.ts +2 -2
  6. package/dist/AppShell/AppShell.d.ts.map +1 -1
  7. package/dist/AppShell/AppShell.js +40 -9
  8. package/dist/AspectRatio/AspectRatio.d.ts +13 -0
  9. package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
  10. package/dist/AspectRatio/AspectRatio.js +9 -0
  11. package/dist/Avatar/Avatar.d.ts.map +1 -1
  12. package/dist/Avatar/Avatar.js +33 -20
  13. package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
  14. package/dist/AvatarGroup/AvatarGroupOverflow.js +2 -3
  15. package/dist/Banner/Banner.d.ts.map +1 -1
  16. package/dist/Banner/Banner.js +15 -14
  17. package/dist/Breadcrumbs/BreadcrumbItem.d.ts.map +1 -1
  18. package/dist/Breadcrumbs/BreadcrumbItem.js +21 -8
  19. package/dist/Breadcrumbs/index.d.ts +2 -1
  20. package/dist/Breadcrumbs/index.d.ts.map +1 -1
  21. package/dist/Breadcrumbs/index.js +1 -0
  22. package/dist/Button/Button.d.ts.map +1 -1
  23. package/dist/Button/Button.js +11 -15
  24. package/dist/Calendar/Calendar.d.ts.map +1 -1
  25. package/dist/Calendar/Calendar.js +2 -1
  26. package/dist/Calendar/styles.d.ts +0 -2
  27. package/dist/Calendar/styles.d.ts.map +1 -1
  28. package/dist/Calendar/styles.js +0 -2
  29. package/dist/Carousel/Carousel.d.ts.map +1 -1
  30. package/dist/Carousel/Carousel.js +8 -12
  31. package/dist/Chat/ChatDictationButton.d.ts.map +1 -1
  32. package/dist/Chat/ChatDictationButton.js +2 -1
  33. package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
  34. package/dist/Chat/ChatLayoutScrollButton.js +3 -1
  35. package/dist/Chat/ChatSendButton.d.ts.map +1 -1
  36. package/dist/Chat/ChatSendButton.js +4 -3
  37. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  38. package/dist/Chat/ChatToolCalls.js +17 -17
  39. package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
  40. package/dist/Chat/useTriggerMenu.js +2 -6
  41. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  42. package/dist/CheckboxInput/CheckboxInput.js +36 -125
  43. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  44. package/dist/ClickableCard/ClickableCard.js +3 -11
  45. package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
  46. package/dist/CodeBlock/CodeBlock.js +84 -67
  47. package/dist/Collapsible/Collapsible.d.ts.map +1 -1
  48. package/dist/Collapsible/Collapsible.js +35 -15
  49. package/dist/CommandPalette/CommandPaletteItem.d.ts +1 -1
  50. package/dist/CommandPalette/CommandPaletteItem.d.ts.map +1 -1
  51. package/dist/CommandPalette/CommandPaletteItem.js +6 -3
  52. package/dist/CommandPalette/CommandPaletteList.js +1 -1
  53. package/dist/ComplexSelector/ComplexSelector.d.ts +1 -1
  54. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  55. package/dist/ComplexSelector/ComplexSelector.js +35 -33
  56. package/dist/ContextMenu/ContextMenu.d.ts +2 -2
  57. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  58. package/dist/ContextMenu/ContextMenu.js +1 -1
  59. package/dist/ContextMenu/index.d.ts +2 -1
  60. package/dist/ContextMenu/index.d.ts.map +1 -1
  61. package/dist/ContextMenu/index.js +1 -0
  62. package/dist/DateInput/DateInput.d.ts +9 -2
  63. package/dist/DateInput/DateInput.d.ts.map +1 -1
  64. package/dist/DateInput/DateInput.js +24 -23
  65. package/dist/DateRangeInput/DateRangeInput.d.ts +9 -2
  66. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  67. package/dist/DateRangeInput/DateRangeInput.js +11 -16
  68. package/dist/DateTimeInput/DateTimeInput.d.ts +9 -1
  69. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  70. package/dist/DateTimeInput/DateTimeInput.js +22 -17
  71. package/dist/Dialog/Dialog.d.ts.map +1 -1
  72. package/dist/Dialog/Dialog.js +4 -4
  73. package/dist/DropdownMenu/DropdownMenu.d.ts +33 -8
  74. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  75. package/dist/DropdownMenu/DropdownMenu.js +62 -20
  76. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +8 -11
  77. package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -1
  78. package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +32 -31
  79. package/dist/DropdownMenu/DropdownMenuDivider.d.ts +25 -0
  80. package/dist/DropdownMenu/DropdownMenuDivider.d.ts.map +1 -0
  81. package/dist/DropdownMenu/DropdownMenuDivider.js +66 -0
  82. package/dist/DropdownMenu/DropdownMenuItem.d.ts +15 -2
  83. package/dist/DropdownMenu/DropdownMenuItem.d.ts.map +1 -1
  84. package/dist/DropdownMenu/DropdownMenuItem.js +20 -8
  85. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +5 -5
  86. package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -1
  87. package/dist/DropdownMenu/DropdownMenuRadioItem.js +24 -67
  88. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  89. package/dist/DropdownMenu/DropdownMenuSubMenu.js +2 -8
  90. package/dist/DropdownMenu/index.d.ts +2 -1
  91. package/dist/DropdownMenu/index.d.ts.map +1 -1
  92. package/dist/DropdownMenu/index.js +4 -0
  93. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  94. package/dist/DropdownMenu/renderDropdownItems.js +35 -27
  95. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  96. package/dist/EmptyState/EmptyState.js +9 -5
  97. package/dist/Field/InputClearButton.d.ts +16 -3
  98. package/dist/Field/InputClearButton.d.ts.map +1 -1
  99. package/dist/Field/InputClearButton.js +15 -4
  100. package/dist/HoverCard/useHoverCard.d.ts.map +1 -1
  101. package/dist/HoverCard/useHoverCard.js +3 -19
  102. package/dist/Indicator/CheckIndicator.d.ts +25 -0
  103. package/dist/Indicator/CheckIndicator.d.ts.map +1 -0
  104. package/dist/Indicator/CheckIndicator.js +155 -0
  105. package/dist/Indicator/CheckboxIndicator.d.ts +22 -0
  106. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -0
  107. package/dist/Indicator/CheckboxIndicator.js +173 -0
  108. package/dist/Indicator/RadioIndicator.d.ts +23 -0
  109. package/dist/Indicator/RadioIndicator.d.ts.map +1 -0
  110. package/dist/Indicator/RadioIndicator.js +137 -0
  111. package/dist/Indicator/index.d.ts +24 -0
  112. package/dist/Indicator/index.d.ts.map +1 -0
  113. package/dist/Indicator/index.js +26 -0
  114. package/dist/Indicator/indicator.markers.stylex.d.ts +16 -0
  115. package/dist/Indicator/indicator.markers.stylex.d.ts.map +1 -0
  116. package/dist/Indicator/indicator.markers.stylex.js +21 -0
  117. package/dist/Indicator/indicatorRegistry.d.ts +71 -0
  118. package/dist/Indicator/indicatorRegistry.d.ts.map +1 -0
  119. package/dist/Indicator/indicatorRegistry.js +93 -0
  120. package/dist/Indicator/types.d.ts +167 -0
  121. package/dist/Indicator/types.d.ts.map +1 -0
  122. package/dist/Indicator/types.js +1 -0
  123. package/dist/Indicator/useIndicator.d.ts +18 -0
  124. package/dist/Indicator/useIndicator.d.ts.map +1 -0
  125. package/dist/Indicator/useIndicator.js +30 -0
  126. package/dist/Item/Item.d.ts.map +1 -1
  127. package/dist/Item/Item.js +4 -8
  128. package/dist/Layer/useLayer.d.ts +14 -0
  129. package/dist/Layer/useLayer.d.ts.map +1 -1
  130. package/dist/Layer/useLayer.js +25 -2
  131. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  132. package/dist/Lightbox/Lightbox.js +56 -53
  133. package/dist/Link/Link.d.ts.map +1 -1
  134. package/dist/Link/Link.js +4 -5
  135. package/dist/MobileNav/MobileNav.js +8 -8
  136. package/dist/MoreMenu/MoreMenu.d.ts +14 -1
  137. package/dist/MoreMenu/MoreMenu.d.ts.map +1 -1
  138. package/dist/MoreMenu/MoreMenu.js +4 -0
  139. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  140. package/dist/MultiSelector/MultiSelector.js +78 -75
  141. package/dist/NumberInput/NumberInput.d.ts +27 -1
  142. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  143. package/dist/NumberInput/NumberInput.js +230 -42
  144. package/dist/Outline/Outline.d.ts.map +1 -1
  145. package/dist/Outline/Outline.js +2 -3
  146. package/dist/Pagination/Pagination.d.ts.map +1 -1
  147. package/dist/Pagination/Pagination.js +61 -63
  148. package/dist/Popover/Popover.d.ts.map +1 -1
  149. package/dist/Popover/Popover.js +6 -8
  150. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  151. package/dist/PowerSearch/PowerSearch.js +1 -1
  152. package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
  153. package/dist/PowerSearch/PowerSearchEditPopover.js +7 -0
  154. package/dist/ProgressBar/ProgressBar.d.ts +11 -4
  155. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  156. package/dist/ProgressBar/ProgressBar.js +96 -6
  157. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  158. package/dist/RadioList/RadioListItem.js +29 -84
  159. package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
  160. package/dist/Resizable/ResizeHandle.js +2 -3
  161. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  162. package/dist/SegmentedControl/SegmentedControlItem.js +2 -3
  163. package/dist/SelectableCard/SelectableCard.d.ts.map +1 -1
  164. package/dist/SelectableCard/SelectableCard.js +3 -11
  165. package/dist/Selector/Selector.d.ts +2 -1
  166. package/dist/Selector/Selector.d.ts.map +1 -1
  167. package/dist/Selector/Selector.js +164 -94
  168. package/dist/Selector/hooks.d.ts +18 -6
  169. package/dist/Selector/hooks.d.ts.map +1 -1
  170. package/dist/Selector/hooks.js +57 -40
  171. package/dist/SideNav/SideNav.d.ts.map +1 -1
  172. package/dist/SideNav/SideNav.js +1 -1
  173. package/dist/SideNav/SideNavCollapseButton.d.ts +1 -1
  174. package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
  175. package/dist/SideNav/SideNavCollapseButton.js +29 -14
  176. package/dist/SideNav/SideNavHeading.d.ts.map +1 -1
  177. package/dist/SideNav/SideNavHeading.js +56 -20
  178. package/dist/SideNav/SideNavItem.d.ts.map +1 -1
  179. package/dist/SideNav/SideNavItem.js +28 -22
  180. package/dist/Stack/stack.stylex.d.ts +3 -3
  181. package/dist/Switch/Switch.js +11 -10
  182. package/dist/TabList/Tab.d.ts.map +1 -1
  183. package/dist/TabList/Tab.js +2 -3
  184. package/dist/TabList/TabMenu.d.ts.map +1 -1
  185. package/dist/TabList/TabMenu.js +41 -19
  186. package/dist/Table/BaseTable.d.ts.map +1 -1
  187. package/dist/Table/BaseTable.js +8 -0
  188. package/dist/Table/index.d.ts +1 -1
  189. package/dist/Table/index.d.ts.map +1 -1
  190. package/dist/Table/index.js +1 -1
  191. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts +1 -1
  192. package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
  193. package/dist/Table/plugins/groupedRows/useTableGroupedRows.js +22 -18
  194. package/dist/Table/plugins/rowExpansion/index.d.ts +1 -1
  195. package/dist/Table/plugins/rowExpansion/index.d.ts.map +1 -1
  196. package/dist/Table/plugins/rowExpansion/index.js +1 -1
  197. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts +41 -76
  198. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  199. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +94 -345
  200. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  201. package/dist/Table/plugins/sortable/useTableSortable.js +23 -4
  202. package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -1
  203. package/dist/Table/plugins/tree/useTableTreeData.js +29 -33
  204. package/dist/Table/tableContextMenu.d.ts.map +1 -1
  205. package/dist/Table/tableContextMenu.js +2 -1
  206. package/dist/Table/types.d.ts +11 -0
  207. package/dist/Table/types.d.ts.map +1 -1
  208. package/dist/TextArea/TextArea.d.ts +10 -1
  209. package/dist/TextArea/TextArea.d.ts.map +1 -1
  210. package/dist/TextArea/TextArea.js +16 -6
  211. package/dist/TextInput/TextInput.d.ts +10 -1
  212. package/dist/TextInput/TextInput.d.ts.map +1 -1
  213. package/dist/TextInput/TextInput.js +13 -19
  214. package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
  215. package/dist/Thumbnail/Thumbnail.js +32 -26
  216. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  217. package/dist/TimeInput/TimeInput.js +7 -15
  218. package/dist/Timestamp/TimestampHoverCard.d.ts.map +1 -1
  219. package/dist/Timestamp/TimestampHoverCard.js +16 -34
  220. package/dist/Token/Token.d.ts.map +1 -1
  221. package/dist/Token/Token.js +20 -11
  222. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  223. package/dist/Tokenizer/Tokenizer.js +4 -2
  224. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  225. package/dist/Tooltip/useTooltip.js +3 -19
  226. package/dist/TopNav/TopNav.d.ts.map +1 -1
  227. package/dist/TopNav/TopNav.js +6 -5
  228. package/dist/TopNav/TopNavHeading.d.ts.map +1 -1
  229. package/dist/TopNav/TopNavHeading.js +54 -16
  230. package/dist/TopNav/TopNavItem.d.ts.map +1 -1
  231. package/dist/TopNav/TopNavItem.js +3 -9
  232. package/dist/TopNav/TopNavMegaMenu.d.ts +7 -0
  233. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  234. package/dist/TopNav/TopNavMegaMenu.js +146 -60
  235. package/dist/TopNav/TopNavMegaMenuItem.d.ts.map +1 -1
  236. package/dist/TopNav/TopNavMegaMenuItem.js +22 -3
  237. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  238. package/dist/TopNav/TopNavMenu.js +83 -38
  239. package/dist/TreeList/TreeList.d.ts.map +1 -1
  240. package/dist/TreeList/TreeList.js +13 -0
  241. package/dist/TreeList/TreeListBranches.d.ts +1 -1
  242. package/dist/TreeList/TreeListBranches.d.ts.map +1 -1
  243. package/dist/TreeList/TreeListBranches.js +14 -4
  244. package/dist/TreeList/TreeListItem.d.ts +11 -1
  245. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  246. package/dist/TreeList/TreeListItem.js +57 -30
  247. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  248. package/dist/Typeahead/BaseTypeahead.js +16 -8
  249. package/dist/astryx.css +74 -85
  250. package/dist/astryx.umd.js +56 -56
  251. package/dist/astryx.umd.js.map +4 -4
  252. package/dist/hooks/containerReveal.stylex.d.ts +61 -0
  253. package/dist/hooks/containerReveal.stylex.d.ts.map +1 -0
  254. package/dist/hooks/containerReveal.stylex.js +63 -0
  255. package/dist/hooks/index.d.ts +3 -0
  256. package/dist/hooks/index.d.ts.map +1 -1
  257. package/dist/hooks/index.js +2 -0
  258. package/dist/hooks/useClipboard.d.ts +59 -0
  259. package/dist/hooks/useClipboard.d.ts.map +1 -0
  260. package/dist/hooks/useClipboard.js +106 -0
  261. package/dist/hooks/useContainerReveal.d.ts +14 -8
  262. package/dist/hooks/useContainerReveal.d.ts.map +1 -1
  263. package/dist/hooks/useContainerReveal.js +28 -69
  264. package/dist/hooks/useIndicatorFocusRing.d.ts +28 -0
  265. package/dist/hooks/useIndicatorFocusRing.d.ts.map +1 -0
  266. package/dist/hooks/useIndicatorFocusRing.js +96 -0
  267. package/dist/hooks/useLongPress.d.ts.map +1 -1
  268. package/dist/hooks/useLongPress.js +12 -2
  269. package/dist/hooks/useScrollLock.d.ts.map +1 -1
  270. package/dist/hooks/useScrollLock.js +33 -18
  271. package/dist/hooks/useStreamingText.d.ts +18 -3
  272. package/dist/hooks/useStreamingText.d.ts.map +1 -1
  273. package/dist/hooks/useStreamingText.js +47 -7
  274. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  275. package/dist/hooks/useTreeFocus.js +9 -3
  276. package/dist/hooks/useTypeahead.d.ts +3 -2
  277. package/dist/hooks/useTypeahead.d.ts.map +1 -1
  278. package/dist/hooks/useTypeahead.js +19 -7
  279. package/dist/index.d.ts +1 -0
  280. package/dist/index.d.ts.map +1 -1
  281. package/dist/index.js +1 -0
  282. package/dist/theme/defineTheme.d.ts +16 -0
  283. package/dist/theme/defineTheme.d.ts.map +1 -1
  284. package/dist/theme/defineTheme.js +8 -0
  285. package/dist/theme/derivedVarRegistry.d.ts +8 -0
  286. package/dist/theme/derivedVarRegistry.d.ts.map +1 -1
  287. package/dist/theme/derivedVarRegistry.js +14 -13
  288. package/dist/theme/expandColorScale.d.ts +3 -1
  289. package/dist/theme/expandColorScale.d.ts.map +1 -1
  290. package/dist/theme/expandColorScale.js +17 -7
  291. package/dist/theme/expandTypeScale.d.ts.map +1 -1
  292. package/dist/theme/expandTypeScale.js +17 -0
  293. package/dist/theme/generateThemeRules.d.ts.map +1 -1
  294. package/dist/theme/generateThemeRules.js +11 -0
  295. package/dist/theme/index.d.ts +1 -0
  296. package/dist/theme/index.d.ts.map +1 -1
  297. package/dist/utils/focusOutline.stylex.d.ts +107 -0
  298. package/dist/utils/focusOutline.stylex.d.ts.map +1 -0
  299. package/dist/utils/focusOutline.stylex.js +130 -0
  300. package/dist/utils/focusReturn.d.ts +24 -0
  301. package/dist/utils/focusReturn.d.ts.map +1 -0
  302. package/dist/utils/focusReturn.js +29 -0
  303. package/dist/utils/index.d.ts +1 -0
  304. package/dist/utils/index.d.ts.map +1 -1
  305. package/dist/utils/index.js +1 -0
  306. package/dist/utils/mergeRefs.d.ts.map +1 -1
  307. package/dist/utils/mergeRefs.js +7 -5
  308. package/dist/utils/rtlStyles.d.ts +2 -2
  309. package/dist/utils/rtlStyles.js +2 -2
  310. package/dist/utils/themeProps.d.ts +20 -1
  311. package/dist/utils/themeProps.d.ts.map +1 -1
  312. package/dist/utils/themeProps.js +8 -2
  313. package/locales/en.json +12 -0
  314. package/locales/pseudo.json +9 -0
  315. package/package.json +7 -2
  316. package/src/AlertDialog/AlertDialog.doc.mjs +16 -3
  317. package/src/AlertDialog/AlertDialog.test.tsx +208 -1
  318. package/src/AlertDialog/AlertDialog.tsx +21 -3
  319. package/src/AppShell/AppShell.doc.mjs +11 -0
  320. package/src/AppShell/AppShell.test.tsx +139 -0
  321. package/src/AppShell/AppShell.tsx +32 -22
  322. package/src/AspectRatio/AspectRatio.doc.mjs +27 -3
  323. package/src/AspectRatio/AspectRatio.test.tsx +30 -0
  324. package/src/AspectRatio/AspectRatio.tsx +13 -0
  325. package/src/Avatar/Avatar.doc.mjs +2 -13
  326. package/src/Avatar/Avatar.test.tsx +33 -8
  327. package/src/Avatar/Avatar.tsx +52 -50
  328. package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -9
  329. package/src/Banner/Banner.tsx +9 -6
  330. package/src/Breadcrumbs/BreadcrumbItem.tsx +16 -6
  331. package/src/Breadcrumbs/index.ts +5 -1
  332. package/src/Button/Button.tsx +21 -41
  333. package/src/Calendar/Calendar.tsx +2 -1
  334. package/src/Calendar/styles.ts +0 -8
  335. package/src/Carousel/Carousel.doc.mjs +3 -0
  336. package/src/Carousel/Carousel.tsx +10 -6
  337. package/src/Chat/Chat.doc.mjs +2 -0
  338. package/src/Chat/ChatDictationButton.tsx +7 -1
  339. package/src/Chat/ChatLayoutScrollButton.test.tsx +22 -0
  340. package/src/Chat/ChatLayoutScrollButton.tsx +8 -1
  341. package/src/Chat/ChatSendButton.tsx +2 -3
  342. package/src/Chat/ChatToolCalls.test.tsx +35 -0
  343. package/src/Chat/ChatToolCalls.tsx +36 -16
  344. package/src/Chat/useTriggerMenu.tsx +2 -5
  345. package/src/CheckboxInput/CheckboxInput.doc.mjs +4 -2
  346. package/src/CheckboxInput/CheckboxInput.test.tsx +115 -0
  347. package/src/CheckboxInput/CheckboxInput.tsx +45 -202
  348. package/src/ClickableCard/ClickableCard.tsx +6 -11
  349. package/src/CodeBlock/CodeBlock.doc.mjs +3 -0
  350. package/src/CodeBlock/CodeBlock.test.tsx +91 -0
  351. package/src/CodeBlock/CodeBlock.tsx +57 -80
  352. package/src/Collapsible/Collapsible.tsx +22 -18
  353. package/src/CommandPalette/CommandPalette.test.tsx +29 -0
  354. package/src/CommandPalette/CommandPaletteItem.tsx +7 -4
  355. package/src/CommandPalette/CommandPaletteList.tsx +2 -2
  356. package/src/ComplexSelector/ComplexSelector.doc.mjs +42 -0
  357. package/src/ComplexSelector/ComplexSelector.test.tsx +64 -0
  358. package/src/ComplexSelector/ComplexSelector.tsx +46 -29
  359. package/src/ContextMenu/ContextMenu.doc.mjs +1 -1
  360. package/src/ContextMenu/ContextMenu.test.tsx +41 -0
  361. package/src/ContextMenu/ContextMenu.tsx +3 -3
  362. package/src/ContextMenu/index.ts +5 -1
  363. package/src/DateInput/DateInput.doc.mjs +18 -3
  364. package/src/DateInput/DateInput.test.tsx +93 -11
  365. package/src/DateInput/DateInput.tsx +41 -19
  366. package/src/DateRangeInput/DateRangeInput.doc.mjs +10 -2
  367. package/src/DateRangeInput/DateRangeInput.test.tsx +110 -14
  368. package/src/DateRangeInput/DateRangeInput.tsx +24 -12
  369. package/src/DateTimeInput/DateTimeInput.doc.mjs +16 -2
  370. package/src/DateTimeInput/DateTimeInput.test.tsx +75 -0
  371. package/src/DateTimeInput/DateTimeInput.tsx +35 -8
  372. package/src/Dialog/Dialog.tsx +2 -9
  373. package/src/DropdownMenu/DropdownMenu.doc.mjs +5 -9
  374. package/src/DropdownMenu/DropdownMenu.test.tsx +534 -4
  375. package/src/DropdownMenu/DropdownMenu.tsx +113 -52
  376. package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +27 -30
  377. package/src/DropdownMenu/DropdownMenuDivider.doc.mjs +34 -0
  378. package/src/DropdownMenu/DropdownMenuDivider.tsx +81 -0
  379. package/src/DropdownMenu/DropdownMenuItem.doc.mjs +39 -4
  380. package/src/DropdownMenu/DropdownMenuItem.tsx +48 -10
  381. package/src/DropdownMenu/DropdownMenuRadioItem.tsx +30 -76
  382. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +25 -19
  383. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +44 -14
  384. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +1 -5
  385. package/src/DropdownMenu/index.ts +8 -1
  386. package/src/DropdownMenu/renderDropdownItems.tsx +33 -34
  387. package/src/EmptyState/EmptyState.doc.mjs +4 -0
  388. package/src/EmptyState/EmptyState.test.tsx +62 -0
  389. package/src/EmptyState/EmptyState.tsx +16 -6
  390. package/src/Field/Field.doc.mjs +1 -0
  391. package/src/Field/InputClearButton.test.tsx +117 -0
  392. package/src/Field/InputClearButton.tsx +30 -3
  393. package/src/HoverCard/useHoverCard.tsx +2 -23
  394. package/src/Icon/Icon.doc.mjs +2 -1
  395. package/src/Indicator/CheckIndicator.tsx +174 -0
  396. package/src/Indicator/CheckboxIndicator.tsx +257 -0
  397. package/src/Indicator/Indicator.doc.mjs +344 -0
  398. package/src/Indicator/Indicator.test.tsx +393 -0
  399. package/src/Indicator/RadioIndicator.tsx +206 -0
  400. package/src/Indicator/index.ts +45 -0
  401. package/src/Indicator/indicator.markers.stylex.ts +19 -0
  402. package/src/Indicator/indicatorRegistry.test.tsx +106 -0
  403. package/src/Indicator/indicatorRegistry.ts +120 -0
  404. package/src/Indicator/types.ts +186 -0
  405. package/src/Indicator/useIndicator.ts +39 -0
  406. package/src/Item/Item.doc.mjs +1 -1
  407. package/src/Item/Item.tsx +7 -14
  408. package/src/Layer/useLayer.doc.mjs +2 -2
  409. package/src/Layer/useLayer.test.tsx +85 -0
  410. package/src/Layer/useLayer.tsx +39 -1
  411. package/src/Lightbox/Lightbox.doc.mjs +1 -1
  412. package/src/Lightbox/Lightbox.tsx +43 -38
  413. package/src/Link/Link.tsx +4 -11
  414. package/src/MobileNav/MobileNav.tsx +3 -3
  415. package/src/MoreMenu/MoreMenu.doc.mjs +31 -0
  416. package/src/MoreMenu/MoreMenu.test.tsx +68 -1
  417. package/src/MoreMenu/MoreMenu.tsx +19 -0
  418. package/src/MultiSelector/MultiSelector.doc.mjs +9 -1
  419. package/src/MultiSelector/MultiSelector.test.tsx +255 -16
  420. package/src/MultiSelector/MultiSelector.tsx +103 -99
  421. package/src/NumberInput/NumberInput.doc.mjs +265 -50
  422. package/src/NumberInput/NumberInput.test.tsx +726 -21
  423. package/src/NumberInput/NumberInput.tsx +353 -59
  424. package/src/Outline/Outline.tsx +2 -5
  425. package/src/Pagination/Pagination.doc.mjs +1 -1
  426. package/src/Pagination/Pagination.test.tsx +22 -7
  427. package/src/Pagination/Pagination.tsx +38 -35
  428. package/src/Popover/Popover.tsx +6 -7
  429. package/src/PowerSearch/PowerSearch.tsx +1 -1
  430. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +42 -0
  431. package/src/PowerSearch/PowerSearchEditPopover.tsx +7 -0
  432. package/src/ProgressBar/ProgressBar.doc.mjs +27 -5
  433. package/src/ProgressBar/ProgressBar.test.tsx +224 -1
  434. package/src/ProgressBar/ProgressBar.tsx +124 -22
  435. package/src/RadioList/RadioList.doc.mjs +4 -2
  436. package/src/RadioList/RadioListItem.tsx +31 -130
  437. package/src/Resizable/ResizeHandle.tsx +2 -9
  438. package/src/SegmentedControl/SegmentedControlItem.tsx +2 -9
  439. package/src/SelectableCard/SelectableCard.tsx +4 -9
  440. package/src/Selector/Selector.doc.mjs +7 -4
  441. package/src/Selector/Selector.test.tsx +935 -25
  442. package/src/Selector/Selector.tsx +189 -106
  443. package/src/Selector/hooks.ts +77 -50
  444. package/src/SideNav/SideNav.test.tsx +46 -0
  445. package/src/SideNav/SideNav.tsx +1 -0
  446. package/src/SideNav/SideNavCollapseButton.tsx +15 -10
  447. package/src/SideNav/SideNavHeading.tsx +48 -10
  448. package/src/SideNav/SideNavItem.tsx +20 -12
  449. package/src/Stack/Stack.doc.mjs +110 -0
  450. package/src/StatusDot/StatusDot.doc.mjs +12 -3
  451. package/src/Switch/Switch.test.tsx +32 -0
  452. package/src/Switch/Switch.tsx +8 -8
  453. package/src/TabList/Tab.tsx +2 -9
  454. package/src/TabList/TabMenu.tsx +14 -21
  455. package/src/Table/BaseTable.tsx +11 -0
  456. package/src/Table/index.ts +1 -4
  457. package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +30 -14
  458. package/src/Table/plugins/rowExpansion/index.ts +1 -4
  459. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +125 -175
  460. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +115 -458
  461. package/src/Table/plugins/sortable/useTableSortable.tsx +2 -6
  462. package/src/Table/plugins/tree/useTableTreeData.tsx +43 -24
  463. package/src/Table/tableContextMenu.test.tsx +27 -0
  464. package/src/Table/tableContextMenu.tsx +1 -0
  465. package/src/Table/types.ts +11 -0
  466. package/src/Table/useTableGroupedRows.doc.mjs +1 -1
  467. package/src/Table/useTableRowExpansion.doc.mjs +43 -66
  468. package/src/TextArea/TextArea.doc.mjs +42 -2
  469. package/src/TextArea/TextArea.test.tsx +219 -0
  470. package/src/TextArea/TextArea.tsx +34 -7
  471. package/src/TextInput/TextInput.doc.mjs +18 -2
  472. package/src/TextInput/TextInput.test.tsx +189 -0
  473. package/src/TextInput/TextInput.tsx +25 -31
  474. package/src/Thumbnail/Thumbnail.tsx +2 -9
  475. package/src/TimeInput/TimeInput.doc.mjs +2 -2
  476. package/src/TimeInput/TimeInput.test.tsx +30 -3
  477. package/src/TimeInput/TimeInput.tsx +9 -26
  478. package/src/Timestamp/Timestamp.doc.mjs +4 -3
  479. package/src/Timestamp/TimestampHoverCard.tsx +13 -35
  480. package/src/Token/Token.tsx +5 -28
  481. package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
  482. package/src/Tokenizer/Tokenizer.test.tsx +45 -3
  483. package/src/Tokenizer/Tokenizer.tsx +10 -2
  484. package/src/Tooltip/useTooltip.tsx +2 -23
  485. package/src/TopNav/TopNav.tsx +1 -3
  486. package/src/TopNav/TopNavHeading.tsx +42 -13
  487. package/src/TopNav/TopNavItem.tsx +3 -21
  488. package/src/TopNav/TopNavMegaMenu.test.tsx +433 -33
  489. package/src/TopNav/TopNavMegaMenu.tsx +118 -53
  490. package/src/TopNav/TopNavMegaMenuItem.tsx +2 -9
  491. package/src/TopNav/TopNavMenu.tsx +34 -37
  492. package/src/TreeList/TreeList.doc.mjs +5 -0
  493. package/src/TreeList/TreeList.test.tsx +206 -7
  494. package/src/TreeList/TreeList.tsx +22 -0
  495. package/src/TreeList/TreeListBranches.tsx +25 -2
  496. package/src/TreeList/TreeListItem.tsx +89 -52
  497. package/src/Typeahead/BaseTypeahead.tsx +19 -6
  498. package/src/Typeahead/Typeahead.doc.mjs +1 -0
  499. package/src/Typeahead/Typeahead.test.tsx +76 -3
  500. package/src/hooks/containerReveal.stylex.ts +102 -0
  501. package/src/hooks/index.ts +4 -0
  502. package/src/hooks/useClipboard.doc.mjs +87 -0
  503. package/src/hooks/useClipboard.test.tsx +151 -0
  504. package/src/hooks/useClipboard.ts +135 -0
  505. package/src/hooks/useContainerReveal.doc.mjs +7 -7
  506. package/src/hooks/useContainerReveal.test.tsx +42 -62
  507. package/src/hooks/useContainerReveal.ts +36 -91
  508. package/src/hooks/useImageMode.test.ts +1 -1
  509. package/src/hooks/useIndicatorFocusRing.tsx +120 -0
  510. package/src/hooks/useLongPress.test.tsx +18 -0
  511. package/src/hooks/useLongPress.ts +12 -2
  512. package/src/hooks/useScrollLock.test.ts +72 -0
  513. package/src/hooks/useScrollLock.ts +51 -19
  514. package/src/hooks/useStreamingText.test.ts +46 -1
  515. package/src/hooks/useStreamingText.ts +49 -7
  516. package/src/hooks/useTreeFocus.test.tsx +50 -0
  517. package/src/hooks/useTreeFocus.ts +14 -3
  518. package/src/hooks/useTypeahead.test.tsx +150 -0
  519. package/src/hooks/useTypeahead.ts +24 -9
  520. package/src/i18n/InternationalizationProvider.doc.mjs +7 -2
  521. package/src/index.ts +1 -0
  522. package/src/theme/defineTheme.ts +24 -0
  523. package/src/theme/derivedVarRegistry.test.ts +21 -1
  524. package/src/theme/derivedVarRegistry.ts +19 -6
  525. package/src/theme/expandColorScale.test.ts +14 -0
  526. package/src/theme/expandColorScale.ts +24 -9
  527. package/src/theme/expandTypeScale.test.ts +43 -0
  528. package/src/theme/expandTypeScale.ts +17 -0
  529. package/src/theme/generateThemeRules.test.ts +61 -20
  530. package/src/theme/generateThemeRules.ts +11 -0
  531. package/src/theme/index.ts +13 -0
  532. package/src/utils/focusOutline.stylex.ts +163 -0
  533. package/src/utils/focusReturn.test.ts +42 -0
  534. package/src/utils/focusReturn.ts +31 -0
  535. package/src/utils/index.ts +1 -0
  536. package/src/utils/mergeRefs.test.ts +48 -0
  537. package/src/utils/mergeRefs.ts +12 -6
  538. package/src/utils/rtlStyles.ts +2 -2
  539. package/src/utils/themeProps.ts +26 -4
  540. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts +0 -9
  541. package/dist/CheckboxInput/checkbox.markers.stylex.d.ts.map +0 -1
  542. package/dist/CheckboxInput/checkbox.markers.stylex.js +0 -14
  543. package/dist/RadioList/radio.markers.stylex.d.ts +0 -7
  544. package/dist/RadioList/radio.markers.stylex.d.ts.map +0 -1
  545. package/dist/RadioList/radio.markers.stylex.js +0 -12
  546. package/dist/hooks/containerReveal.pool.stylex.d.ts +0 -303
  547. package/dist/hooks/containerReveal.pool.stylex.d.ts.map +0 -1
  548. package/dist/hooks/containerReveal.pool.stylex.js +0 -292
  549. package/src/CheckboxInput/checkbox.markers.stylex.ts +0 -12
  550. package/src/RadioList/radio.markers.stylex.ts +0 -10
  551. package/src/hooks/containerReveal.pool.stylex.ts +0 -571
@@ -9,13 +9,65 @@
9
9
  * SYNC: When TopNavMegaMenu changes, update tests to match new behavior
10
10
  */
11
11
 
12
- import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
13
- import {render, screen} from '@testing-library/react';
12
+ import {describe, it, expect, vi, beforeAll, afterAll, afterEach} from 'vitest';
13
+ import {render, screen, act, fireEvent} from '@testing-library/react';
14
14
  import userEvent from '@testing-library/user-event';
15
15
  import {TopNavMegaMenu} from './TopNavMegaMenu';
16
16
  import {TopNavMegaMenuItem} from './TopNavMegaMenuItem';
17
17
  import {TopNavRenderContext} from './TopNavRenderContext';
18
18
 
19
+ // =============================================================================
20
+ // Popover API mock — jsdom implements no Popover API, so default-mode tests
21
+ // that actually open/close the panel install these shims on HTMLElement.
22
+ // =============================================================================
23
+
24
+ let originalMatches: typeof HTMLElement.prototype.matches;
25
+
26
+ function installPopoverApiMock() {
27
+ originalMatches = HTMLElement.prototype.matches;
28
+ HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
29
+ // Model the browser's auto-popover stack: opening one auto popover evicts
30
+ // any currently open auto popover before showing the new one.
31
+ if (this.getAttribute('popover') === 'auto') {
32
+ document
33
+ .querySelectorAll<HTMLElement>('[popover="auto"][popover-open]')
34
+ .forEach(openPopover => {
35
+ if (openPopover === this) {
36
+ return;
37
+ }
38
+ openPopover.removeAttribute('popover-open');
39
+ const closeEvent = new Event('toggle', {bubbles: false});
40
+ Object.defineProperty(closeEvent, 'newState', {value: 'closed'});
41
+ openPopover.dispatchEvent(closeEvent);
42
+ });
43
+ }
44
+ this.setAttribute('popover-open', '');
45
+ const event = new Event('toggle', {bubbles: false});
46
+ Object.defineProperty(event, 'newState', {value: 'open'});
47
+ this.dispatchEvent(event);
48
+ });
49
+ HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
50
+ this.removeAttribute('popover-open');
51
+ const event = new Event('toggle', {bubbles: false});
52
+ Object.defineProperty(event, 'newState', {value: 'closed'});
53
+ this.dispatchEvent(event);
54
+ });
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ (HTMLElement.prototype as any).matches = function (
57
+ selector: string,
58
+ ): boolean {
59
+ if (selector === ':popover-open') {
60
+ return this.hasAttribute('popover-open');
61
+ }
62
+ return originalMatches.call(this, selector);
63
+ };
64
+ }
65
+
66
+ function restorePopoverApiMock() {
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ (HTMLElement.prototype as any).matches = originalMatches;
69
+ }
70
+
19
71
  // =============================================================================
20
72
  // Default (desktop) mode
21
73
  // =============================================================================
@@ -80,37 +132,8 @@ describe('TopNavMegaMenu — default mode', () => {
80
132
  // =============================================================================
81
133
 
82
134
  describe('TopNavMegaMenu — popup semantics', () => {
83
- // Mock the Popover API (not implemented in jsdom).
84
- const originalMatches = HTMLElement.prototype.matches;
85
-
86
- beforeAll(() => {
87
- HTMLElement.prototype.showPopover = vi.fn(function (this: HTMLElement) {
88
- this.setAttribute('popover-open', '');
89
- const event = new Event('toggle', {bubbles: false});
90
- Object.defineProperty(event, 'newState', {value: 'open'});
91
- this.dispatchEvent(event);
92
- });
93
- HTMLElement.prototype.hidePopover = vi.fn(function (this: HTMLElement) {
94
- this.removeAttribute('popover-open');
95
- const event = new Event('toggle', {bubbles: false});
96
- Object.defineProperty(event, 'newState', {value: 'closed'});
97
- this.dispatchEvent(event);
98
- });
99
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
- (HTMLElement.prototype as any).matches = function (
101
- selector: string,
102
- ): boolean {
103
- if (selector === ':popover-open') {
104
- return this.hasAttribute('popover-open');
105
- }
106
- return originalMatches.call(this, selector);
107
- };
108
- });
109
-
110
- afterAll(() => {
111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
- (HTMLElement.prototype as any).matches = originalMatches;
113
- });
135
+ beforeAll(installPopoverApiMock);
136
+ afterAll(restorePopoverApiMock);
114
137
 
115
138
  it('trigger aria-controls resolves to the popup element', async () => {
116
139
  const user = userEvent.setup();
@@ -223,6 +246,383 @@ describe('TopNavMegaMenu — popup semantics', () => {
223
246
  });
224
247
  });
225
248
 
249
+ // =============================================================================
250
+ // Hover → click guard (default mode) — issue #3121
251
+ //
252
+ // The trigger opens on hover and on click. A hover-open records a timestamp so
253
+ // the click that naturally follows a hover confirms and pins the panel for
254
+ // CLICK_GUARD_MS (the panel just appeared under the cursor) instead of toggling
255
+ // it shut. Deliberate clicks outside that window still toggle.
256
+ // =============================================================================
257
+
258
+ describe('TopNavMegaMenu — hover/click guard (default mode)', () => {
259
+ beforeAll(installPopoverApiMock);
260
+ afterAll(restorePopoverApiMock);
261
+ afterEach(() => {
262
+ vi.useRealTimers();
263
+ });
264
+
265
+ function renderMenu() {
266
+ render(
267
+ <TopNavMegaMenu
268
+ label="Products"
269
+ items={<TopNavMegaMenuItem title="Analytics" href="/analytics" />}
270
+ />,
271
+ );
272
+ return screen.getByRole('button', {name: 'Products'});
273
+ }
274
+
275
+ it('keeps the panel open when a hover-open is immediately clicked', async () => {
276
+ vi.useFakeTimers({shouldAdvanceTime: true});
277
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
278
+ const trigger = renderMenu();
279
+
280
+ // Hover opens the panel after the show delay (150ms).
281
+ await user.hover(trigger);
282
+ act(() => {
283
+ vi.advanceTimersByTime(200);
284
+ });
285
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
286
+
287
+ // The click that naturally follows the hover must NOT toggle it shut.
288
+ await user.click(trigger);
289
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
290
+
291
+ // It is still a click interaction, so it pins the panel just like a
292
+ // click-open. Leaving the trigger must not dismiss it.
293
+ await user.unhover(trigger);
294
+ act(() => {
295
+ vi.advanceTimersByTime(300);
296
+ });
297
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
298
+ });
299
+
300
+ it('closes on a click that lands well after the hover-open (past the guard)', async () => {
301
+ vi.useFakeTimers({shouldAdvanceTime: true});
302
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
303
+ const trigger = renderMenu();
304
+
305
+ await user.hover(trigger);
306
+ act(() => {
307
+ vi.advanceTimersByTime(200);
308
+ });
309
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
310
+
311
+ // Past the guard window, a click is a deliberate dismissal.
312
+ act(() => {
313
+ vi.advanceTimersByTime(600);
314
+ });
315
+ await user.click(trigger);
316
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
317
+ });
318
+
319
+ it('toggles cleanly for click-only interaction (no hover, no guard)', async () => {
320
+ const user = userEvent.setup();
321
+ const trigger = renderMenu();
322
+
323
+ // A click with no preceding hover opens...
324
+ await user.click(trigger);
325
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
326
+
327
+ // ...and the next click closes — the guard only applies after a hover-open.
328
+ await user.click(trigger);
329
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
330
+ });
331
+
332
+ it('closes a hover-opened (transient) panel when the pointer leaves', async () => {
333
+ vi.useFakeTimers({shouldAdvanceTime: true});
334
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
335
+ const trigger = renderMenu();
336
+
337
+ await user.hover(trigger);
338
+ act(() => {
339
+ vi.advanceTimersByTime(200);
340
+ });
341
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
342
+
343
+ // Leaving closes it after hideDelay (250ms) — hover opens are transient.
344
+ await user.unhover(trigger);
345
+ act(() => {
346
+ vi.advanceTimersByTime(300);
347
+ });
348
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
349
+ });
350
+
351
+ it('keeps a click-opened (pinned) panel open when the pointer leaves', async () => {
352
+ vi.useFakeTimers({shouldAdvanceTime: true});
353
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
354
+ const trigger = renderMenu();
355
+
356
+ // A click pins the panel open (a deliberate open should persist).
357
+ await user.click(trigger);
358
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
359
+
360
+ await user.unhover(trigger);
361
+ act(() => {
362
+ vi.advanceTimersByTime(400);
363
+ });
364
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
365
+ });
366
+
367
+ it('re-entering the trigger cancels a pending hide', async () => {
368
+ vi.useFakeTimers({shouldAdvanceTime: true});
369
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
370
+ const trigger = renderMenu();
371
+
372
+ await user.hover(trigger);
373
+ act(() => {
374
+ vi.advanceTimersByTime(200);
375
+ });
376
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
377
+
378
+ // Leave (schedules a hide), then return before hideDelay elapses.
379
+ await user.unhover(trigger);
380
+ act(() => {
381
+ vi.advanceTimersByTime(100);
382
+ });
383
+ await user.hover(trigger);
384
+ act(() => {
385
+ vi.advanceTimersByTime(300);
386
+ });
387
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
388
+ });
389
+
390
+ it('does not reopen when the pointer enters the panel after close', async () => {
391
+ vi.useFakeTimers({shouldAdvanceTime: true});
392
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
393
+ const trigger = renderMenu();
394
+ const panel = screen.getByRole('group', {
395
+ name: 'Products',
396
+ hidden: true,
397
+ });
398
+
399
+ await user.click(trigger);
400
+ await user.click(trigger);
401
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
402
+
403
+ // The exit transition can leave the closed panel briefly hit-testable.
404
+ fireEvent.mouseEnter(panel);
405
+ act(() => {
406
+ vi.advanceTimersByTime(200);
407
+ });
408
+
409
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
410
+ });
411
+
412
+ it('re-hovering an open panel does not refresh the click guard', async () => {
413
+ vi.useFakeTimers({shouldAdvanceTime: true});
414
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
415
+ const trigger = renderMenu();
416
+
417
+ await user.hover(trigger);
418
+ act(() => {
419
+ vi.advanceTimersByTime(200);
420
+ });
421
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
422
+
423
+ // Wait past the guard window, re-hovering along the way. The re-hover must
424
+ // NOT re-stamp the guard timestamp (only a fresh open may), so the click
425
+ // below is still judged a deliberate close.
426
+ act(() => {
427
+ vi.advanceTimersByTime(600);
428
+ });
429
+ await user.hover(trigger);
430
+ act(() => {
431
+ vi.advanceTimersByTime(200);
432
+ });
433
+ await user.click(trigger);
434
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
435
+ });
436
+
437
+ it('opening a sibling mega menu closes the pinned menu through the native auto-popover stack', async () => {
438
+ vi.useFakeTimers({shouldAdvanceTime: true});
439
+ const user = userEvent.setup({advanceTimers: vi.advanceTimersByTime});
440
+ render(
441
+ <>
442
+ <TopNavMegaMenu
443
+ label="Products"
444
+ items={<TopNavMegaMenuItem title="Analytics" href="/analytics" />}
445
+ />
446
+ <TopNavMegaMenu
447
+ label="Solutions"
448
+ items={<TopNavMegaMenuItem title="Enterprise" href="/enterprise" />}
449
+ />
450
+ </>,
451
+ );
452
+
453
+ const products = screen.getByRole('button', {name: 'Products'});
454
+ const solutions = screen.getByRole('button', {name: 'Solutions'});
455
+
456
+ await user.click(products);
457
+ expect(products).toHaveAttribute('aria-expanded', 'true');
458
+
459
+ await user.hover(solutions);
460
+ act(() => {
461
+ vi.advanceTimersByTime(200);
462
+ });
463
+
464
+ expect(products).toHaveAttribute('aria-expanded', 'false');
465
+ expect(solutions).toHaveAttribute('aria-expanded', 'true');
466
+ });
467
+ });
468
+
469
+ // =============================================================================
470
+ // Dismissal (default mode) — issue #3121
471
+ //
472
+ // The panel stays an *auto* popover for native light dismiss and stack
473
+ // exclusivity. `popoverTarget` makes the button its native invoker so trigger
474
+ // activation is not treated as an outside click before the guard can run.
475
+ // jsdom cannot exercise that native event ordering; these tests lock in the
476
+ // required wiring, while the end-to-end interaction is browser-verified in the
477
+ // PR's manual test plan.
478
+ // =============================================================================
479
+
480
+ describe('TopNavMegaMenu — dismissal (default mode)', () => {
481
+ beforeAll(installPopoverApiMock);
482
+ afterAll(restorePopoverApiMock);
483
+
484
+ function renderAndOpen() {
485
+ render(
486
+ <TopNavMegaMenu
487
+ label="Products"
488
+ items={<TopNavMegaMenuItem title="Analytics" href="/analytics" />}
489
+ />,
490
+ );
491
+ return screen.getByRole('button', {name: 'Products'});
492
+ }
493
+
494
+ it('renders an auto popover and registers the trigger as its native invoker', () => {
495
+ const trigger = renderAndOpen();
496
+ const popup = document.getElementById(
497
+ trigger.getAttribute('aria-controls') as string,
498
+ );
499
+ expect(popup).toHaveAttribute('popover', 'auto');
500
+ expect(trigger).toHaveAttribute('popovertarget', popup?.id);
501
+ });
502
+
503
+ it('prevents the native invoker toggle so the click guard owns activation', async () => {
504
+ const trigger = renderAndOpen();
505
+
506
+ const clickEvent = new MouseEvent('click', {
507
+ bubbles: true,
508
+ cancelable: true,
509
+ detail: 1,
510
+ });
511
+ fireEvent(trigger, clickEvent);
512
+
513
+ expect(clickEvent.defaultPrevented).toBe(true);
514
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
515
+ });
516
+
517
+ it('reconciles a browser-initiated native light dismiss', async () => {
518
+ const user = userEvent.setup();
519
+ const trigger = renderAndOpen();
520
+
521
+ await user.click(trigger);
522
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
523
+
524
+ const popup = document.getElementById(
525
+ trigger.getAttribute('aria-controls') as string,
526
+ ) as HTMLElement;
527
+ popup.removeAttribute('popover-open');
528
+ const toggleEvent = new Event('toggle', {bubbles: false});
529
+ Object.defineProperty(toggleEvent, 'newState', {value: 'closed'});
530
+ fireEvent(popup, toggleEvent);
531
+
532
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
533
+ });
534
+ });
535
+
536
+ // =============================================================================
537
+ // Keyboard (default mode) — issue #3121
538
+ //
539
+ // Keyboard activation always OPENS (never toggles the panel closed) and moves
540
+ // focus into the panel; Escape closes it and returns focus to the trigger.
541
+ // =============================================================================
542
+
543
+ describe('TopNavMegaMenu — keyboard (default mode)', () => {
544
+ beforeAll(installPopoverApiMock);
545
+ afterAll(restorePopoverApiMock);
546
+
547
+ it('opens on Enter (keyboard activation)', async () => {
548
+ const user = userEvent.setup();
549
+ render(
550
+ <TopNavMegaMenu
551
+ label="Products"
552
+ items={<TopNavMegaMenuItem title="Analytics" href="/analytics" />}
553
+ />,
554
+ );
555
+ const trigger = screen.getByRole('button', {name: 'Products'});
556
+ trigger.focus();
557
+
558
+ await user.keyboard('{Enter}');
559
+
560
+ // aria-expanded is the observable signal. Enter also auto-focuses the first
561
+ // link (unlike pointer/hover opens, which keep focus on the trigger), but
562
+ // that focus landing is only verifiable in a real browser — jsdom renders
563
+ // the popover content display:none, so activeElement can't move onto it
564
+ // (browser-verified via the Playwright checks for #3121). Panel content
565
+ // presence is deliberately NOT asserted: useLayer renders it into the DOM
566
+ // even while closed, so such an assertion would be vacuous.
567
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
568
+ });
569
+
570
+ it('opens on Space', async () => {
571
+ const user = userEvent.setup();
572
+ render(
573
+ <TopNavMegaMenu
574
+ label="Products"
575
+ items={<TopNavMegaMenuItem title="Analytics" href="/analytics" />}
576
+ />,
577
+ );
578
+ const trigger = screen.getByRole('button', {name: 'Products'});
579
+ trigger.focus();
580
+
581
+ await user.keyboard(' ');
582
+
583
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
584
+ });
585
+
586
+ it('keyboard activation never toggles an open panel closed', async () => {
587
+ const user = userEvent.setup();
588
+ render(
589
+ <TopNavMegaMenu
590
+ label="Products"
591
+ items={<TopNavMegaMenuItem title="Analytics" href="/analytics" />}
592
+ />,
593
+ );
594
+ const trigger = screen.getByRole('button', {name: 'Products'});
595
+
596
+ // Open via pointer (focus stays on the trigger).
597
+ await user.click(trigger);
598
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
599
+
600
+ // Enter on an already-open panel keeps it open — no toggle-close.
601
+ trigger.focus();
602
+ await user.keyboard('{Enter}');
603
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
604
+ });
605
+
606
+ it('closes on Escape and returns focus to the trigger', async () => {
607
+ const user = userEvent.setup();
608
+ render(
609
+ <TopNavMegaMenu
610
+ label="Products"
611
+ items={<TopNavMegaMenuItem title="Analytics" href="/analytics" />}
612
+ />,
613
+ );
614
+ const trigger = screen.getByRole('button', {name: 'Products'});
615
+
616
+ // Pointer open leaves focus on the trigger.
617
+ await user.click(trigger);
618
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
619
+
620
+ await user.keyboard('{Escape}');
621
+ expect(trigger).toHaveAttribute('aria-expanded', 'false');
622
+ expect(trigger).toHaveFocus();
623
+ });
624
+ });
625
+
226
626
  // =============================================================================
227
627
  // Mobile-bar mode — should be hidden
228
628
  // =============================================================================