@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
@@ -12,7 +12,8 @@
12
12
  * rest, the rail ends icons-only or with its labels, a second press mid-motion
13
13
  * stays continuous, an expand whose old wrap is not copied keeps the edges
14
14
  * together, and Reduce Motion cross-fades under a finger (DR2) and changes at
15
- * once under a mouse (DR3)
15
+ * once under a mouse (DR3), also on a loaded machine: those two cases read
16
+ * the motion by its own start and end, never at a fixed time
16
17
  * @position Testing; the components' unit cases are
17
18
  * SideNav/SideNavRailMotion.test.tsx
18
19
  *
@@ -216,30 +217,145 @@ function toggleButton(page: Page) {
216
217
  return page.getByRole('button', {name: /^(Collapse|Expand) sidebar$/});
217
218
  }
218
219
 
219
- /** Every running animation's animated properties and duration. */
220
- async function runningMotion(page: Page) {
221
- return page.evaluate(() =>
222
- document.getAnimations().map(animation => {
223
- const effect = animation.effect as KeyframeEffect;
224
- const properties = new Set<string>();
225
- for (const frame of effect.getKeyframes()) {
226
- for (const key of Object.keys(frame)) {
227
- if (
228
- !['offset', 'computedOffset', 'easing', 'composite'].includes(key)
229
- ) {
230
- properties.add(key);
220
+ /**
221
+ * A loaded machine, made repeatable: Chromium's CPU throttle slows the page's
222
+ * main thread four times, or its animation clock runs every animation at a
223
+ * quarter of its speed, as when frames come late. Neither changes what the
224
+ * motion is; both move when it starts and ends in real time.
225
+ */
226
+ const LOADS = ['idle', 'a slow CPU', 'slow frames'] as const;
227
+ type Load = (typeof LOADS)[number];
228
+
229
+ async function applyLoad(page: Page, load: Load): Promise<void> {
230
+ if (load === 'idle') {
231
+ return;
232
+ }
233
+ const cdp = await page.context().newCDPSession(page);
234
+ if (load === 'a slow CPU') {
235
+ await cdp.send('Emulation.setCPUThrottlingRate', {rate: 4});
236
+ } else {
237
+ await cdp.send('Animation.enable');
238
+ await cdp.send('Animation.setPlaybackRate', {playbackRate: 0.25});
239
+ }
240
+ }
241
+
242
+ /** The test-name suffix for a load: nothing when idle. */
243
+ function underLoad(load: Load): string {
244
+ return load === 'idle' ? '' : `, with ${load}`;
245
+ }
246
+
247
+ /** One animation of the rail's motion: what it animates and when it ends. */
248
+ interface MotionPart {
249
+ readonly properties: ReadonlyArray<string>;
250
+ /**
251
+ * When it ends on its own clock, in ms: its delay, every iteration and its
252
+ * end delay, so a delayed or repeated fade counts in full.
253
+ */
254
+ readonly endTime: number;
255
+ /** Whether it animates an element holding the page's main content. */
256
+ readonly holdsPage: boolean;
257
+ }
258
+
259
+ /** The record `recordMotion` keeps on the page, for `settledMotion`. */
260
+ interface MotionRecord {
261
+ readonly seen: Map<Animation, MotionPart>;
262
+ readonly collect: () => void;
263
+ sampling: boolean;
264
+ }
265
+
266
+ /**
267
+ * Starts recording the motion a press of the toggle causes: every animation
268
+ * running on any frame from now on, described when first seen. Animations on
269
+ * the toggle element itself are its press feedback (its highlight's release
270
+ * fade runs on the button), not the rail's, so they are left out; everything
271
+ * inside it, its chevron included, stays in. `settledMotion` ends the record.
272
+ */
273
+ async function recordMotion(page: Page): Promise<void> {
274
+ await toggleButton(page).evaluate(toggle => {
275
+ const seen = new Map<Animation, MotionPart>();
276
+ const collect = () => {
277
+ for (const animation of document.getAnimations()) {
278
+ const effect = animation.effect;
279
+ if (
280
+ seen.has(animation) ||
281
+ !(effect instanceof KeyframeEffect) ||
282
+ effect.target === toggle
283
+ ) {
284
+ continue;
285
+ }
286
+ const properties = new Set<string>();
287
+ for (const frame of effect.getKeyframes()) {
288
+ for (const key of Object.keys(frame)) {
289
+ if (
290
+ !['offset', 'computedOffset', 'easing', 'composite'].includes(key)
291
+ ) {
292
+ properties.add(key);
293
+ }
231
294
  }
232
295
  }
296
+ seen.set(animation, {
297
+ properties: [...properties],
298
+ endTime: Number(effect.getComputedTiming().endTime),
299
+ holdsPage:
300
+ effect.target instanceof Element &&
301
+ effect.target.contains(document.querySelector('[role="main"]')),
302
+ });
233
303
  }
234
- return {
235
- properties: [...properties],
236
- duration: Number(effect.getTiming().duration),
237
- holdsPage:
238
- effect.target instanceof Element &&
239
- effect.target.contains(document.querySelector('[role="main"]')),
240
- };
241
- }),
242
- );
304
+ };
305
+ const record: MotionRecord = {seen, collect, sampling: true};
306
+ (window as unknown as {railMotion: MotionRecord}).railMotion = record;
307
+ const sample = () => {
308
+ if (record.sampling) {
309
+ collect();
310
+ requestAnimationFrame(sample);
311
+ }
312
+ };
313
+ requestAnimationFrame(sample);
314
+ });
315
+ }
316
+
317
+ /**
318
+ * Ends the record once the motion has ended on its own clock: every recorded
319
+ * animation has finished or been cancelled, and two more frames have passed
320
+ * without a new one, so what its end commits has landed. One that never ends
321
+ * (endless iterations) is not waited for; it is returned with an `endTime`
322
+ * of Infinity, for the cases to fail on. It never waits a fixed time: a loaded
323
+ * machine changes how long this takes, not what it reads.
324
+ */
325
+ async function settledMotion(page: Page): Promise<ReadonlyArray<MotionPart>> {
326
+ return page.evaluate(async () => {
327
+ const record = (window as unknown as {railMotion: MotionRecord}).railMotion;
328
+ const isRunning = (animation: Animation) =>
329
+ animation.playState === 'running' &&
330
+ Number.isFinite(Number(animation.effect?.getComputedTiming().endTime));
331
+ const twoFrames = async () =>
332
+ new Promise<void>(resolve => {
333
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
334
+ });
335
+ for (;;) {
336
+ record.collect();
337
+ const running = [...record.seen.keys()].filter(isRunning);
338
+ if (running.length > 0) {
339
+ await Promise.all(
340
+ running.map(async animation =>
341
+ animation.finished.catch(() => undefined),
342
+ ),
343
+ );
344
+ continue;
345
+ }
346
+ const seenBefore = record.seen.size;
347
+ await twoFrames();
348
+ record.collect();
349
+ if (
350
+ record.seen.size === seenBefore &&
351
+ ![...record.seen.keys()].some(isRunning)
352
+ ) {
353
+ break;
354
+ }
355
+ }
356
+ record.sampling = false;
357
+ return [...record.seen.values()];
358
+ });
243
359
  }
244
360
 
245
361
  test.describe('component:SideNav FR6 and component:AppShell FR17 under a finger', () => {
@@ -341,25 +457,29 @@ test.describe('component:SideNav FR6 and component:AppShell FR17 under a finger'
341
457
  });
342
458
  });
343
459
 
344
- test('under Reduce Motion a tap only cross-fades, in 200 ms or less, with the page surface still (design:reduced-motion DR2)', async ({
345
- page,
346
- }) => {
347
- await page.emulateMedia({reducedMotion: 'reduce'});
348
- await openStory(page, PHONE_STORY);
349
- await toggleButton(page).tap();
350
- const motion = await runningMotion(page);
351
- expect(motion.length).toBeGreaterThan(0);
352
- for (const {properties, duration, holdsPage} of motion) {
353
- expect(properties).toEqual(['opacity']);
354
- expect(duration).toBeLessThanOrEqual(200);
355
- expect(holdsPage).toBe(false);
356
- }
357
- await page.waitForTimeout(400);
358
- expect(await railAtRest(page)).toMatchObject({
359
- labelsShown: false,
360
- copies: 0,
460
+ for (const load of LOADS) {
461
+ test(`under Reduce Motion a tap only cross-fades, in 200 ms or less, with the page surface still${underLoad(load)} (design:reduced-motion DR2)`, async ({
462
+ page,
463
+ }) => {
464
+ await page.emulateMedia({reducedMotion: 'reduce'});
465
+ await openStory(page, PHONE_STORY);
466
+ await applyLoad(page, load);
467
+ await recordMotion(page);
468
+ await toggleButton(page).tap();
469
+ await expect(toggleButton(page)).toHaveAccessibleName('Expand sidebar');
470
+ const motion = await settledMotion(page);
471
+ expect(motion.length).toBeGreaterThan(0);
472
+ for (const {properties, endTime, holdsPage} of motion) {
473
+ expect(properties).toEqual(['opacity']);
474
+ expect(endTime).toBeLessThanOrEqual(200);
475
+ expect(holdsPage).toBe(false);
476
+ }
477
+ expect(await railAtRest(page)).toMatchObject({
478
+ labelsShown: false,
479
+ copies: 0,
480
+ });
361
481
  });
362
- });
482
+ }
363
483
  });
364
484
 
365
485
  test.describe('component:SideNav FR6 and component:AppShell FR17 under a mouse', () => {
@@ -402,16 +522,21 @@ test.describe('component:SideNav FR6 and component:AppShell FR17 under a mouse',
402
522
  });
403
523
  });
404
524
 
405
- test('under Reduce Motion a click changes the rail and the page at once (design:reduced-motion DR3)', async ({
406
- page,
407
- }) => {
408
- await page.emulateMedia({reducedMotion: 'reduce'});
409
- await openStory(page, DESKTOP_STORY);
410
- await toggleButton(page).click();
411
- expect(await runningMotion(page)).toEqual([]);
412
- expect(await railAtRest(page)).toMatchObject({
413
- labelsShown: false,
414
- copies: 0,
525
+ for (const load of LOADS) {
526
+ test(`under Reduce Motion a click changes the rail and the page at once${underLoad(load)} (design:reduced-motion DR3)`, async ({
527
+ page,
528
+ }) => {
529
+ await page.emulateMedia({reducedMotion: 'reduce'});
530
+ await openStory(page, DESKTOP_STORY);
531
+ await applyLoad(page, load);
532
+ await recordMotion(page);
533
+ await toggleButton(page).click();
534
+ await expect(toggleButton(page)).toHaveAccessibleName('Expand sidebar');
535
+ expect(await settledMotion(page)).toEqual([]);
536
+ expect(await railAtRest(page)).toMatchObject({
537
+ labelsShown: false,
538
+ copies: 0,
539
+ });
415
540
  });
416
- });
541
+ }
417
542
  });
@@ -97,7 +97,9 @@ const styles = stylex.create({
97
97
  },
98
98
  animate: {
99
99
  animationDirection: 'alternate',
100
- animationDuration: durationVars['--duration-medium-max'],
100
+ // The loop reads the duration before a theme's tempo scales it
101
+ // (spec:AST-108 FR4).
102
+ animationDuration: `var(--_astryx-pre-tempo-duration-medium-max, ${durationVars['--duration-medium-max']})`,
101
103
  animationIterationCount: 'infinite',
102
104
  // Disable the pulse under reduced-motion; the static placeholder still
103
105
  // reads as loading (complex-20).
@@ -9,6 +9,7 @@
9
9
  * SYNC: When Slider.tsx changes, update tests to match new behavior
10
10
  */
11
11
 
12
+ import {InternationalizationProvider} from '../i18n';
12
13
  import {useState} from 'react';
13
14
  import {describe, it, expect, vi, beforeEach} from 'vitest';
14
15
  import {render, screen, act, fireEvent, waitFor} from '@testing-library/react';
@@ -75,6 +76,24 @@ describe('Slider', () => {
75
76
  expect(sliders[1]).toHaveAccessibleName('Maximum value');
76
77
  });
77
78
 
79
+ it('translates the range thumb names under another locale', () => {
80
+ render(
81
+ <InternationalizationProvider
82
+ locale="fr"
83
+ overrides={{
84
+ fr: {
85
+ '@astryx.slider.minimumThumb': 'Valeur minimale',
86
+ '@astryx.slider.maximumThumb': 'Valeur maximale',
87
+ },
88
+ }}>
89
+ <Slider label="Prix" value={[20, 80] as [number, number]} />
90
+ </InternationalizationProvider>,
91
+ );
92
+ const sliders = screen.getAllByRole('slider');
93
+ expect(sliders[0]).toHaveAccessibleName('Valeur minimale');
94
+ expect(sliders[1]).toHaveAccessibleName('Valeur maximale');
95
+ });
96
+
78
97
  it('sets aria-valuetext with formatValue', () => {
79
98
  render(
80
99
  <Slider label="Temperature" value={72} formatValue={v => `${v}°F`} />,
@@ -337,6 +356,18 @@ describe('Slider', () => {
337
356
  expect(slider).not.toHaveAttribute('aria-required');
338
357
  });
339
358
 
359
+ it('conveys required state under FormLayout defaultOptionality="required" unless isOptional (FormLayout FR5)', () => {
360
+ render(
361
+ <FormLayout defaultOptionality="required">
362
+ <Slider label="Volume" value={50} />
363
+ <Slider label="Balance" value={50} isOptional />
364
+ </FormLayout>,
365
+ );
366
+ const [volume, balance] = screen.getAllByRole('slider');
367
+ expect(volume).toHaveAccessibleDescription(/Required/);
368
+ expect(balance).not.toHaveAccessibleDescription(/Required/);
369
+ });
370
+
340
371
  it('conveys required state on both thumbs of a range slider', () => {
341
372
  render(
342
373
  <Slider
@@ -58,6 +58,8 @@ import type {SizeValue} from '../utils/types';
58
58
  import {themeProps} from '../utils/themeProps';
59
59
 
60
60
  import {useMergedRefs} from '../hooks/useMergedRefs';
61
+ import {useResolvedRequired} from '../hooks/useResolvedRequired';
62
+ import {useTranslator} from '../i18n';
61
63
  // =============================================================================
62
64
  // Types
63
65
  // =============================================================================
@@ -512,6 +514,7 @@ export function Slider({ref, ...props}: SliderProps) {
512
514
  'aria-describedby': ariaDescribedByProp,
513
515
  ...restWithRange
514
516
  } = props;
517
+ const t = useTranslator();
515
518
  const {minStepsBetweenThumbs: _minSteps, ...restProps} =
516
519
  restWithRange as typeof restWithRange & {minStepsBetweenThumbs?: number};
517
520
  const {rest, container} = useFieldInputStyling('Slider', restProps, {
@@ -591,7 +594,9 @@ export function Slider({ref, ...props}: SliderProps) {
591
594
  // role="slider" in WAI-ARIA 1.2, so the thumb instead points its
592
595
  // aria-describedby at a visually hidden "Required" span — mirroring the
593
596
  // Field label's visible indicator (where isOptional takes precedence).
594
- const conveysRequired = isRequired && !isOptional;
597
+ // A surrounding FormLayout's defaultOptionality="required" makes an unmarked
598
+ // field required too (FormLayout FR5).
599
+ const conveysRequired = useResolvedRequired({isRequired, isOptional});
595
600
 
596
601
  // Build aria-describedby
597
602
  const describedByParts: string[] = [];
@@ -980,8 +985,8 @@ export function Slider({ref, ...props}: SliderProps) {
980
985
  // name from the visible label element via aria-labelledby.
981
986
  const thumbLabel = isRange
982
987
  ? thumbIndex === 0
983
- ? 'Minimum value'
984
- : 'Maximum value'
988
+ ? t('@astryx.slider.minimumThumb')
989
+ : t('@astryx.slider.maximumThumb')
985
990
  : undefined;
986
991
 
987
992
  // ARIA bounds must agree with the movement clamping in updateValue: in
@@ -87,16 +87,28 @@ async function runState(
87
87
  });
88
88
  }
89
89
 
90
- test('every slider expectation is exercised by a Slider state', async ({
91
- page,
92
- }) => {
93
- const cdp = await page.context().newCDPSession(page);
90
+ // Checking coverage runs every state again, which on a loaded runner nears
91
+ // the per-test timeout as one test. It runs as one serial part per state,
92
+ // sharing their results, then one check over all of them.
93
+ test.describe('every slider expectation is exercised by a Slider state', () => {
94
+ test.describe.configure({mode: 'serial'});
94
95
  const results: BindingResult[] = [];
96
+
95
97
  for (const state of SLIDER_BINDING_STATES) {
96
- results.push(await runState(page, cdp, state));
98
+ test(`runs ${state.id}`, async ({page}) => {
99
+ const cdp = await page.context().newCDPSession(page);
100
+ results.push(await runState(page, cdp, state));
101
+ });
97
102
  }
98
- expect(neverExercised(results)).toEqual([]);
99
- expect(unmatchedKnownFailures(SLIDER_KNOWN_FAILURES, results)).toEqual([]);
103
+
104
+ test('no expectation is left unexercised', () => {
105
+ // Every part ran in this worker; this test alone proves nothing.
106
+ expect(results.map(result => result.state).sort()).toEqual(
107
+ SLIDER_BINDING_STATES.map(state => state.id).sort(),
108
+ );
109
+ expect(neverExercised(results)).toEqual([]);
110
+ expect(unmatchedKnownFailures(SLIDER_KNOWN_FAILURES, results)).toEqual([]);
111
+ });
100
112
  });
101
113
 
102
114
  for (const state of SLIDER_BINDING_STATES) {
@@ -21,8 +21,8 @@ import * as stylex from '@stylexjs/stylex';
21
21
  import {colorVars, durationVars, spacingVars} from '../theme/tokens.stylex';
22
22
  import type {BaseProps} from '../BaseProps';
23
23
  import {Text} from '../Text/Text';
24
- import {mergeProps} from '../utils';
25
- import {useTranslator} from '../i18n';
24
+ import {mergeProps} from '../utils/mergeProps';
25
+ import {useTranslator} from '../i18n/useTranslator';
26
26
  import {themeProps} from '../utils/themeProps';
27
27
 
28
28
  // =============================================================================
@@ -334,7 +334,9 @@ const styles = stylex.create({
334
334
  // it (a frozen spinner reads as broken), matching ProgressBar's approach.
335
335
  // The role="status" + "Loading" label still convey busy state (obs-6).
336
336
  animationDuration: {
337
- default: durationVars['--duration-slow-min'],
337
+ // The loop reads the duration before a theme's tempo scales it
338
+ // (spec:AST-108 FR4).
339
+ default: `var(--_astryx-pre-tempo-duration-slow-min, ${durationVars['--duration-slow-min']})`,
338
340
  '@media (prefers-reduced-motion: reduce)': '3s',
339
341
  },
340
342
  animationIterationCount: 'infinite',
@@ -30,7 +30,7 @@ import {
30
30
  paddingBlockStartStyles,
31
31
  paddingBlockEndStyles,
32
32
  } from '../Layout/padding.stylex';
33
- import {mergeProps} from '../utils';
33
+ import {mergeProps} from '../utils/mergeProps';
34
34
  import {themeProps} from '../utils/themeProps';
35
35
 
36
36
  const overflowStyles = stylex.create({
@@ -19,7 +19,7 @@ import {
19
19
  type StackItemCrossAlignSelf,
20
20
  type StackItemSize,
21
21
  } from './stackItem.stylex';
22
- import {mergeProps} from '../utils';
22
+ import {mergeProps} from '../utils/mergeProps';
23
23
  import {themeProps} from '../utils/themeProps';
24
24
 
25
25
  const overflowStyles = stylex.create({
@@ -9,8 +9,9 @@
9
9
  * SYNC: When TabList components change, update tests to match new behavior
10
10
  */
11
11
 
12
+ import {useState} from 'react';
12
13
  import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
13
- import {render, screen, fireEvent} from '@testing-library/react';
14
+ import {act, render, screen, fireEvent, waitFor} from '@testing-library/react';
14
15
  import {__resetMenuPressForTest} from '../hooks/useMenuPress';
15
16
  import userEvent from '@testing-library/user-event';
16
17
  import {
@@ -933,8 +934,10 @@ describe('TabMenu keyboard navigation (roving tabindex)', () => {
933
934
  hidden: true,
934
935
  });
935
936
 
936
- fireEvent.keyDown(menu, {key: 'ArrowDown'});
937
- expect(analytics).toHaveFocus();
937
+ // Opening moves focus to the first item a frame later (APG menu-button
938
+ // focus-on-open): wait for it, since a key pressed before that frame
939
+ // starts from no item and one pressed after it starts from the first.
940
+ await waitFor(() => expect(analytics).toHaveFocus());
938
941
 
939
942
  fireEvent.keyDown(menu, {key: 'ArrowDown'});
940
943
  expect(reports).toHaveFocus();
@@ -963,7 +966,8 @@ describe('TabMenu keyboard navigation (roving tabindex)', () => {
963
966
  hidden: true,
964
967
  });
965
968
 
966
- fireEvent.keyDown(menu, {key: 'ArrowDown'}); // focus analytics
969
+ // Focus lands on the first item a frame after opening (see above).
970
+ await waitFor(() => expect(analytics).toHaveFocus());
967
971
  fireEvent.keyDown(menu, {key: 'ArrowDown'}); // focus reports
968
972
 
969
973
  // The tab stop follows focus, so it is still a single stop after moving.
@@ -1237,6 +1241,56 @@ describe('TabList overflow (scroll)', () => {
1237
1241
  expect(scrollBy).not.toHaveBeenCalled();
1238
1242
  });
1239
1243
 
1244
+ it('measures a pressed tab after the press, in the next frame, and still shows it whole', async () => {
1245
+ function Controlled() {
1246
+ const [value, setValue] = useState('a');
1247
+ return (
1248
+ <TabList value={value} onChange={setValue}>
1249
+ <Tab value="a" label="Alpha" />
1250
+ <Tab value="b" label="Beta" />
1251
+ <Tab value="c" label="Gamma" />
1252
+ </TabList>
1253
+ );
1254
+ }
1255
+ const {container} = render(<Controlled />);
1256
+ const strip = container.querySelector<HTMLElement>(STRIP);
1257
+ const tab = container.querySelector<HTMLElement>('[data-tab-value="c"]');
1258
+ if (!strip || !tab) {
1259
+ throw new Error('no tab strip');
1260
+ }
1261
+ const scrollBy = vi.fn();
1262
+ strip.scrollBy = scrollBy;
1263
+ const stripRect = vi.fn(
1264
+ () => ({left: 0, right: 300, width: 300}) as DOMRect,
1265
+ );
1266
+ strip.getBoundingClientRect = stripRect;
1267
+ tab.getBoundingClientRect = () =>
1268
+ ({left: 240, right: 320, width: 80}) as DOMRect;
1269
+
1270
+ // Pressing a tab focuses it first, and focusing it reveals it.
1271
+ fireEvent.focus(tab);
1272
+ expect(scrollBy).toHaveBeenCalledTimes(1);
1273
+ expect(scrollBy.mock.calls[0][0].left).toBe(20);
1274
+ const readsBeforePress = stripRect.mock.calls.length;
1275
+
1276
+ // The selection itself reads no geometry inside the press...
1277
+ fireEvent.click(tab);
1278
+ expect(tab).toHaveAttribute('aria-current', 'true');
1279
+ expect(stripRect.mock.calls.length).toBe(readsBeforePress);
1280
+ expect(scrollBy).toHaveBeenCalledTimes(1);
1281
+
1282
+ // ...and the next frame measures the selected tab, now wider, so it still
1283
+ // ends fully inside the strip.
1284
+ tab.getBoundingClientRect = () =>
1285
+ ({left: 220, right: 306, width: 86}) as DOMRect;
1286
+ await act(
1287
+ async () =>
1288
+ new Promise<void>(resolve => requestAnimationFrame(() => resolve())),
1289
+ );
1290
+ expect(scrollBy).toHaveBeenCalledTimes(2);
1291
+ expect(scrollBy.mock.calls[1][0].left).toBe(6);
1292
+ });
1293
+
1240
1294
  it('reveals on a selection change, not on an unrelated overflow change', () => {
1241
1295
  const {strip, rerender} = renderStrip({value: 'c', overflow: 'visible'});
1242
1296
  const scrollBy = vi.fn();
@@ -515,6 +515,10 @@ export function TabList({
515
515
  revealSelectedTabRef.current = revealSelectedTab;
516
516
  }, [revealSelectedTab]);
517
517
 
518
+ // The tab whose focus revealed it (see `handleRootFocus`), while it keeps
519
+ // focus.
520
+ const focusRevealedValueRef = useRef<string | null>(null);
521
+
518
522
  // The tab you are on has to be visible. Selection can move without focus —
519
523
  // on mount, or when the host sets `value` itself — and neither scrolls the
520
524
  // strip the way clicking or arrowing to a tab does. The check is what makes
@@ -525,6 +529,14 @@ export function TabList({
525
529
  return;
526
530
  }
527
531
  revealedValueRef.current = value;
532
+ if (value != null && focusRevealedValueRef.current === value) {
533
+ // A press or an arrow focused this tab first, and focusing it revealed
534
+ // it. Measuring now would force the new selection's style and layout
535
+ // inside the press, before it can paint. The next frame lays them out
536
+ // anyway, so finish there: the selected weight can still widen the tab.
537
+ const frame = requestAnimationFrame(() => revealSelectedTabRef.current());
538
+ return () => cancelAnimationFrame(frame);
539
+ }
528
540
  revealSelectedTabRef.current();
529
541
  }, [value]);
530
542
 
@@ -595,7 +607,9 @@ export function TabList({
595
607
  // The browser scrolls a focused element into view only when it is
596
608
  // entirely outside the scrollport, so arrowing onto a half-visible stop
597
609
  // leaves it cut off under the fade. Finish the job it started.
598
- revealStop(e.target.closest(TAB_STOP_SELECTOR));
610
+ const stop = e.target.closest<HTMLElement>(TAB_STOP_SELECTOR);
611
+ revealStop(stop);
612
+ focusRevealedValueRef.current = stop?.dataset.tabValue ?? null;
599
613
  },
600
614
  [onFocusProp, onHintFocus, handleFocus, revealStop],
601
615
  );
@@ -603,6 +617,7 @@ export function TabList({
603
617
  const handleRootBlur = useCallback(
604
618
  (e: React.FocusEvent<HTMLElement>) => {
605
619
  onBlurProp?.(e);
620
+ focusRevealedValueRef.current = null;
606
621
  if (e.defaultPrevented) {
607
622
  return;
608
623
  }