@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,165 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {act, render} from '@testing-library/react';
4
+ import {createRef} from 'react';
5
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
6
+ import {MonthScroller, type MonthScrollerHandle} from './MonthScroller';
7
+ import {SCROLL_QUIET_MS} from './useScrollSettle';
8
+
9
+ class FakeResizeObserver {
10
+ constructor(private callback: ResizeObserverCallback) {}
11
+ observe() {
12
+ this.callback([], this);
13
+ }
14
+ unobserve() {}
15
+ disconnect() {}
16
+ }
17
+
18
+ const PANE = 300;
19
+
20
+ /**
21
+ * The month scroller's settle, with the browser's scrolling played by hand:
22
+ * `scrollTo` is recorded, and the offset is whatever the test sets.
23
+ */
24
+ describe('MonthScroller settle', () => {
25
+ const scrollTo = vi.fn();
26
+ beforeEach(() => {
27
+ vi.useFakeTimers({
28
+ toFake: [
29
+ 'setTimeout',
30
+ 'clearTimeout',
31
+ 'requestAnimationFrame',
32
+ 'cancelAnimationFrame',
33
+ 'Date',
34
+ 'performance',
35
+ ],
36
+ });
37
+ vi.stubGlobal('ResizeObserver', FakeResizeObserver);
38
+ vi.spyOn(HTMLElement.prototype, 'clientWidth', 'get').mockReturnValue(PANE);
39
+ vi.spyOn(HTMLElement.prototype, 'clientHeight', 'get').mockReturnValue(
40
+ PANE,
41
+ );
42
+ Element.prototype.scrollTo = scrollTo;
43
+ scrollTo.mockClear();
44
+ });
45
+ afterEach(() => {
46
+ vi.useRealTimers();
47
+ vi.unstubAllGlobals();
48
+ vi.restoreAllMocks();
49
+ });
50
+
51
+ function renderScroller() {
52
+ const handleRef = createRef<MonthScrollerHandle>();
53
+ const view = render(
54
+ <MonthScroller
55
+ handleRef={handleRef}
56
+ minMonthIndex={0}
57
+ maxMonthIndex={23}
58
+ initialMonthIndex={9}
59
+ onVisibleMonthChange={() => {}}
60
+ selectedDate={null}
61
+ today={{year: 2026, month: 10, day: 15}}
62
+ isDateDisabled={() => false}
63
+ weekStartsOn={0}
64
+ onSelect={() => {}}
65
+ />,
66
+ );
67
+ const scroller = view.container.querySelector<HTMLElement>(
68
+ '[data-scroller="months"]',
69
+ );
70
+ if (scroller == null) {
71
+ throw new Error('no month scroller');
72
+ }
73
+ scrollTo.mockClear();
74
+ return {handleRef, scroller};
75
+ }
76
+
77
+ /** The browser moving the scroller to `left`, as a scroll event reports. */
78
+ /**
79
+ * A touch event carrying the lists a real one always has: jsdom has no
80
+ * constructible TouchEvent, and the gesture hook reads `touches[0]`.
81
+ */
82
+ function touchEvent(type: 'touchstart' | 'touchend') {
83
+ const event = new Event(type, {bubbles: true});
84
+ const point = {identifier: 1, clientX: 100, clientY: 100};
85
+ Object.defineProperties(event, {
86
+ touches: {value: type === 'touchend' ? [] : [point]},
87
+ targetTouches: {value: type === 'touchend' ? [] : [point]},
88
+ changedTouches: {value: [point]},
89
+ });
90
+ return event;
91
+ }
92
+
93
+ function scrollBy(scroller: HTMLElement, left: number) {
94
+ scroller.scrollLeft = left;
95
+ scroller.dispatchEvent(new Event('scroll'));
96
+ }
97
+
98
+ it('turns scroll snapping back on after re-centers that end on the month they began on', () => {
99
+ const {handleRef, scroller} = renderScroller();
100
+ // Two far jumps in one batch, out and back: each re-centers the mounted
101
+ // months, pausing scroll snapping, and the second puts the center back
102
+ // where it was, so the render that follows changes no center.
103
+ act(() => {
104
+ handleRef.current?.scrollToMonth(20);
105
+ handleRef.current?.scrollToMonth(9);
106
+ });
107
+ act(() => {
108
+ vi.advanceTimersByTime(50);
109
+ });
110
+ expect(scroller.style.scrollSnapType).toBe('');
111
+ });
112
+
113
+ it('leaves snapping on for a re-center while a finger drives the scroll', () => {
114
+ const {scroller} = renderScroller();
115
+ // A finger drags the next month in: the scroller crosses the middle of
116
+ // October's pane and re-centers its months with the finger still down.
117
+ act(() => {
118
+ scroller.dispatchEvent(touchEvent('touchstart'));
119
+ });
120
+ act(() => scrollBy(scroller, 9 * PANE + 0.6 * PANE));
121
+ act(() => {
122
+ vi.advanceTimersByTime(16);
123
+ });
124
+ // Snapping stays on: iOS tracks a snap target for the gesture.
125
+ expect(scroller.style.scrollSnapType).toBe('');
126
+ });
127
+
128
+ it('pauses snapping for a re-center again once the finger’s scroll has come to rest', () => {
129
+ const {handleRef, scroller} = renderScroller();
130
+ act(() => {
131
+ scroller.dispatchEvent(touchEvent('touchstart'));
132
+ scroller.dispatchEvent(touchEvent('touchend'));
133
+ });
134
+ act(() => {
135
+ vi.advanceTimersByTime(SCROLL_QUIET_MS + 50);
136
+ });
137
+ // An arrow's far jump after the swipe re-centers with snapping paused.
138
+ act(() => handleRef.current?.scrollToMonth(20));
139
+ expect(scroller.style.scrollSnapType).toBe('none');
140
+ act(() => {
141
+ vi.advanceTimersByTime(50);
142
+ });
143
+ expect(scroller.style.scrollSnapType).toBe('');
144
+ });
145
+
146
+ it('finishes a steer the browser cut short instead of rounding it back', () => {
147
+ const {handleRef, scroller} = renderScroller();
148
+ // An arrow toward month 10, its scroll 95 % of the way there.
149
+ act(() => handleRef.current?.scrollToMonth(10));
150
+ act(() => scrollBy(scroller, 9 * PANE + 0.95 * PANE));
151
+ // The other arrow, late in that scroll: back toward month 9.
152
+ act(() => handleRef.current?.scrollToMonth(9));
153
+ scrollTo.mockClear();
154
+ // WebKit ends the retargeted scroll where the first one's time ran out,
155
+ // near month 10, and the scroller goes quiet there.
156
+ act(() => scrollBy(scroller, 9 * PANE + 0.95 * PANE));
157
+ act(() => {
158
+ vi.advanceTimersByTime(SCROLL_QUIET_MS + 50);
159
+ });
160
+ // The settle finishes the steer onto month 9, not the nearest pane (10).
161
+ expect(
162
+ scrollTo.mock.calls.map(([options]) => (options as ScrollToOptions).left),
163
+ ).toEqual([9 * PANE]);
164
+ });
165
+ });
@@ -772,7 +772,7 @@ export function TouchDateField({
772
772
 
773
773
  const fireChange = useCallback(
774
774
  (newValue: ISODateString | undefined) => {
775
- if (isBusy) {
775
+ if (isEffectivelyDisabled) {
776
776
  return;
777
777
  }
778
778
  onChange?.(newValue);
@@ -783,7 +783,13 @@ export function TouchDateField({
783
783
  });
784
784
  }
785
785
  },
786
- [isBusy, onChange, changeAction, startTransition, setOptimisticValue],
786
+ [
787
+ isEffectivelyDisabled,
788
+ onChange,
789
+ changeAction,
790
+ startTransition,
791
+ setOptimisticValue,
792
+ ],
787
793
  );
788
794
 
789
795
  const openSheet = useCallback(() => {
@@ -841,6 +847,11 @@ export function TouchDateField({
841
847
  * value is still cleared either way — that half never depends on the range.
842
848
  */
843
849
  const handleResetInSheet = useCallback(() => {
850
+ // Disabled: reject the entire reset without closing or moving focus.
851
+ // Busy-only fields keep month navigation; fireChange blocks the clear.
852
+ if (isDisabled) {
853
+ return;
854
+ }
844
855
  fireChange(undefined);
845
856
  const currentMonth = monthIndexOf(today);
846
857
  if (currentMonth < minMonthIndex || currentMonth > maxMonthIndex) {
@@ -851,7 +862,7 @@ export function TouchDateField({
851
862
  }
852
863
  setMonthIndex(currentMonth);
853
864
  scrollerHandleRef.current?.scrollToMonth(currentMonth, 'smooth');
854
- }, [fireChange, today, monthIndex, minMonthIndex, maxMonthIndex]);
865
+ }, [isDisabled, fireChange, today, monthIndex, minMonthIndex, maxMonthIndex]);
855
866
 
856
867
  // Selection commits on the tap and leaves the sheet up, so a mistake can be
857
868
  // corrected in place and a nearby date reconsidered without reopening.
@@ -0,0 +1,312 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file MonthScrollerSteps.interaction.browser.spec.ts
5
+ * @input The built "Default" DateInput story, shown in a bottom sheet, the
6
+ * arrows clicked from the page, real mouse and trackpad input and the
7
+ * interaction harness
8
+ * @output Interaction proofs for the month scroller's steps: after the month
9
+ * and year wheels close, a trackpad scroll still pages to the next month and
10
+ * Reset from four months on still rests on today's month; an arrow step
11
+ * through a stalled frame moves exactly one month; under a finger, a swipe
12
+ * that crosses the middle of a month as it lifts keeps scroll snapping on
13
+ * and rests on the next month.
14
+ * (A second arrow step late in the first one's scroll, which WebKit cuts
15
+ * short, is proven in MonthScrollerSettle.test.tsx: a loaded page's frames
16
+ * are too far apart to aim a click at a share of a 300 ms scroll.)
17
+ * @position Interaction tests of MonthScroller.tsx
18
+ * (`*.interaction.browser.spec.ts`, run by `pnpm test:interaction`)
19
+ */
20
+
21
+ import {expect, test, type Locator, type Page} from '@playwright/test';
22
+ import {
23
+ blockMainThread,
24
+ drag,
25
+ openStory,
26
+ touchDriver,
27
+ withStorybook,
28
+ } from '@astryxdesign/interaction-harness';
29
+
30
+ const storybook = withStorybook(test);
31
+ const STORY = 'core-dateinput--default';
32
+ const ARGS = {presentation: 'bottom-sheet'};
33
+
34
+ /** Waits until the scroller's offset has not moved for ten frames. */
35
+ async function waitForRest(viewport: Locator): Promise<number> {
36
+ return viewport.evaluate(
37
+ async element =>
38
+ new Promise<number>(resolve => {
39
+ let last = element.scrollLeft;
40
+ let still = 0;
41
+ const check = () => {
42
+ const now = element.scrollLeft;
43
+ still = now === last ? still + 1 : 0;
44
+ last = now;
45
+ if (still >= 10) {
46
+ resolve(now);
47
+ } else {
48
+ requestAnimationFrame(check);
49
+ }
50
+ };
51
+ requestAnimationFrame(check);
52
+ }),
53
+ );
54
+ }
55
+
56
+ /** The month the scroller rests on, as the pane's own grid names it. */
57
+ /**
58
+ * Waits until a month's grid sits flush with the scroller's edge, the offset
59
+ * unchanged, for ten frames: the end of the scroller's own settle
60
+ * correction, however long the page takes to run it. (The panes' width can
61
+ * be fractional, so the grid's own box is the measure, not the offset.)
62
+ */
63
+ async function waitForRestOnMonth(viewport: Locator): Promise<number> {
64
+ return viewport.evaluate(
65
+ async element =>
66
+ new Promise<number>(resolve => {
67
+ let last = element.scrollLeft;
68
+ let still = 0;
69
+ const flush = () => {
70
+ const edge = element.getBoundingClientRect().left;
71
+ return [
72
+ ...element.querySelectorAll<HTMLElement>('[role="grid"]'),
73
+ ].some(
74
+ grid => Math.abs(grid.getBoundingClientRect().left - edge) < 1,
75
+ );
76
+ };
77
+ const check = () => {
78
+ const now = element.scrollLeft;
79
+ still = now === last && flush() ? still + 1 : 0;
80
+ last = now;
81
+ if (still >= 10) {
82
+ resolve(now);
83
+ } else {
84
+ requestAnimationFrame(check);
85
+ }
86
+ };
87
+ requestAnimationFrame(check);
88
+ }),
89
+ );
90
+ }
91
+
92
+ async function monthAtRest(viewport: Locator): Promise<string> {
93
+ return viewport.evaluate(element => {
94
+ const box = element.getBoundingClientRect();
95
+ const middle = box.left + box.width / 2;
96
+ for (const grid of element.querySelectorAll<HTMLElement>('[role="grid"]')) {
97
+ const r = grid.getBoundingClientRect();
98
+ if (r.left <= middle && r.right >= middle) {
99
+ return grid.getAttribute('aria-label') ?? '';
100
+ }
101
+ }
102
+ return '';
103
+ });
104
+ }
105
+
106
+ async function openCalendar(page: Page) {
107
+ await openStory(page, storybook(), STORY, ARGS);
108
+ await page
109
+ .getByRole('button', {name: /calendar/i})
110
+ .first()
111
+ .click();
112
+ const viewport = page.locator('[data-scroller="months"]');
113
+ await expect(viewport).toBeVisible();
114
+ await waitForRest(viewport);
115
+ return viewport;
116
+ }
117
+
118
+ test.use({hasTouch: false, isMobile: false});
119
+
120
+ /**
121
+ * Waits until the scroller comes to rest, or until it has gone more than
122
+ * `limit` past `from`: a scroller that runs on by itself never rests, and
123
+ * the proof should fail where it ran, not at the test's timeout.
124
+ */
125
+ async function waitForRestOrRun(
126
+ viewport: Locator,
127
+ from: number,
128
+ limit: number,
129
+ ): Promise<number> {
130
+ return viewport.evaluate(
131
+ async (element, {from, limit}) =>
132
+ new Promise<number>(resolve => {
133
+ let last = element.scrollLeft;
134
+ let still = 0;
135
+ const check = () => {
136
+ const now = element.scrollLeft;
137
+ still = now === last ? still + 1 : 0;
138
+ last = now;
139
+ if (still >= 10 || Math.abs(now - from) > limit) {
140
+ resolve(now);
141
+ } else {
142
+ requestAnimationFrame(check);
143
+ }
144
+ };
145
+ requestAnimationFrame(check);
146
+ }),
147
+ {from, limit},
148
+ );
149
+ }
150
+
151
+ /**
152
+ * A stalled frame right after an arrow (a 300 ms block of the main thread)
153
+ * let the re-center that follows the step land after the scroll had ended,
154
+ * and WebKit's re-snap after it then paged on by itself, a month every few
155
+ * frames. The step moves exactly one month.
156
+ */
157
+ test('an arrow step through a stalled frame moves exactly one month', async ({
158
+ page,
159
+ }) => {
160
+ const viewport = await openCalendar(page);
161
+ const title = page.getByRole('button', {name: /Choose month and year/});
162
+ const from = await viewport.evaluate(element => element.scrollLeft);
163
+ const width = await viewport.evaluate(element => element.clientWidth);
164
+ const box = await page
165
+ .getByRole('button', {name: 'Next month'})
166
+ .boundingBox();
167
+ if (box == null) {
168
+ throw new Error('no Next month button');
169
+ }
170
+ await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
171
+ await blockMainThread(page, 300);
172
+ const at = await waitForRestOrRun(viewport, from, 2 * width);
173
+ expect(Math.round((at - from) / width)).toBe(1);
174
+ await waitForRestOnMonth(viewport);
175
+ expect(
176
+ Math.round(((await viewport.evaluate(e => e.scrollLeft)) - from) / width),
177
+ ).toBe(1);
178
+ await expect(title).toHaveAccessibleName(
179
+ new RegExp(await monthAtRest(viewport)),
180
+ );
181
+ });
182
+
183
+ /**
184
+ * Closing the month and year wheels re-asserts the month shown, a steer that
185
+ * moves nothing. A trackpad scroll after it is the user's: it pages to the
186
+ * next month and stays there, the title following.
187
+ */
188
+ test('after the month and year wheels close, a trackpad scroll to the next month stays there', async ({
189
+ page,
190
+ }) => {
191
+ const viewport = await openCalendar(page);
192
+ const toggle = page.getByRole('button', {name: /Choose month and year/});
193
+ const start = await monthAtRest(viewport);
194
+ expect(start).not.toBe('');
195
+ const toggleBox = await toggle.boundingBox();
196
+ if (toggleBox == null) {
197
+ throw new Error('no month and year toggle');
198
+ }
199
+ const clickToggle = async () =>
200
+ page.mouse.click(
201
+ toggleBox.x + toggleBox.width / 2,
202
+ toggleBox.y + toggleBox.height / 2,
203
+ );
204
+ await clickToggle();
205
+ await expect(toggle).toHaveAttribute('aria-expanded', 'true');
206
+ await clickToggle();
207
+ await expect(toggle).toHaveAttribute('aria-expanded', 'false');
208
+ await waitForRest(viewport);
209
+
210
+ const box = await viewport.boundingBox();
211
+ if (box == null) {
212
+ throw new Error('no month scroller');
213
+ }
214
+ await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
215
+ // A horizontal trackpad scroll of 70 % of a month, as one wheel event: a
216
+ // burst of small ones reaches a loaded page far enough apart to count as
217
+ // separate gestures, each snapping back on its own.
218
+ await page.mouse.wheel(box.width * 0.7, 0);
219
+ // Past the scroller's own settle correction too, which ends on a month's
220
+ // boundary.
221
+ await waitForRestOnMonth(viewport);
222
+ const next = await monthAtRest(viewport);
223
+ expect(next).not.toBe(start);
224
+ await expect(toggle).toHaveAccessibleName(new RegExp(next));
225
+ });
226
+
227
+ /**
228
+ * Reset from four months on is a far jump, mounting today's month before the
229
+ * scroller moves. After the month and year wheels close, whose re-assert is a
230
+ * steer that moved nothing, the jump still rests on today's month, the title
231
+ * with it.
232
+ */
233
+ test('after the month and year wheels close, Reset from four months on rests on today’s month', async ({
234
+ page,
235
+ }) => {
236
+ const viewport = await openCalendar(page);
237
+ const toggle = page.getByRole('button', {name: /Choose month and year/});
238
+ const today = await monthAtRest(viewport);
239
+ expect(today).not.toBe('');
240
+ const clickCentre = async (name: string | RegExp) => {
241
+ const box = await page.getByRole('button', {name}).boundingBox();
242
+ if (box == null) {
243
+ throw new Error(`no ${String(name)} button`);
244
+ }
245
+ await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
246
+ };
247
+ for (let step = 0; step < 4; step++) {
248
+ await clickCentre('Next month');
249
+ await waitForRest(viewport);
250
+ }
251
+ expect(await monthAtRest(viewport)).not.toBe(today);
252
+ await clickCentre(/Choose month and year/);
253
+ await expect(toggle).toHaveAttribute('aria-expanded', 'true');
254
+ await clickCentre(/Choose month and year/);
255
+ await expect(toggle).toHaveAttribute('aria-expanded', 'false');
256
+ await waitForRest(viewport);
257
+
258
+ await clickCentre('Reset');
259
+ // Past the scroller's own settle correction too, which ends on a month's
260
+ // boundary.
261
+ await waitForRestOnMonth(viewport);
262
+ expect(await monthAtRest(viewport)).toBe(today);
263
+ await expect(toggle).toHaveAccessibleName(new RegExp(today));
264
+ });
265
+
266
+ test.describe('under a finger', () => {
267
+ test.use({hasTouch: true, isMobile: true});
268
+
269
+ /**
270
+ * A swipe whose last move crosses the middle of the month makes the
271
+ * scroller re-center as the finger lifts. The finger drives that scroll, so
272
+ * the re-center leaves scroll snapping on (turning it off drops the snap
273
+ * target iOS tracks for the gesture, and the swipe coasted past the month),
274
+ * and the swipe comes to rest on the next month.
275
+ */
276
+ test('a swipe that crosses the middle of a month as it lifts keeps snapping on and rests on the next month', async ({
277
+ page,
278
+ }) => {
279
+ const viewport = await openCalendar(page);
280
+ const start = await monthAtRest(viewport);
281
+ const box = await viewport.boundingBox();
282
+ if (box == null) {
283
+ throw new Error('no month scroller');
284
+ }
285
+ // Every change of the scroller's inline scroll-snap-type from here on.
286
+ await viewport.evaluate(element => {
287
+ const changes: string[] = [];
288
+ (window as unknown as {snapChanges: string[]}).snapChanges = changes;
289
+ new MutationObserver(() => {
290
+ changes.push((element as HTMLElement).style.scrollSnapType || 'on');
291
+ }).observe(element, {attributes: true, attributeFilter: ['style']});
292
+ });
293
+ const driver = await touchDriver(page);
294
+ const y = box.y + box.height / 2;
295
+ const from = {x: box.x + box.width * 0.8, y};
296
+ // 55 % of a month toward the next one, the middle crossed by the last
297
+ // move, lifting a quarter frame after it.
298
+ await driver.play(
299
+ drag(from, {x: from.x - box.width * 0.55, y}, {durationMs: 300}),
300
+ );
301
+ await waitForRestOnMonth(viewport);
302
+ const rest = await monthAtRest(viewport);
303
+ expect(rest).not.toBe('');
304
+ expect(rest).not.toBe(start);
305
+ // Snapping was never turned off while the finger drove the scroll.
306
+ expect(
307
+ await page.evaluate(
308
+ () => (window as unknown as {snapChanges: string[]}).snapChanges,
309
+ ),
310
+ ).not.toContain('none');
311
+ });
312
+ });
@@ -18,9 +18,9 @@ function generateThemeTestCSS(theme: Parameters<typeof generateThemeCSS>[0]) {
18
18
  const {prose, component} = generateThemeCSS(theme);
19
19
  return [prose, component].filter(Boolean).join('\n\n');
20
20
  }
21
- // getButton/queryButton instead of getByRole('button', {name}): the closed
22
- // popover keeps a two-month Calendar (~85 role=button nodes) mounted, which
23
- // made every role+name query compute ~85 accessible names through jsdom's
21
+ // getButton/queryButton instead of getByRole('button', {name}): the open
22
+ // popover holds a two-month Calendar (~85 role=button nodes), which makes
23
+ // every role+name query compute ~85 accessible names through jsdom's
24
24
  // slow getComputedStyle (~450ms per query). See fastRoleQueries.ts.
25
25
  import {getButton, queryButton} from '../__tests__/fastRoleQueries';
26
26
  import * as stylex from '@stylexjs/stylex';
@@ -767,6 +767,49 @@ describe('DateRangeInput', () => {
767
767
  });
768
768
  });
769
769
 
770
+ describe('DateRangeInput calendar while the popover is closed', () => {
771
+ const calendarGrid = () => document.querySelector('[role="grid"]');
772
+
773
+ it('renders no calendar until the popover opens, and none after it closes', () => {
774
+ render(
775
+ <DateRangeInput
776
+ label="Range"
777
+ value={{start: '2026-01-05', end: '2026-01-09'}}
778
+ onChange={() => {}}
779
+ />,
780
+ );
781
+ const toggle = getButton('Open calendar');
782
+ expect(calendarGrid()).toBeNull();
783
+
784
+ fireEvent.click(toggle);
785
+ expect(calendarGrid()).not.toBeNull();
786
+
787
+ fireEvent.click(toggle);
788
+ expect(calendarGrid()).toBeNull();
789
+ });
790
+
791
+ it('moves focus into the popover on each open from the toggle', async () => {
792
+ render(
793
+ <DateRangeInput
794
+ label="Range"
795
+ value={{start: '2026-01-05', end: '2026-01-09'}}
796
+ onChange={() => {}}
797
+ />,
798
+ );
799
+ const toggle = getButton('Open calendar');
800
+
801
+ for (let i = 0; i < 2; i++) {
802
+ fireEvent.click(toggle);
803
+ // The first calendar control takes focus, not the dialog surface.
804
+ await waitFor(() => {
805
+ expect(getButton('Previous month')).toHaveFocus();
806
+ });
807
+ fireEvent.click(toggle);
808
+ expect(calendarGrid()).toBeNull();
809
+ }
810
+ });
811
+ });
812
+
770
813
  describe('DateRangeInput statusVariant forwarding', () => {
771
814
  it('defaults to attached (status renders with data-variant="attached")', () => {
772
815
  const {container} = render(
@@ -851,18 +851,22 @@ export function DateRangeInput({
851
851
  })}
852
852
  </div>
853
853
  )}
854
- <Calendar
855
- mode="range"
856
- value={optimisticValue}
857
- onChange={handleRangeSelect}
858
- min={min}
859
- max={max}
860
- dateConstraints={dateConstraints}
861
- maxRangeSpan={maxRangeSpan}
862
- minRangeSpan={minRangeSpan}
863
- numberOfMonths={numberOfMonths}
864
- weekStartsOn={weekStartsOn}
865
- />
854
+ {/* The layer renders its children open or closed, so mount the
855
+ calendar only while it is open. */}
856
+ {popover.isOpen && (
857
+ <Calendar
858
+ mode="range"
859
+ value={optimisticValue}
860
+ onChange={handleRangeSelect}
861
+ min={min}
862
+ max={max}
863
+ dateConstraints={dateConstraints}
864
+ maxRangeSpan={maxRangeSpan}
865
+ minRangeSpan={minRangeSpan}
866
+ numberOfMonths={numberOfMonths}
867
+ weekStartsOn={weekStartsOn}
868
+ />
869
+ )}
866
870
  </div>,
867
871
  {placement: 'below', alignment: 'start'},
868
872
  )}
@@ -115,8 +115,8 @@ export const docs = {
115
115
  name: 'hourFormat',
116
116
  type: "'12h' | '24h'",
117
117
  description:
118
- "Hour display format. '12h' shows AM/PM; '24h' uses 24-hour notation.",
119
- default: "'12h'",
118
+ "Hour display format. '12h' shows the locale's day period; '24h' uses 24-hour notation. When unset, follows the InternationalizationProvider locale's hour cycle (en-US 12-hour, en-GB 24-hour); a person's own preference rides on the locale tag (e.g. 'en-US-u-hc-h23').",
119
+ default: "the locale's hour cycle",
120
120
  },
121
121
  {
122
122
  name: 'timeIncrement',
@@ -270,7 +270,7 @@ export const docs = {
270
270
  {
271
271
  guidance: true,
272
272
  description:
273
- "Choose the hour format (12h or 24h) that matches your audience's locale.",
273
+ "Leave hourFormat unset so the field follows the InternationalizationProvider locale's hour cycle (12-hour in en-US, 24-hour in en-GB or de-DE). Set it only when the field itself needs a fixed format, and carry a person's own 12/24-hour preference on the provider locale (e.g. 'en-US-u-hc-h23') rather than on each field.",
274
274
  },
275
275
  {
276
276
  guidance: false,
@@ -393,7 +393,7 @@ export const docsZh = {
393
393
  {
394
394
  guidance: true,
395
395
  description:
396
- "Choose the hour format (12h or 24h) that matches your audience's locale.",
396
+ "Leave hourFormat unset so the field follows the InternationalizationProvider locale's hour cycle (12-hour in en-US, 24-hour in en-GB or de-DE). Set it only when the field itself needs a fixed format, and carry a person's own 12/24-hour preference on the provider locale (e.g. 'en-US-u-hc-h23') rather than on each field.",
397
397
  },
398
398
  {
399
399
  guidance: false,
@@ -502,8 +502,9 @@ export const docsZh = {
502
502
  {
503
503
  name: 'hourFormat',
504
504
  type: "'12h' | '24h'",
505
- description: "控制显示格式。'12h' 显示 AM/PM;'24h' 使用 24 小时制。",
506
- default: "'12h'",
505
+ description:
506
+ "控制显示格式。'12h' 使用区域设置的上午/下午用语;'24h' 使用 24 小时制。未设置时遵循 InternationalizationProvider 区域设置的小时制(en-US 为 12 小时制,en-GB 为 24 小时制);用户自己的偏好可通过区域标签传入(例如 'en-US-u-hc-h23')。",
507
+ default: '区域设置的小时制',
507
508
  },
508
509
  {
509
510
  name: 'timeIncrement',
@@ -648,7 +649,7 @@ export const docsDense = {
648
649
  {
649
650
  guidance: true,
650
651
  description:
651
- "Choose the hour format (12h or 24h) that matches your audience's locale.",
652
+ "Leave hourFormat unset so the field follows the InternationalizationProvider locale's hour cycle (12-hour in en-US, 24-hour in en-GB or de-DE). Set it only when the field itself needs a fixed format, and carry a person's own 12/24-hour preference on the provider locale (e.g. 'en-US-u-hc-h23') rather than on each field.",
652
653
  },
653
654
  {
654
655
  guidance: false,
@@ -690,7 +691,8 @@ export const docsDense = {
690
691
  dateConstraints: 'custom constraint fns to disable specific dates',
691
692
  hasSeconds:
692
693
  'include seconds; retains Astryx time field because iOS native picker has no seconds wheel',
693
- hourFormat: "display format. '12h' shows AM/PM; '24h' uses 24-hour",
694
+ hourFormat:
695
+ "display format. '12h' shows the locale's day period; '24h' uses 24-hour. Unset follows the provider locale's hour cycle (a -u-hc- tag carries a user preference)",
694
696
  timeIncrement:
695
697
  'minute step for Astryx typed field; non-default retains Astryx time field in native modes because iOS native step is validation-only',
696
698
  timeOptionInterval: