@astryxdesign/core 0.7.5 → 0.7.6-canary.0ef60dd

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 (559) hide show
  1. package/dist/AlertDialog/AlertDialog.d.ts +1 -1
  2. package/dist/AlertDialog/AlertDialog.d.ts.map +1 -1
  3. package/dist/AlertDialog/AlertDialog.js +5 -5
  4. package/dist/Avatar/Avatar.d.ts.map +1 -1
  5. package/dist/Avatar/Avatar.js +12 -3
  6. package/dist/Badge/Badge.d.ts.map +1 -1
  7. package/dist/Badge/Badge.js +7 -1
  8. package/dist/BottomSheet/BottomSheet.js +1 -1
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  10. package/dist/BottomSheet/BottomSheetPanel.js +2 -1
  11. package/dist/BottomSheet/useSheetGestures.d.ts +12 -0
  12. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  13. package/dist/BottomSheet/useSheetGestures.js +30 -3
  14. package/dist/Button/Button.js +4 -4
  15. package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
  16. package/dist/CheckboxInput/CheckboxInput.js +6 -5
  17. package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
  18. package/dist/ClickableCard/ClickableCard.js +37 -4
  19. package/dist/DateInput/DateInput.d.ts.map +1 -1
  20. package/dist/DateInput/DateInput.js +12 -4
  21. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  22. package/dist/DateInput/MonthScroller.js +101 -15
  23. package/dist/DateInput/TouchDateField.d.ts.map +1 -1
  24. package/dist/DateInput/TouchDateField.js +8 -3
  25. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  26. package/dist/DateRangeInput/DateRangeInput.js +1 -1
  27. package/dist/DateTimeInput/DateTimeInput.d.ts +5 -2
  28. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  29. package/dist/DateTimeInput/DateTimeInput.js +23 -12
  30. package/dist/DateTimeInput/NativeTimeSegment.d.ts.map +1 -1
  31. package/dist/DateTimeInput/NativeTimeSegment.js +5 -4
  32. package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
  33. package/dist/DateTimeInput/TouchDateTimeField.js +4 -3
  34. package/dist/Dialog/Dialog.d.ts.map +1 -1
  35. package/dist/Dialog/Dialog.js +34 -9
  36. package/dist/Dialog/DialogHeader.d.ts.map +1 -1
  37. package/dist/Dialog/DialogHeader.js +5 -4
  38. package/dist/Divider/Divider.d.ts.map +1 -1
  39. package/dist/Divider/Divider.js +2 -1
  40. package/dist/Drawer/Drawer.d.ts +1 -0
  41. package/dist/Drawer/Drawer.d.ts.map +1 -1
  42. package/dist/Drawer/Drawer.js +7 -2
  43. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  44. package/dist/DropdownMenu/DropdownMenu.js +7 -3
  45. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  46. package/dist/DropdownMenu/MenuBottomSheetActionList.js +6 -5
  47. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts +20 -2
  48. package/dist/DropdownMenu/lazyMenuBottomSheet.d.ts.map +1 -1
  49. package/dist/DropdownMenu/lazyMenuBottomSheet.js +31 -3
  50. package/dist/DropdownMenu/renderDropdownItems.d.ts.map +1 -1
  51. package/dist/DropdownMenu/renderDropdownItems.js +21 -16
  52. package/dist/EmptyState/EmptyState.d.ts.map +1 -1
  53. package/dist/EmptyState/EmptyState.js +2 -1
  54. package/dist/Field/FieldLabel.d.ts +1 -1
  55. package/dist/Field/FieldLabel.d.ts.map +1 -1
  56. package/dist/Field/FieldLabel.js +4 -4
  57. package/dist/Field/InputClearButton.js +3 -3
  58. package/dist/Field/PanelSearchInput.js +2 -2
  59. package/dist/FieldStatus/FieldStatus.js +2 -2
  60. package/dist/FileInput/FileInput.d.ts.map +1 -1
  61. package/dist/FileInput/FileInput.js +7 -1
  62. package/dist/Heading/Heading.js +1 -1
  63. package/dist/Icon/Icon.js +1 -1
  64. package/dist/Indicator/CheckIndicator.d.ts.map +1 -1
  65. package/dist/Indicator/CheckIndicator.js +2 -1
  66. package/dist/Indicator/CheckboxIndicator.d.ts.map +1 -1
  67. package/dist/Indicator/CheckboxIndicator.js +3 -1
  68. package/dist/Indicator/RadioIndicator.d.ts.map +1 -1
  69. package/dist/Indicator/RadioIndicator.js +3 -1
  70. package/dist/Item/Item.d.ts.map +1 -1
  71. package/dist/Item/Item.js +2 -1
  72. package/dist/Item/ItemSwipeLayer.js +1 -1
  73. package/dist/Layer/popoverOperations.d.ts +27 -0
  74. package/dist/Layer/popoverOperations.d.ts.map +1 -0
  75. package/dist/Layer/popoverOperations.js +159 -0
  76. package/dist/Layer/useLayer.d.ts +1 -1
  77. package/dist/Layer/useLayer.d.ts.map +1 -1
  78. package/dist/Layer/useLayer.js +93 -59
  79. package/dist/Layout/Layout.js +1 -1
  80. package/dist/Layout/LayoutContent.js +1 -1
  81. package/dist/Layout/LayoutFooter.js +1 -1
  82. package/dist/Layout/LayoutHeader.js +1 -1
  83. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  84. package/dist/Lightbox/Lightbox.js +5 -0
  85. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +5 -4
  86. package/dist/List/List.js +1 -1
  87. package/dist/List/ListItem.js +3 -3
  88. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  89. package/dist/MobileNav/MobileNav.js +7 -2
  90. package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +7 -6
  91. package/dist/MultiSelector/MultiSelector.d.ts +1 -1
  92. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  93. package/dist/MultiSelector/MultiSelector.js +26 -10
  94. package/dist/NumberInput/NumberInput.d.ts +3 -1
  95. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  96. package/dist/NumberInput/NumberInput.js +3 -3
  97. package/dist/NumberInput/numberParser.d.ts +3 -0
  98. package/dist/NumberInput/numberParser.d.ts.map +1 -1
  99. package/dist/NumberInput/numberParser.js +7 -44
  100. package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
  101. package/dist/ProgressBar/ProgressBar.js +16 -12
  102. package/dist/Resizable/useResizable.d.ts.map +1 -1
  103. package/dist/Resizable/useResizable.js +28 -2
  104. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  105. package/dist/SegmentedControl/SegmentedControlItem.js +4 -1
  106. package/dist/Selector/useSelectorPresentation.d.ts +8 -1
  107. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  108. package/dist/Selector/useSelectorPresentation.js +20 -4
  109. package/dist/Skeleton/Skeleton.d.ts.map +1 -1
  110. package/dist/Skeleton/Skeleton.js +1 -1
  111. package/dist/Slider/Slider.d.ts.map +1 -1
  112. package/dist/Slider/Slider.js +10 -2
  113. package/dist/Spinner/Spinner.d.ts.map +1 -1
  114. package/dist/Spinner/Spinner.js +3 -3
  115. package/dist/Stack/Stack.js +1 -1
  116. package/dist/Stack/StackItem.js +1 -1
  117. package/dist/TabList/TabList.d.ts.map +1 -1
  118. package/dist/TabList/TabList.js +16 -1
  119. package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
  120. package/dist/Table/plugins/columnResize/useTableColumnResize.js +9 -1
  121. package/dist/Text/Text.js +1 -1
  122. package/dist/Text/useTruncation.d.ts.map +1 -1
  123. package/dist/Text/useTruncation.js +23 -5
  124. package/dist/TextArea/TextArea.d.ts +1 -1
  125. package/dist/TextArea/TextArea.d.ts.map +1 -1
  126. package/dist/TextArea/TextArea.js +13 -2
  127. package/dist/TextInput/TextInput.d.ts.map +1 -1
  128. package/dist/TextInput/TextInput.js +3 -3
  129. package/dist/TimeInput/TimeInput.d.ts +6 -2
  130. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  131. package/dist/TimeInput/TimeInput.js +21 -14
  132. package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
  133. package/dist/TimeInput/TouchTimeField.js +5 -4
  134. package/dist/Timestamp/Timestamp.d.ts.map +1 -1
  135. package/dist/Timestamp/Timestamp.js +2 -2
  136. package/dist/Timestamp/TimestampHoverCard.js +4 -4
  137. package/dist/Toast/Toast.d.ts.map +1 -1
  138. package/dist/Toast/Toast.js +60 -20
  139. package/dist/Toast/ToastViewport.d.ts.map +1 -1
  140. package/dist/Toast/ToastViewport.js +82 -51
  141. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  142. package/dist/Tokenizer/Tokenizer.js +13 -2
  143. package/dist/Tokenizer/TokenizerBottomSheet.js +3 -3
  144. package/dist/Toolbar/Toolbar.d.ts.map +1 -1
  145. package/dist/Toolbar/Toolbar.js +2 -1
  146. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  147. package/dist/TopNav/TopNavMegaMenu.js +3 -0
  148. package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
  149. package/dist/TopNav/TopNavMenu.js +1 -0
  150. package/dist/Typeahead/Typeahead.d.ts +1 -1
  151. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  152. package/dist/Typeahead/Typeahead.js +9 -1
  153. package/dist/astryx.css +20 -4
  154. package/dist/gestures/GestureArena.d.ts +6 -0
  155. package/dist/gestures/GestureArena.d.ts.map +1 -1
  156. package/dist/gestures/GestureArena.js +39 -0
  157. package/dist/gestures/GestureHost.d.ts +352 -0
  158. package/dist/gestures/GestureHost.d.ts.map +1 -0
  159. package/dist/gestures/GestureHost.js +1773 -0
  160. package/dist/gestures/GestureRecognizer.d.ts +10 -0
  161. package/dist/gestures/GestureRecognizer.d.ts.map +1 -1
  162. package/dist/gestures/GestureRecognizer.js +16 -0
  163. package/dist/gestures/LongPressGestureRecognizer.d.ts +1 -0
  164. package/dist/gestures/LongPressGestureRecognizer.d.ts.map +1 -1
  165. package/dist/gestures/LongPressGestureRecognizer.js +3 -0
  166. package/dist/gestures/PanGestureRecognizer.d.ts +1 -0
  167. package/dist/gestures/PanGestureRecognizer.d.ts.map +1 -1
  168. package/dist/gestures/PanGestureRecognizer.js +3 -0
  169. package/dist/gestures/PinchGestureRecognizer.d.ts +31 -0
  170. package/dist/gestures/PinchGestureRecognizer.d.ts.map +1 -0
  171. package/dist/gestures/PinchGestureRecognizer.js +128 -0
  172. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts +71 -0
  173. package/dist/gestures/ScreenEdgePanGestureRecognizer.d.ts.map +1 -0
  174. package/dist/gestures/ScreenEdgePanGestureRecognizer.js +223 -0
  175. package/dist/gestures/SwipeGestureRecognizer.d.ts +37 -0
  176. package/dist/gestures/SwipeGestureRecognizer.d.ts.map +1 -0
  177. package/dist/gestures/SwipeGestureRecognizer.js +148 -0
  178. package/dist/gestures/TapGestureRecognizer.d.ts +1 -0
  179. package/dist/gestures/TapGestureRecognizer.d.ts.map +1 -1
  180. package/dist/gestures/TapGestureRecognizer.js +3 -0
  181. package/dist/gestures/VelocityTracker.d.ts +6 -2
  182. package/dist/gestures/VelocityTracker.d.ts.map +1 -1
  183. package/dist/gestures/VelocityTracker.js +7 -3
  184. package/dist/gestures/haptics.d.ts +65 -0
  185. package/dist/gestures/haptics.d.ts.map +1 -0
  186. package/dist/gestures/haptics.js +182 -0
  187. package/dist/gestures/nativeScroll.d.ts +42 -0
  188. package/dist/gestures/nativeScroll.d.ts.map +1 -0
  189. package/dist/gestures/nativeScroll.js +228 -0
  190. package/dist/gestures/touchAction.d.ts +60 -0
  191. package/dist/gestures/touchAction.d.ts.map +1 -0
  192. package/dist/gestures/touchAction.js +157 -0
  193. package/dist/gestures/touchActionRule.d.ts +34 -0
  194. package/dist/gestures/touchActionRule.d.ts.map +1 -0
  195. package/dist/gestures/touchActionRule.js +140 -0
  196. package/dist/gestures/types.d.ts +18 -7
  197. package/dist/gestures/types.d.ts.map +1 -1
  198. package/dist/gestures/types.js +22 -2
  199. package/dist/hooks/index.d.ts +1 -1
  200. package/dist/hooks/index.d.ts.map +1 -1
  201. package/dist/hooks/modalAnnounce.d.ts.map +1 -1
  202. package/dist/hooks/modalAnnounce.js +107 -61
  203. package/dist/hooks/useAdaptivePresentation.d.ts.map +1 -1
  204. package/dist/hooks/useAdaptivePresentation.js +4 -1
  205. package/dist/hooks/useAnnounce.d.ts +101 -42
  206. package/dist/hooks/useAnnounce.d.ts.map +1 -1
  207. package/dist/hooks/useAnnounce.js +161 -75
  208. package/dist/hooks/useAnnounceRenderedText.d.ts +11 -3
  209. package/dist/hooks/useAnnounceRenderedText.d.ts.map +1 -1
  210. package/dist/hooks/useAnnounceRenderedText.js +5 -3
  211. package/dist/hooks/useBusyAnnouncement.d.ts.map +1 -1
  212. package/dist/hooks/useBusyAnnouncement.js +6 -12
  213. package/dist/hooks/useMenuHover.d.ts.map +1 -1
  214. package/dist/hooks/useMenuHover.js +12 -1
  215. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  216. package/dist/i18n/generated-locales/en.generated.js +5 -0
  217. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  218. package/dist/i18n/generated-locales/pseudo.generated.js +5 -0
  219. package/dist/motion/animator.d.ts +7 -1
  220. package/dist/motion/animator.d.ts.map +1 -1
  221. package/dist/motion/animator.js +119 -27
  222. package/dist/theme/defineTheme.d.ts +19 -5
  223. package/dist/theme/defineTheme.d.ts.map +1 -1
  224. package/dist/theme/defineTheme.js +64 -4
  225. package/dist/theme/domainTokens/domainTokenDefaults.d.ts +3 -0
  226. package/dist/theme/domainTokens/domainTokenDefaults.d.ts.map +1 -0
  227. package/dist/theme/domainTokens/domainTokenDefaults.js +19 -0
  228. package/dist/theme/domainTokens/index.d.ts +5 -4
  229. package/dist/theme/domainTokens/index.d.ts.map +1 -1
  230. package/dist/theme/domainTokens/index.js +5 -9
  231. package/dist/theme/index.d.ts +1 -0
  232. package/dist/theme/index.d.ts.map +1 -1
  233. package/dist/theme/motionKnobs.d.ts +56 -0
  234. package/dist/theme/motionKnobs.d.ts.map +1 -0
  235. package/dist/theme/motionKnobs.js +48 -0
  236. package/dist/theme/resolveThemeValues.d.ts +11 -4
  237. package/dist/theme/resolveThemeValues.d.ts.map +1 -1
  238. package/dist/theme/resolveThemeValues.js +40 -4
  239. package/dist/theme/themeAdaptations.d.ts +7 -3
  240. package/dist/theme/themeAdaptations.d.ts.map +1 -1
  241. package/dist/theme/themeAdaptations.js +36 -7
  242. package/dist/theme/themeMotion.d.ts +96 -0
  243. package/dist/theme/themeMotion.d.ts.map +1 -0
  244. package/dist/theme/themeMotion.js +177 -0
  245. package/dist/theme/tokenReferences.d.ts +31 -0
  246. package/dist/theme/tokenReferences.d.ts.map +1 -0
  247. package/dist/theme/tokenReferences.js +132 -0
  248. package/dist/theme/tokens.d.ts +1 -0
  249. package/dist/theme/tokens.d.ts.map +1 -1
  250. package/dist/theme/tokens.js +9 -129
  251. package/dist/theme/tokens.stylex.d.ts +12 -0
  252. package/dist/theme/tokens.stylex.d.ts.map +1 -1
  253. package/dist/theme/tokens.stylex.js +12 -0
  254. package/dist/utils/focusReturn.d.ts +17 -1
  255. package/dist/utils/focusReturn.d.ts.map +1 -1
  256. package/dist/utils/focusReturn.js +11 -4
  257. package/dist/utils/getNodeText.d.ts +8 -0
  258. package/dist/utils/getNodeText.d.ts.map +1 -0
  259. package/dist/utils/getNodeText.js +37 -0
  260. package/dist/utils/interactionModality.d.ts +11 -2
  261. package/dist/utils/interactionModality.d.ts.map +1 -1
  262. package/dist/utils/interactionModality.js +58 -5
  263. package/dist/utils/interactionOverlay.stylex.d.ts.map +1 -1
  264. package/dist/utils/interactionOverlay.stylex.js +17 -8
  265. package/dist/utils/plainDate.d.ts +2 -0
  266. package/dist/utils/plainDate.d.ts.map +1 -1
  267. package/dist/utils/plainDate.js +46 -3
  268. package/dist/utils/presentation.d.ts +50 -0
  269. package/dist/utils/presentation.d.ts.map +1 -0
  270. package/dist/utils/presentation.js +125 -0
  271. package/dist/utils/pressFeedback.d.ts.map +1 -1
  272. package/dist/utils/pressFeedback.js +20 -1
  273. package/dist/utils/primaryClick.d.ts +11 -0
  274. package/dist/utils/primaryClick.d.ts.map +1 -0
  275. package/dist/utils/primaryClick.js +25 -0
  276. package/dist/utils/timeFormat.d.ts +47 -0
  277. package/dist/utils/timeFormat.d.ts.map +1 -0
  278. package/dist/utils/timeFormat.js +240 -0
  279. package/dist/utils/touchPresentation.d.ts.map +1 -1
  280. package/dist/utils/touchPresentation.js +5 -3
  281. package/dist/utils/touchPresentation.stylex.d.ts +15 -0
  282. package/dist/utils/touchPresentation.stylex.d.ts.map +1 -0
  283. package/dist/utils/touchPresentation.stylex.js +33 -0
  284. package/dist/utils/unicodeDigits.d.ts +20 -0
  285. package/dist/utils/unicodeDigits.d.ts.map +1 -0
  286. package/dist/utils/unicodeDigits.js +70 -0
  287. package/locales/en.json +23 -3
  288. package/locales/pseudo.json +15 -0
  289. package/package.json +5 -4
  290. package/src/AlertDialog/AlertDialog.tsx +6 -5
  291. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +18 -6
  292. package/src/Avatar/Avatar.spec.md +22 -22
  293. package/src/Avatar/Avatar.tsx +18 -1
  294. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +126 -0
  295. package/src/Badge/Badge.doc.mjs +1 -1
  296. package/src/Badge/Badge.tsx +19 -2
  297. package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +260 -0
  298. package/src/BottomSheet/BottomSheet.test.tsx +46 -0
  299. package/src/BottomSheet/BottomSheet.tsx +1 -1
  300. package/src/BottomSheet/BottomSheetPanel.tsx +2 -1
  301. package/src/BottomSheet/useSheetGestures.test.ts +52 -0
  302. package/src/BottomSheet/useSheetGestures.ts +37 -6
  303. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +18 -6
  304. package/src/Button/Button.tsx +4 -4
  305. package/src/Calendar/Calendar.spec.md +87 -48
  306. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +21 -7
  307. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +21 -7
  308. package/src/CheckboxInput/CheckboxInput.tsx +6 -5
  309. package/src/ClickableCard/ClickableCard.test.tsx +96 -0
  310. package/src/ClickableCard/ClickableCard.tsx +48 -4
  311. package/src/Collapsible/Collapsible.spec.md +2 -0
  312. package/src/Collapsible/CollapsibleGroup.spec.md +57 -29
  313. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +18 -7
  314. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +18 -6
  315. package/src/ComplexSelector/ComplexSelector.test.tsx +37 -0
  316. package/src/ContextMenu/ContextMenu.test.tsx +35 -0
  317. package/src/DateInput/DateInput.test.tsx +115 -3
  318. package/src/DateInput/DateInput.tsx +29 -14
  319. package/src/DateInput/DateInputTouch.test.tsx +76 -0
  320. package/src/DateInput/MonthScroller.tsx +106 -20
  321. package/src/DateInput/MonthScrollerSettle.test.tsx +165 -0
  322. package/src/DateInput/TouchDateField.tsx +14 -3
  323. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +312 -0
  324. package/src/DateRangeInput/DateRangeInput.test.tsx +46 -3
  325. package/src/DateRangeInput/DateRangeInput.tsx +16 -12
  326. package/src/DateTimeInput/DateTimeInput.doc.mjs +10 -8
  327. package/src/DateTimeInput/DateTimeInput.spec.md +75 -51
  328. package/src/DateTimeInput/DateTimeInput.test.tsx +81 -0
  329. package/src/DateTimeInput/DateTimeInput.tsx +46 -24
  330. package/src/DateTimeInput/NativeTimeSegment.tsx +4 -6
  331. package/src/DateTimeInput/TouchDateTimeField.tsx +3 -7
  332. package/src/Dialog/Dialog.test.tsx +216 -2
  333. package/src/Dialog/Dialog.tsx +42 -9
  334. package/src/Dialog/DialogHeader.tsx +5 -4
  335. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +202 -0
  336. package/src/Divider/Divider.tsx +2 -1
  337. package/src/Drawer/Drawer.spec.md +111 -80
  338. package/src/Drawer/Drawer.test.tsx +35 -0
  339. package/src/Drawer/Drawer.tsx +13 -2
  340. package/src/Drawer/DrawerHeader.spec.md +181 -0
  341. package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +267 -0
  342. package/src/DropdownMenu/DropdownMenu.test.tsx +41 -0
  343. package/src/DropdownMenu/DropdownMenu.tsx +10 -3
  344. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +34 -0
  345. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -5
  346. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +19 -7
  347. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +21 -9
  348. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +60 -0
  349. package/src/DropdownMenu/lazyMenuBottomSheet.ts +36 -7
  350. package/src/DropdownMenu/renderDropdownItems.tsx +24 -19
  351. package/src/EmptyState/EmptyState.tsx +2 -1
  352. package/src/Field/FieldLabel.tsx +5 -4
  353. package/src/Field/InputClearButton.tsx +3 -3
  354. package/src/Field/PanelSearchInput.tsx +2 -2
  355. package/src/FieldStatus/FieldStatus.tsx +2 -2
  356. package/src/FileInput/FileInput.test.tsx +21 -0
  357. package/src/FileInput/FileInput.tsx +4 -1
  358. package/src/FormLayout/FormLayout.spec.md +7 -8
  359. package/src/Heading/Heading.tsx +1 -1
  360. package/src/Icon/Icon.tsx +1 -1
  361. package/src/Indicator/CheckIndicator.spec.md +47 -42
  362. package/src/Indicator/CheckIndicator.tsx +2 -1
  363. package/src/Indicator/CheckboxIndicator.spec.md +40 -37
  364. package/src/Indicator/CheckboxIndicator.tsx +3 -1
  365. package/src/Indicator/Indicator.spec.md +188 -0
  366. package/src/Indicator/RadioIndicator.tsx +3 -1
  367. package/src/Item/Item.tsx +2 -1
  368. package/src/Item/ItemSwipeLayer.tsx +1 -1
  369. package/src/Kbd/Kbd.spec.md +81 -38
  370. package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +126 -0
  371. package/src/Layer/popoverOperations.test.ts +205 -0
  372. package/src/Layer/popoverOperations.ts +172 -0
  373. package/src/Layer/useLayer.test.tsx +348 -1
  374. package/src/Layer/useLayer.tsx +118 -71
  375. package/src/Layout/Layout.spec.md +127 -131
  376. package/src/Layout/Layout.tsx +1 -1
  377. package/src/Layout/LayoutContent.spec.md +205 -0
  378. package/src/Layout/LayoutContent.tsx +1 -1
  379. package/src/Layout/LayoutFooter.spec.md +190 -0
  380. package/src/Layout/LayoutFooter.tsx +1 -1
  381. package/src/Layout/LayoutHeader.spec.md +193 -0
  382. package/src/Layout/LayoutHeader.tsx +1 -1
  383. package/src/Layout/LayoutPanel.spec.md +194 -0
  384. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +18 -6
  385. package/src/Lightbox/Lightbox.test.tsx +18 -0
  386. package/src/Lightbox/Lightbox.tsx +5 -0
  387. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +8 -4
  388. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +18 -8
  389. package/src/List/List.tsx +1 -1
  390. package/src/List/ListItem.spec.md +205 -0
  391. package/src/List/ListItem.tsx +3 -3
  392. package/src/MetadataList/MetadataList.spec.md +187 -0
  393. package/src/MetadataList/MetadataListItem.spec.md +165 -0
  394. package/src/MobileNav/MobileNav.test.tsx +15 -0
  395. package/src/MobileNav/MobileNav.tsx +9 -1
  396. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +89 -0
  397. package/src/MoreMenu/MoreMenu.test.tsx +11 -0
  398. package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +7 -8
  399. package/src/MultiSelector/MultiSelector.spec.md +7 -4
  400. package/src/MultiSelector/MultiSelector.test.tsx +270 -1
  401. package/src/MultiSelector/MultiSelector.tsx +25 -10
  402. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +8 -1
  403. package/src/NavMenu/NavHeadingMenu.spec.md +1 -5
  404. package/src/NumberInput/NumberInput.doc.mjs +13 -7
  405. package/src/NumberInput/NumberInput.spec.md +59 -37
  406. package/src/NumberInput/NumberInput.tsx +13 -3
  407. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +102 -0
  408. package/src/NumberInput/numberParser.ts +7 -45
  409. package/src/ProgressBar/ProgressBar.test.tsx +108 -32
  410. package/src/ProgressBar/ProgressBar.tsx +58 -10
  411. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +21 -9
  412. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +831 -0
  413. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +21 -14
  414. package/src/Resizable/useResizable.test.ts +43 -0
  415. package/src/Resizable/useResizable.ts +32 -9
  416. package/src/SegmentedControl/SegmentedControl.test.tsx +84 -0
  417. package/src/SegmentedControl/SegmentedControlItem.tsx +11 -5
  418. package/src/Selector/Selector.test.tsx +90 -1
  419. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +8 -1
  420. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +8 -1
  421. package/src/Selector/useSelectorPresentation.ts +31 -3
  422. package/src/SideNav/SideNav.test.tsx +27 -0
  423. package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +176 -51
  424. package/src/Skeleton/Skeleton.tsx +3 -1
  425. package/src/Slider/Slider.test.tsx +31 -0
  426. package/src/Slider/Slider.tsx +8 -3
  427. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +19 -7
  428. package/src/Spinner/Spinner.tsx +5 -3
  429. package/src/Stack/Stack.tsx +1 -1
  430. package/src/Stack/StackItem.tsx +1 -1
  431. package/src/TabList/TabList.test.tsx +58 -4
  432. package/src/TabList/TabList.tsx +16 -1
  433. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +163 -0
  434. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +18 -6
  435. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +22 -0
  436. package/src/Table/plugins/columnResize/useTableColumnResize.tsx +8 -4
  437. package/src/Text/Text.tsx +1 -1
  438. package/src/Text/useTruncation.test.tsx +128 -1
  439. package/src/Text/useTruncation.ts +23 -5
  440. package/src/TextArea/TextArea.spec.md +10 -13
  441. package/src/TextArea/TextArea.test.tsx +60 -0
  442. package/src/TextArea/TextArea.tsx +15 -1
  443. package/src/TextInput/TextInput.test.tsx +21 -0
  444. package/src/TextInput/TextInput.tsx +3 -1
  445. package/src/TimeInput/NativeTimeInput.test.tsx +17 -0
  446. package/src/TimeInput/Presentation.test.tsx +21 -0
  447. package/src/TimeInput/TimeInput.doc.mjs +8 -8
  448. package/src/TimeInput/TimeInput.spec.md +45 -48
  449. package/src/TimeInput/TimeInput.test.tsx +76 -0
  450. package/src/TimeInput/TimeInput.tsx +36 -18
  451. package/src/TimeInput/TouchTimeField.tsx +4 -6
  452. package/src/Timestamp/Timestamp.spec.md +6 -12
  453. package/src/Timestamp/Timestamp.test.tsx +30 -5
  454. package/src/Timestamp/Timestamp.tsx +6 -2
  455. package/src/Timestamp/TimestampHoverCard.tsx +4 -4
  456. package/src/Toast/Toast.spec.md +14 -20
  457. package/src/Toast/Toast.tsx +104 -36
  458. package/src/Toast/ToastViewport.test.tsx +719 -1
  459. package/src/Toast/ToastViewport.tsx +100 -50
  460. package/src/Tokenizer/Tokenizer.spec.md +5 -3
  461. package/src/Tokenizer/Tokenizer.test.tsx +68 -0
  462. package/src/Tokenizer/Tokenizer.tsx +14 -2
  463. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +15 -4
  464. package/src/Tokenizer/TokenizerBottomSheet.tsx +3 -3
  465. package/src/Toolbar/Toolbar.spec.md +5 -6
  466. package/src/Toolbar/Toolbar.test.tsx +30 -0
  467. package/src/Toolbar/Toolbar.tsx +2 -1
  468. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +18 -6
  469. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +18 -7
  470. package/src/TopNav/TopNav.test.tsx +27 -0
  471. package/src/TopNav/TopNavMegaMenu.test.tsx +21 -0
  472. package/src/TopNav/TopNavMegaMenu.tsx +4 -1
  473. package/src/TopNav/TopNavMenu.test.tsx +17 -0
  474. package/src/TopNav/TopNavMenu.tsx +1 -0
  475. package/src/Typeahead/Typeahead.test.tsx +40 -0
  476. package/src/Typeahead/Typeahead.tsx +12 -1
  477. package/src/__tests__/pressState.ts +8 -4
  478. package/src/gestures/GestureArena.ts +35 -0
  479. package/src/gestures/GestureHost.ts +2065 -0
  480. package/src/gestures/GestureRecognizer.ts +18 -0
  481. package/src/gestures/LongPressGestureRecognizer.ts +4 -0
  482. package/src/gestures/PanGestureRecognizer.ts +4 -0
  483. package/src/gestures/PinchGestureRecognizer.ts +148 -0
  484. package/src/gestures/ScreenEdgePanGestureRecognizer.ts +256 -0
  485. package/src/gestures/SwipeGestureRecognizer.ts +197 -0
  486. package/src/gestures/TapGestureRecognizer.ts +4 -0
  487. package/src/gestures/VelocityTracker.ts +10 -3
  488. package/src/gestures/__tests__/GestureHost.test.ts +2087 -0
  489. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +4 -4
  490. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +74 -0
  491. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +498 -0
  492. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +108 -0
  493. package/src/gestures/__tests__/arena.test.ts +5 -0
  494. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +193 -0
  495. package/src/gestures/__tests__/browser/build-fixture.mjs +46 -0
  496. package/src/gestures/__tests__/browser/fixture.ts +307 -0
  497. package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +1137 -0
  498. package/src/gestures/__tests__/browserNegotiation.test.ts +293 -0
  499. package/src/gestures/__tests__/haptics.test.ts +187 -0
  500. package/src/gestures/__tests__/harness.ts +21 -2
  501. package/src/gestures/__tests__/recognizerKind.test.ts +78 -0
  502. package/src/gestures/__tests__/touchActionRule.test.ts +54 -0
  503. package/src/gestures/haptics.ts +203 -0
  504. package/src/gestures/nativeScroll.ts +277 -0
  505. package/src/gestures/touchAction.ts +191 -0
  506. package/src/gestures/touchActionRule.ts +157 -0
  507. package/src/gestures/types.ts +30 -9
  508. package/src/hooks/index.ts +5 -1
  509. package/src/hooks/modalAnnounce.ts +129 -72
  510. package/src/hooks/useAdaptivePresentation.ts +4 -1
  511. package/src/hooks/useAnnounce.doc.mjs +35 -3
  512. package/src/hooks/useAnnounce.replace.test.tsx +516 -0
  513. package/src/hooks/useAnnounce.ts +209 -102
  514. package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +10 -1
  515. package/src/hooks/useAnnounceRenderedText.ts +17 -3
  516. package/src/hooks/useBusyAnnouncement.ts +7 -10
  517. package/src/hooks/useMenuHover.ts +15 -1
  518. package/src/i18n/generated-locales/en.generated.ts +6 -0
  519. package/src/i18n/generated-locales/pseudo.generated.ts +6 -0
  520. package/src/motion/README.md +11 -1
  521. package/src/motion/animator.test.ts +151 -3
  522. package/src/motion/animator.ts +149 -33
  523. package/src/theme/Theme.doc.mjs +1 -1
  524. package/src/theme/defineTheme.ts +117 -8
  525. package/src/theme/derivedVarRegistry.test.ts +4 -0
  526. package/src/theme/domainTokens/domainTokenDefaults.ts +19 -0
  527. package/src/theme/domainTokens/index.ts +6 -10
  528. package/src/theme/expandMotionScale.test.ts +1 -1
  529. package/src/theme/index.ts +5 -0
  530. package/src/theme/motionKnobs.ts +82 -0
  531. package/src/theme/resolveThemeValues.ts +68 -6
  532. package/src/theme/themeAdaptations.ts +52 -12
  533. package/src/theme/themeMotion.test.ts +620 -0
  534. package/src/theme/themeMotion.ts +221 -0
  535. package/src/theme/tokenReferences.ts +149 -0
  536. package/src/theme/tokens.stylex.ts +10 -0
  537. package/src/theme/tokens.ts +15 -158
  538. package/src/utils/focusReturn.ts +17 -6
  539. package/src/utils/getNodeText.test.ts +24 -0
  540. package/src/utils/getNodeText.ts +37 -0
  541. package/src/utils/interactionModality.test.ts +45 -1
  542. package/src/utils/interactionModality.ts +64 -5
  543. package/src/utils/interactionOverlay.stylex.ts +12 -3
  544. package/src/utils/plainDate.test.ts +90 -0
  545. package/src/utils/plainDate.ts +58 -3
  546. package/src/utils/presentation.test.ts +380 -0
  547. package/src/utils/presentation.ts +180 -0
  548. package/src/utils/pressFeedback.test.ts +138 -0
  549. package/src/utils/pressFeedback.ts +34 -11
  550. package/src/utils/pressPaintPriority.test.ts +80 -0
  551. package/src/utils/primaryClick.test.ts +38 -0
  552. package/src/utils/primaryClick.ts +28 -0
  553. package/src/utils/timeFormat.test.ts +204 -0
  554. package/src/utils/timeFormat.ts +314 -0
  555. package/src/utils/touchPresentation.stylex.ts +43 -0
  556. package/src/utils/touchPresentation.test.ts +140 -1
  557. package/src/utils/touchPresentation.ts +5 -3
  558. package/src/utils/unicodeDigits.test.ts +44 -0
  559. package/src/utils/unicodeDigits.ts +71 -0
@@ -0,0 +1,172 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file popoverOperations.ts
5
+ * @input Popover API events (`beforetoggle`, `toggle`) and the layers' own show/hide calls
6
+ * @output The per-document ledger of popover operations in flight, and the deferral of a show that arrives inside one
7
+ * @position Internal to the layer runtime; used by useLayer
8
+ *
9
+ * SYNC: When modified, update:
10
+ * - /packages/core/src/Layer/useLayer.tsx
11
+ * - /packages/core/src/Layer/popoverOperations.test.ts
12
+ */
13
+
14
+ /**
15
+ * Popover operations in flight, per document.
16
+ *
17
+ * The Popover API refuses to show a popover while the document is in the
18
+ * middle of showing or hiding ANY popover: `showPopover()` throws an
19
+ * `InvalidStateError` ("Invalid to show a popover during another show
20
+ * operation"; Chromium 150+, where 149 still accepts such a show).
21
+ * Hiding a popover restores focus to the element that had it —
22
+ * synchronously, inside that window — so a focus-driven layer on the element
23
+ * receiving focus (a tooltip on the trigger that just closed a popover) asks
24
+ * to show while the hide is still running.
25
+ *
26
+ * Two kinds of operation are counted:
27
+ *
28
+ * - The layers' own shows and hides pass through `runPopoverOperation`, so
29
+ * their windows are known exactly: a `show()` that arrives inside one is
30
+ * deferred ahead of asking the browser.
31
+ * - A hide the browser runs on its own — an Escape its close watcher answers,
32
+ * a light dismiss, an `auto` popover evicted by another one opening — never
33
+ * passes through here. It is observable from its element's events:
34
+ * `beforetoggle` fires synchronously as the hide starts, and `toggle` in a
35
+ * task once it has ended; `observeBrowserHide` counts it between the two.
36
+ * That window is wider than the engine's, which ends when the hide returns,
37
+ * so a show inside it is asked of the browser first and deferred only when
38
+ * the browser refuses it: a light dismiss at `pointerup` followed by a show
39
+ * at `click` in the same gesture still opens at once.
40
+ *
41
+ * A deferred show waits in `afterPopoverOperations` and replays, one
42
+ * microtask each, once the document's last operation of either kind has
43
+ * ended and the script that started it has unwound.
44
+ */
45
+ const ownOperationsByDocument = new WeakMap<Document, number>();
46
+ const browserHidesByDocument = new WeakMap<Document, number>();
47
+ const settledCallbacksByDocument = new WeakMap<Document, (() => void)[]>();
48
+
49
+ function count(map: WeakMap<Document, number>, doc: Document, delta: number) {
50
+ map.set(doc, Math.max(0, (map.get(doc) ?? 0) + delta));
51
+ }
52
+
53
+ function anyInFlight(doc: Document): boolean {
54
+ return (
55
+ (ownOperationsByDocument.get(doc) ?? 0) > 0 ||
56
+ (browserHidesByDocument.get(doc) ?? 0) > 0
57
+ );
58
+ }
59
+
60
+ function ended(doc: Document): void {
61
+ if (!anyInFlight(doc) && settledCallbacksByDocument.has(doc)) {
62
+ // The operation's own script is still on the stack (the `finally` of a
63
+ // `runPopoverOperation`, a `toggle` listener): replay once it has unwound.
64
+ queueMicrotask(() => replaySettled(doc));
65
+ }
66
+ }
67
+
68
+ function replaySettled(doc: Document): void {
69
+ if (anyInFlight(doc)) {
70
+ // Another operation started before the replay ran; its end replays.
71
+ return;
72
+ }
73
+ const callbacks = settledCallbacksByDocument.get(doc);
74
+ if (callbacks == null) {
75
+ return;
76
+ }
77
+ settledCallbacksByDocument.delete(doc);
78
+ // One microtask each: a replay that throws reaches the page on its own and
79
+ // leaves the ones after it to run.
80
+ for (const callback of callbacks) {
81
+ queueMicrotask(callback);
82
+ }
83
+ }
84
+
85
+ /** Runs `operation`, counting it as one of the layers' own popover operations in `doc`. */
86
+ export function runPopoverOperation(
87
+ doc: Document,
88
+ operation: () => void,
89
+ ): void {
90
+ count(ownOperationsByDocument, doc, 1);
91
+ try {
92
+ operation();
93
+ } finally {
94
+ count(ownOperationsByDocument, doc, -1);
95
+ ended(doc);
96
+ }
97
+ }
98
+
99
+ /** Whether one of the layers' own shows or hides is running in `doc`: a show asked now would be refused. */
100
+ export function isPopoverOperationInFlight(doc: Document): boolean {
101
+ return (ownOperationsByDocument.get(doc) ?? 0) > 0;
102
+ }
103
+
104
+ /**
105
+ * Whether a hide the browser runs itself is counted in `doc`. The count
106
+ * outlasts the engine's own window (it ends at the element's `toggle`, a task
107
+ * later), so a show asked inside it may well succeed; one the browser refuses
108
+ * is deferred to the count's end.
109
+ */
110
+ export function isBrowserHideInFlight(doc: Document): boolean {
111
+ return (browserHidesByDocument.get(doc) ?? 0) > 0;
112
+ }
113
+
114
+ /** Popovers whose browser-run hide is being counted, so one hide is counted once. */
115
+ const browserHidesInFlight = new WeakSet<Element>();
116
+
117
+ /**
118
+ * Counts a hide the browser runs itself. Called as the element's `beforetoggle`
119
+ * fires, which the hide dispatches synchronously as it starts; the count ends
120
+ * at the element's `toggle`, which the browser queues for the task after the
121
+ * hide, and in any case at the next task: the hide is synchronous, so the task
122
+ * after it is past the window even when the element has left the document and
123
+ * its `toggle` never arrives. Calling it again for the same hide is a no-op.
124
+ */
125
+ export function observeBrowserHide(popover: HTMLElement): void {
126
+ if (browserHidesInFlight.has(popover)) {
127
+ return;
128
+ }
129
+ browserHidesInFlight.add(popover);
130
+ const doc = popover.ownerDocument;
131
+ count(browserHidesByDocument, doc, 1);
132
+ const view = doc.defaultView ?? globalThis;
133
+ let timer: ReturnType<typeof setTimeout> | null = null;
134
+ let done = false;
135
+ const finish = () => {
136
+ if (done) {
137
+ return;
138
+ }
139
+ done = true;
140
+ popover.removeEventListener('toggle', finish);
141
+ if (timer != null) {
142
+ view.clearTimeout(timer);
143
+ timer = null;
144
+ }
145
+ browserHidesInFlight.delete(popover);
146
+ count(browserHidesByDocument, doc, -1);
147
+ ended(doc);
148
+ };
149
+ popover.addEventListener('toggle', finish);
150
+ timer = view.setTimeout(finish, 0);
151
+ }
152
+
153
+ /**
154
+ * Runs `callback` once no popover operation of either kind is in flight in
155
+ * `doc`: at once when none is, otherwise in its own microtask after the last
156
+ * one has ended and the script that started it has unwound.
157
+ */
158
+ export function afterPopoverOperations(
159
+ doc: Document,
160
+ callback: () => void,
161
+ ): void {
162
+ if (!anyInFlight(doc)) {
163
+ callback();
164
+ return;
165
+ }
166
+ const callbacks = settledCallbacksByDocument.get(doc);
167
+ if (callbacks == null) {
168
+ settledCallbacksByDocument.set(doc, [callback]);
169
+ } else {
170
+ callbacks.push(callback);
171
+ }
172
+ }
@@ -18,7 +18,7 @@ import {
18
18
  waitFor,
19
19
  } from '@testing-library/react';
20
20
  import userEvent from '@testing-library/user-event';
21
- import {Profiler, useEffect} from 'react';
21
+ import {Profiler, useEffect, useState} from 'react';
22
22
  import {hydrateRoot} from 'react-dom/client';
23
23
  import {renderToString} from 'react-dom/server';
24
24
  import * as stylex from '@stylexjs/stylex';
@@ -1143,6 +1143,353 @@ describe('useLayer', () => {
1143
1143
  expect(onTooltipShow).toHaveBeenCalledTimes(2);
1144
1144
  });
1145
1145
 
1146
+ /**
1147
+ * The browser's own hide: an Escape its close watcher answers never calls
1148
+ * the hook's hide(). The engine fires `beforetoggle` synchronously as the
1149
+ * hide starts, restores focus inside the hide when focus was inside the
1150
+ * popover, and fires `toggle` in a task once it has ended; `showPopover()`
1151
+ * throws throughout.
1152
+ */
1153
+ function installBrowserHidePopoverApi() {
1154
+ // The ledger's backstop for a hide whose `toggle` never arrives is the
1155
+ // next task; under fake timers, the test decides when that task runs,
1156
+ // so the `toggle` tasks below end a hide first, as in a browser.
1157
+ vi.useFakeTimers({toFake: ['setTimeout', 'clearTimeout']});
1158
+ let operationsInFlight = 0;
1159
+ const previouslyFocused = new WeakMap<HTMLElement, Element | null>();
1160
+ const toggleTasks: (() => void)[] = [];
1161
+ const fireToggle = (
1162
+ popover: HTMLElement,
1163
+ type: 'beforetoggle' | 'toggle',
1164
+ newState: 'open' | 'closed',
1165
+ ) => {
1166
+ const event = new Event(type, {bubbles: false});
1167
+ Object.defineProperty(event, 'newState', {
1168
+ value: newState,
1169
+ configurable: true,
1170
+ });
1171
+ popover.dispatchEvent(event);
1172
+ };
1173
+ const hide = (popover: HTMLElement) => {
1174
+ operationsInFlight += 1;
1175
+ try {
1176
+ fireToggle(popover, 'beforetoggle', 'closed');
1177
+ delete popover.dataset.open;
1178
+ toggleTasks.push(() => fireToggle(popover, 'toggle', 'closed'));
1179
+ const target = previouslyFocused.get(popover);
1180
+ previouslyFocused.delete(popover);
1181
+ if (
1182
+ target instanceof HTMLElement &&
1183
+ popover.contains(document.activeElement)
1184
+ ) {
1185
+ target.focus();
1186
+ }
1187
+ } finally {
1188
+ operationsInFlight -= 1;
1189
+ }
1190
+ };
1191
+ let refusals = 0;
1192
+ const showSpy = vi.fn(function (this: HTMLElement) {
1193
+ if (operationsInFlight > 0 || refusals > 0) {
1194
+ refusals = Math.max(0, refusals - 1);
1195
+ throw new DOMException(
1196
+ "Failed to execute 'showPopover' on 'HTMLElement': Invalid to show a popover during another show operation",
1197
+ 'InvalidStateError',
1198
+ );
1199
+ }
1200
+ operationsInFlight += 1;
1201
+ try {
1202
+ fireToggle(this, 'beforetoggle', 'open');
1203
+ previouslyFocused.set(this, this.ownerDocument.activeElement);
1204
+ this.dataset.open = 'true';
1205
+ toggleTasks.push(() => fireToggle(this, 'toggle', 'open'));
1206
+ } finally {
1207
+ operationsInFlight -= 1;
1208
+ }
1209
+ });
1210
+ const hideSpy = vi.fn(function (this: HTMLElement) {
1211
+ hide(this);
1212
+ });
1213
+ HTMLElement.prototype.showPopover = showSpy;
1214
+ HTMLElement.prototype.hidePopover = hideSpy;
1215
+ return {
1216
+ showSpy,
1217
+ hideSpy,
1218
+ /** The browser hides the popover itself: a close watcher's Escape. */
1219
+ browserHide: (popover: HTMLElement) => hide(popover),
1220
+ /** The task after the hide: the queued `toggle` events fire. */
1221
+ runToggleTasks: () => {
1222
+ for (const task of toggleTasks.splice(0)) {
1223
+ task();
1224
+ }
1225
+ },
1226
+ /** The engine refuses the next show too, whatever is in flight. */
1227
+ refuseNextShow: () => {
1228
+ refusals += 1;
1229
+ },
1230
+ };
1231
+ }
1232
+
1233
+ afterEach(() => {
1234
+ vi.useRealTimers();
1235
+ });
1236
+
1237
+ it('defers the show a browser-run hide wakes until that hide has ended, and replays it at its toggle', async () => {
1238
+ const {showSpy, browserHide, runToggleTasks} =
1239
+ installBrowserHidePopoverApi();
1240
+ const errors: unknown[] = [];
1241
+ const onError = (event: ErrorEvent) => {
1242
+ errors.push(event.error);
1243
+ event.preventDefault();
1244
+ };
1245
+ window.addEventListener('error', onError);
1246
+ const onTooltipShow = vi.fn();
1247
+ const {getByRole} = render(
1248
+ <FocusReturnHarness onTooltipShow={onTooltipShow} />,
1249
+ );
1250
+ const trigger = getByRole('button', {name: 'Trigger'});
1251
+ const inside = getByRole('button', {name: 'Inside', hidden: true});
1252
+ const popover = getByRole('dialog', {hidden: true});
1253
+
1254
+ act(() => trigger.focus());
1255
+ fireEvent.click(trigger);
1256
+ act(() => inside.focus());
1257
+ runToggleTasks();
1258
+ expect(onTooltipShow).toHaveBeenCalledTimes(1);
1259
+ expect(popover).toHaveAttribute('data-open', 'true');
1260
+
1261
+ // Escape, answered by the browser's close watcher: it hides the popover
1262
+ // on its own and returns focus to the trigger inside that hide; the
1263
+ // tooltip asks to show.
1264
+ act(() => browserHide(popover));
1265
+ window.removeEventListener('error', onError);
1266
+
1267
+ expect(errors).toEqual([]);
1268
+ expect(document.activeElement).toBe(trigger);
1269
+ expect(popover).not.toHaveAttribute('data-open');
1270
+ // The show was asked of the browser inside the hide and refused; the
1271
+ // hook kept the refusal to itself and parked the show. Nothing else is
1272
+ // asked before the hide ends at its `toggle`, not even after the
1273
+ // script unwound.
1274
+ await act(async () => {});
1275
+ const refusals = () =>
1276
+ showSpy.mock.results.filter(r => r.type === 'throw').length;
1277
+ expect(refusals()).toBe(1);
1278
+ expect(getByRole('tooltip', {hidden: true})).not.toHaveAttribute(
1279
+ 'data-open',
1280
+ );
1281
+ expect(onTooltipShow).toHaveBeenCalledTimes(1);
1282
+
1283
+ // The task after the hide: the popover's `toggle` fires, the hook
1284
+ // reconciles its state, and the parked show runs exactly once.
1285
+ act(() => runToggleTasks());
1286
+ await act(async () => {});
1287
+ expect(refusals()).toBe(1);
1288
+ expect(getByRole('tooltip', {hidden: true})).toHaveAttribute(
1289
+ 'data-open',
1290
+ 'true',
1291
+ );
1292
+ expect(onTooltipShow).toHaveBeenCalledTimes(2);
1293
+ expect(popover).not.toHaveAttribute('data-open');
1294
+ });
1295
+
1296
+ it('lets a replay the browser refuses again reach the page, and parks nothing more', async () => {
1297
+ const {showSpy, browserHide, runToggleTasks, refuseNextShow} =
1298
+ installBrowserHidePopoverApi();
1299
+ const onTooltipShow = vi.fn();
1300
+ const {getByRole} = render(
1301
+ <FocusReturnHarness onTooltipShow={onTooltipShow} />,
1302
+ );
1303
+ const trigger = getByRole('button', {name: 'Trigger'});
1304
+ const inside = getByRole('button', {name: 'Inside', hidden: true});
1305
+ const popover = getByRole('dialog', {hidden: true});
1306
+ act(() => trigger.focus());
1307
+ fireEvent.click(trigger);
1308
+ act(() => inside.focus());
1309
+ runToggleTasks();
1310
+ const refusals = () =>
1311
+ showSpy.mock.results.filter(r => r.type === 'throw').length;
1312
+
1313
+ // The hide wakes the tooltip; the browser refuses; the show is parked.
1314
+ act(() => browserHide(popover));
1315
+ expect(refusals()).toBe(1);
1316
+
1317
+ // Hold the microtasks the ledger queues, to run the replay by hand.
1318
+ const held: (() => void)[] = [];
1319
+ const queue = vi
1320
+ .spyOn(globalThis, 'queueMicrotask')
1321
+ .mockImplementation(callback => {
1322
+ held.push(callback);
1323
+ });
1324
+ try {
1325
+ act(() => runToggleTasks());
1326
+ // The hide's end queued the batch; the batch queues the one replay.
1327
+ expect(held).toHaveLength(1);
1328
+ held.shift()?.();
1329
+ expect(held).toHaveLength(1);
1330
+ const replay = held.shift();
1331
+ // The replay runs outside the hide, so a browser that refuses again
1332
+ // throws to the page from the replay's own microtask: nothing catches
1333
+ // it, and nothing is parked again.
1334
+ refuseNextShow();
1335
+ expect(replay).toThrow(
1336
+ expect.objectContaining({name: 'InvalidStateError'}),
1337
+ );
1338
+ expect(held).toHaveLength(0);
1339
+ } finally {
1340
+ queue.mockRestore();
1341
+ }
1342
+ await act(async () => {});
1343
+ expect(refusals()).toBe(2);
1344
+ expect(getByRole('tooltip', {hidden: true})).not.toHaveAttribute(
1345
+ 'data-open',
1346
+ );
1347
+ expect(onTooltipShow).toHaveBeenCalledTimes(1);
1348
+ });
1349
+
1350
+ it('opens a layer at once when the press that closed another one opens it after the hide has returned', () => {
1351
+ // A light dismiss runs at pointerup and the click that follows opens the
1352
+ // next layer in the same gesture: the browser accepts that show (its
1353
+ // hide has returned) although the hidden layer's `toggle` has not fired.
1354
+ const {showSpy, browserHide, runToggleTasks} =
1355
+ installBrowserHidePopoverApi();
1356
+ const api: {first?: FixedLayerReturn; second?: FixedLayerReturn} = {};
1357
+ const onSecondShow = vi.fn();
1358
+ function Harness() {
1359
+ api.first = useLayer({mode: 'fixed'});
1360
+ api.second = useLayer({mode: 'fixed', onShow: onSecondShow});
1361
+ return (
1362
+ <>
1363
+ {api.first.render(<span>First</span>, {x: 0, y: 0})}
1364
+ {api.second.render(<span>Second</span>, {x: 0, y: 0})}
1365
+ </>
1366
+ );
1367
+ }
1368
+ const {getByText} = render(<Harness />);
1369
+ act(() => api.first?.show());
1370
+ const first = getByText('First').closest('[popover]') as HTMLElement;
1371
+ const second = getByText('Second').closest('[popover]') as HTMLElement;
1372
+
1373
+ act(() => {
1374
+ browserHide(first);
1375
+ api.second?.show();
1376
+ });
1377
+ expect(showSpy.mock.results.every(r => r.type === 'return')).toBe(true);
1378
+ expect(second).toHaveAttribute('data-open', 'true');
1379
+ expect(onSecondShow).toHaveBeenCalledTimes(1);
1380
+ expect(api.second?.isOpen).toBe(true);
1381
+ // The first layer's hide ends at its toggle, with nothing left to replay.
1382
+ act(() => runToggleTasks());
1383
+ expect(onSecondShow).toHaveBeenCalledTimes(1);
1384
+ });
1385
+
1386
+ it('keeps counting a browser-run hide across a render it causes (an inline onHide, a state update in the focus handler)', async () => {
1387
+ const {showSpy, browserHide, runToggleTasks} =
1388
+ installBrowserHidePopoverApi();
1389
+ const errors: unknown[] = [];
1390
+ const onError = (event: ErrorEvent) => {
1391
+ errors.push(event.error);
1392
+ event.preventDefault();
1393
+ };
1394
+ window.addEventListener('error', onError);
1395
+ const onTooltipShow = vi.fn();
1396
+ // The host re-renders inside the hide: the focus handler sets state,
1397
+ // and `onHide` is a new function each render, so React rebinds the
1398
+ // layer's `toggle` listener and ref while the hide is still running.
1399
+ function RerenderingHarness() {
1400
+ const [focusCount, setFocusCount] = useState(0);
1401
+ const popover = useLayer({mode: 'context', onHide: () => {}});
1402
+ const tooltip = useLayer({mode: 'context', onShow: onTooltipShow});
1403
+ return (
1404
+ <>
1405
+ <button
1406
+ type="button"
1407
+ ref={el => {
1408
+ popover.ref(el);
1409
+ tooltip.ref(el);
1410
+ }}
1411
+ onFocus={() => {
1412
+ setFocusCount(count => count + 1);
1413
+ tooltip.show();
1414
+ }}
1415
+ onBlur={tooltip.hide}
1416
+ onClick={popover.show}>
1417
+ Trigger {focusCount}
1418
+ </button>
1419
+ {popover.render(<button type="button">Inside</button>, {
1420
+ role: 'dialog',
1421
+ })}
1422
+ {tooltip.render(<span>Tip</span>, {role: 'tooltip'})}
1423
+ </>
1424
+ );
1425
+ }
1426
+ const {getByRole} = render(<RerenderingHarness />);
1427
+ const trigger = getByRole('button', {name: /Trigger/});
1428
+ const inside = getByRole('button', {name: 'Inside', hidden: true});
1429
+ const popover = getByRole('dialog', {hidden: true});
1430
+
1431
+ act(() => trigger.focus());
1432
+ fireEvent.click(trigger);
1433
+ act(() => inside.focus());
1434
+ runToggleTasks();
1435
+ expect(onTooltipShow).toHaveBeenCalledTimes(1);
1436
+
1437
+ act(() => browserHide(popover));
1438
+ window.removeEventListener('error', onError);
1439
+ expect(errors).toEqual([]);
1440
+ await act(async () => {});
1441
+ const refusals = () =>
1442
+ showSpy.mock.results.filter(r => r.type === 'throw').length;
1443
+ // One refusal, kept by the hook; still inside the hide's window, so
1444
+ // nothing replayed yet, however the host re-rendered meanwhile.
1445
+ expect(refusals()).toBe(1);
1446
+ expect(onTooltipShow).toHaveBeenCalledTimes(1);
1447
+
1448
+ act(() => runToggleTasks());
1449
+ await act(async () => {});
1450
+ expect(refusals()).toBe(1);
1451
+ expect(getByRole('tooltip', {hidden: true})).toHaveAttribute(
1452
+ 'data-open',
1453
+ 'true',
1454
+ );
1455
+ expect(onTooltipShow).toHaveBeenCalledTimes(2);
1456
+ });
1457
+
1458
+ it('releases a browser-run hide whose element detaches before its toggle, so later shows run', async () => {
1459
+ const {showSpy, browserHide} = installBrowserHidePopoverApi();
1460
+ const api: {popover?: FixedLayerReturn; tooltip?: FixedLayerReturn} = {};
1461
+ function Harness({withPopover}: {withPopover: boolean}) {
1462
+ api.popover = useLayer({mode: 'fixed'});
1463
+ api.tooltip = useLayer({mode: 'fixed'});
1464
+ return (
1465
+ <>
1466
+ {withPopover &&
1467
+ api.popover.render(<span>Popover</span>, {x: 0, y: 0})}
1468
+ {api.tooltip.render(<span>Tip</span>, {x: 0, y: 0})}
1469
+ </>
1470
+ );
1471
+ }
1472
+ const {getByText, rerender} = render(<Harness withPopover />);
1473
+ act(() => api.popover?.show());
1474
+ const popover = getByText('Popover').closest('[popover]') as HTMLElement;
1475
+ // The browser hides it, and React unmounts it before the `toggle` task.
1476
+ act(() => browserHide(popover));
1477
+ rerender(<Harness withPopover={false} />);
1478
+ expect(popover.isConnected).toBe(false);
1479
+
1480
+ // The hide was synchronous: the task after it is past the window.
1481
+ act(() => {
1482
+ vi.runOnlyPendingTimers();
1483
+ });
1484
+ act(() => api.tooltip?.show());
1485
+ await act(async () => {});
1486
+ expect(showSpy).toHaveBeenCalledTimes(2);
1487
+ expect(getByText('Tip').closest('[popover]')).toHaveAttribute(
1488
+ 'data-open',
1489
+ 'true',
1490
+ );
1491
+ });
1492
+
1146
1493
  it('drops a deferred show that hide() cancels before it replays', async () => {
1147
1494
  const api: {popover?: FixedLayerReturn; tooltip?: FixedLayerReturn} = {};
1148
1495
  // While the popover hides, the tooltip asks to show and then changes