@astryxdesign/core 0.7.6-canary.f73623d → 0.7.6-canary.f77aaf2

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 (1395) hide show
  1. package/dist/AppShell/AppShell.d.ts.map +1 -1
  2. package/dist/AppShell/AppShell.js +0 -6
  3. package/dist/Badge/Badge.d.ts.map +1 -1
  4. package/dist/Badge/Badge.js +2 -1
  5. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
  6. package/dist/BottomSheet/BottomSheet.js +6 -2
  7. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
  8. package/dist/BottomSheet/BottomSheetPanel.js +36 -9
  9. package/dist/BottomSheet/BottomSheetSwitcher.d.ts +1 -1
  10. package/dist/BottomSheet/BottomSheetSwitcher.js +1 -1
  11. package/dist/BottomSheet/useMobileKeyboard.d.ts.map +1 -1
  12. package/dist/BottomSheet/useMobileKeyboard.js +44 -8
  13. package/dist/BottomSheet/useSheetGestures.d.ts +36 -6
  14. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -1
  15. package/dist/BottomSheet/useSheetGestures.js +67 -10
  16. package/dist/Breadcrumbs/BreadcrumbItem.js +4 -2
  17. package/dist/Center/Center.d.ts +1 -1
  18. package/dist/Center/Center.js +1 -1
  19. package/dist/Chat/ChatComposer.d.ts.map +1 -1
  20. package/dist/Chat/ChatComposer.js +10 -5
  21. package/dist/Chat/ChatComposerDrawer.d.ts +1 -1
  22. package/dist/Chat/ChatComposerDrawer.js +1 -1
  23. package/dist/Chat/ChatComposerInput.d.ts +1 -1
  24. package/dist/Chat/ChatComposerInput.js +1 -1
  25. package/dist/Chat/ChatLayout.d.ts.map +1 -1
  26. package/dist/Chat/ChatLayout.js +1 -1
  27. package/dist/Chat/ChatMessageList.d.ts.map +1 -1
  28. package/dist/Chat/ChatMessageList.js +3 -1
  29. package/dist/Chat/ChatPastedTextToken.d.ts.map +1 -1
  30. package/dist/Chat/ChatPastedTextToken.js +15 -4
  31. package/dist/Chat/ChatSystemMessage.d.ts.map +1 -1
  32. package/dist/Chat/ChatSystemMessage.js +2 -1
  33. package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
  34. package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
  35. package/dist/CheckboxList/CheckboxList.js +5 -2
  36. package/dist/CheckboxList/CheckboxListContext.d.ts +8 -0
  37. package/dist/CheckboxList/CheckboxListContext.d.ts.map +1 -1
  38. package/dist/CheckboxList/CheckboxListItem.d.ts.map +1 -1
  39. package/dist/CheckboxList/CheckboxListItem.js +5 -2
  40. package/dist/Code/Code.d.ts +1 -1
  41. package/dist/Code/Code.js +1 -1
  42. package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
  43. package/dist/CommandPalette/CommandPaletteInput.js +8 -5
  44. package/dist/ComplexSelector/ComplexSelector.d.ts +2 -2
  45. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  46. package/dist/ComplexSelector/ComplexSelector.js +7 -2
  47. package/dist/DateInput/DateInput.d.ts +3 -2
  48. package/dist/DateInput/DateInput.d.ts.map +1 -1
  49. package/dist/DateInput/MonthScroller.d.ts.map +1 -1
  50. package/dist/DateInput/MonthScroller.js +17 -18
  51. package/dist/DateRangeInput/DateRangeInput.d.ts +3 -2
  52. package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
  53. package/dist/DateRangeInput/DateRangeInput.js +8 -4
  54. package/dist/Dialog/Dialog.d.ts.map +1 -1
  55. package/dist/Dialog/Dialog.js +10 -15
  56. package/dist/Drawer/Drawer.d.ts +1 -1
  57. package/dist/Drawer/Drawer.js +1 -1
  58. package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
  59. package/dist/DropdownMenu/DropdownMenu.js +19 -8
  60. package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
  61. package/dist/DropdownMenu/DropdownMenuGroup.js +2 -1
  62. package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
  63. package/dist/DropdownMenu/DropdownMenuSubMenu.js +1 -1
  64. package/dist/DropdownMenu/MenuBottomSheetActionList.d.ts.map +1 -1
  65. package/dist/DropdownMenu/MenuBottomSheetActionList.js +9 -3
  66. package/dist/Field/Field.d.ts +2 -2
  67. package/dist/Field/Field.d.ts.map +1 -1
  68. package/dist/Field/FieldLabel.d.ts.map +1 -1
  69. package/dist/Field/FieldLabel.js +19 -2
  70. package/dist/Field/PanelSearchInput.d.ts.map +1 -1
  71. package/dist/Field/PanelSearchInput.js +10 -5
  72. package/dist/Field/fieldStatusPlacement.d.ts +15 -0
  73. package/dist/Field/fieldStatusPlacement.d.ts.map +1 -0
  74. package/dist/Field/fieldStatusPlacement.js +1 -0
  75. package/dist/FieldStatus/FieldStatus.d.ts +16 -28
  76. package/dist/FieldStatus/FieldStatus.d.ts.map +1 -1
  77. package/dist/FieldStatus/FieldStatus.js +25 -3
  78. package/dist/FieldStatus/index.d.ts +7 -0
  79. package/dist/FieldStatus/index.d.ts.map +1 -1
  80. package/dist/FileInput/FileInput.d.ts +10 -2
  81. package/dist/FileInput/FileInput.d.ts.map +1 -1
  82. package/dist/FileInput/FileInput.js +86 -35
  83. package/dist/Item/Item.js +5 -5
  84. package/dist/Item/ItemSwipeLayer.d.ts.map +1 -1
  85. package/dist/Item/ItemSwipeLayer.js +8 -2
  86. package/dist/Layer/useTouchTrigger.d.ts +35 -3
  87. package/dist/Layer/useTouchTrigger.d.ts.map +1 -1
  88. package/dist/Layer/useTouchTrigger.js +80 -11
  89. package/dist/Link/Link.d.ts.map +1 -1
  90. package/dist/Link/Link.js +10 -1
  91. package/dist/Markdown/Markdown.d.ts.map +1 -1
  92. package/dist/Markdown/Markdown.js +1 -0
  93. package/dist/MetadataList/MetadataList.d.ts.map +1 -1
  94. package/dist/MetadataList/MetadataList.js +2 -1
  95. package/dist/MetadataList/MetadataListItem.d.ts.map +1 -1
  96. package/dist/MetadataList/MetadataListItem.js +2 -1
  97. package/dist/MultiSelector/MultiSelector.d.ts +2 -2
  98. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  99. package/dist/MultiSelector/MultiSelector.js +35 -25
  100. package/dist/MultiSelector/hooks.d.ts.map +1 -1
  101. package/dist/MultiSelector/hooks.js +4 -2
  102. package/dist/NumberInput/NumberInput.d.ts +3 -2
  103. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  104. package/dist/NumberInput/NumberInput.js +11 -15
  105. package/dist/OverflowList/OverflowList.d.ts.map +1 -1
  106. package/dist/OverflowList/OverflowList.js +2 -1
  107. package/dist/Pagination/Pagination.d.ts.map +1 -1
  108. package/dist/Popover/Popover.js +2 -2
  109. package/dist/Popover/usePopover.d.ts +7 -1
  110. package/dist/Popover/usePopover.d.ts.map +1 -1
  111. package/dist/Popover/usePopover.js +8 -2
  112. package/dist/PowerSearch/PowerSearch.d.ts +3 -2
  113. package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
  114. package/dist/PowerSearch/PowerSearch.js +4 -2
  115. package/dist/RadioList/RadioList.d.ts.map +1 -1
  116. package/dist/RadioList/RadioList.js +7 -2
  117. package/dist/RadioList/RadioListItem.d.ts.map +1 -1
  118. package/dist/RadioList/RadioListItem.js +8 -2
  119. package/dist/RadioList/RadioListReasonContext.d.ts +2 -0
  120. package/dist/RadioList/RadioListReasonContext.d.ts.map +1 -0
  121. package/dist/RadioList/RadioListReasonContext.js +22 -0
  122. package/dist/Resizable/useResizable.d.ts.map +1 -1
  123. package/dist/Resizable/useResizable.js +53 -10
  124. package/dist/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  125. package/dist/SegmentedControl/SegmentedControl.js +22 -5
  126. package/dist/SegmentedControl/SegmentedControlContext.d.ts +6 -0
  127. package/dist/SegmentedControl/SegmentedControlContext.d.ts.map +1 -1
  128. package/dist/SegmentedControl/SegmentedControlItem.d.ts +1 -1
  129. package/dist/SegmentedControl/SegmentedControlItem.d.ts.map +1 -1
  130. package/dist/SegmentedControl/SegmentedControlItem.js +16 -3
  131. package/dist/Selector/Selector.d.ts +3 -3
  132. package/dist/Selector/Selector.d.ts.map +1 -1
  133. package/dist/Selector/Selector.js +14 -8
  134. package/dist/Selector/hooks.js +2 -2
  135. package/dist/Selector/useSelectorPresentation.d.ts +7 -1
  136. package/dist/Selector/useSelectorPresentation.d.ts.map +1 -1
  137. package/dist/Selector/useSelectorPresentation.js +2 -1
  138. package/dist/SideNav/railMotion.d.ts +5 -5
  139. package/dist/SideNav/railMotion.d.ts.map +1 -1
  140. package/dist/SideNav/railMotion.js +69 -177
  141. package/dist/Slider/Slider.d.ts.map +1 -1
  142. package/dist/Slider/Slider.js +100 -17
  143. package/dist/Table/BaseTable.d.ts.map +1 -1
  144. package/dist/Table/BaseTable.js +7 -6
  145. package/dist/Table/glidingDivider.d.ts +8 -1
  146. package/dist/Table/glidingDivider.d.ts.map +1 -1
  147. package/dist/Table/glidingDivider.js +8 -3
  148. package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
  149. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.d.ts.map +1 -1
  150. package/dist/Table/plugins/stickyColumns/useTableStickyColumns.js +1 -2
  151. package/dist/TextArea/TextArea.d.ts +2 -2
  152. package/dist/TextArea/TextArea.d.ts.map +1 -1
  153. package/dist/TextInput/TextInput.d.ts +3 -2
  154. package/dist/TextInput/TextInput.d.ts.map +1 -1
  155. package/dist/TimeInput/TimeInput.d.ts +3 -2
  156. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  157. package/dist/Timer/Timer.js +1 -1
  158. package/dist/Toast/useToast.d.ts.map +1 -1
  159. package/dist/Toast/useToast.js +8 -2
  160. package/dist/ToggleButton/ToggleButton.d.ts.map +1 -1
  161. package/dist/ToggleButton/ToggleButton.js +11 -3
  162. package/dist/Token/Token.d.ts +1 -1
  163. package/dist/Token/Token.d.ts.map +1 -1
  164. package/dist/Token/Token.js +15 -0
  165. package/dist/Tokenizer/Tokenizer.d.ts +3 -2
  166. package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
  167. package/dist/Tokenizer/Tokenizer.js +73 -50
  168. package/dist/Tokenizer/TokenizerBottomSheet.d.ts +1 -1
  169. package/dist/Tokenizer/TokenizerBottomSheet.d.ts.map +1 -1
  170. package/dist/Tokenizer/TokenizerBottomSheet.js +9 -15
  171. package/dist/TopNav/TopNavMegaMenu.d.ts.map +1 -1
  172. package/dist/TopNav/TopNavMegaMenu.js +4 -3
  173. package/dist/TreeList/TreeList.d.ts.map +1 -1
  174. package/dist/TreeList/TreeList.js +6 -3
  175. package/dist/TreeList/TreeListItem.d.ts.map +1 -1
  176. package/dist/TreeList/TreeListItem.js +4 -3
  177. package/dist/Typeahead/BaseTypeahead.d.ts +4 -1
  178. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  179. package/dist/Typeahead/BaseTypeahead.js +262 -43
  180. package/dist/Typeahead/Typeahead.d.ts +3 -2
  181. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  182. package/dist/Typeahead/Typeahead.js +36 -10
  183. package/dist/Typeahead/index.d.ts +1 -1
  184. package/dist/Typeahead/index.d.ts.map +1 -1
  185. package/dist/Typeahead/types.d.ts +26 -3
  186. package/dist/Typeahead/types.d.ts.map +1 -1
  187. package/dist/astryx.css +1 -2
  188. package/dist/gestures/haptics.d.ts.map +1 -1
  189. package/dist/gestures/haptics.js +1 -5
  190. package/dist/hooks/menuPressMarker.d.ts +13 -0
  191. package/dist/hooks/menuPressMarker.d.ts.map +1 -0
  192. package/dist/hooks/menuPressMarker.js +15 -0
  193. package/dist/hooks/useClickableContainer.d.ts +24 -4
  194. package/dist/hooks/useClickableContainer.d.ts.map +1 -1
  195. package/dist/hooks/useClickableContainer.js +41 -14
  196. package/dist/hooks/useFocusReturnVisibility.d.ts +8 -1
  197. package/dist/hooks/useFocusReturnVisibility.d.ts.map +1 -1
  198. package/dist/hooks/useFocusReturnVisibility.js +14 -8
  199. package/dist/hooks/useInputStatusIcon.d.ts +2 -2
  200. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  201. package/dist/hooks/useInputStatusIcon.js +1 -2
  202. package/dist/hooks/useListFocus.d.ts +19 -0
  203. package/dist/hooks/useListFocus.d.ts.map +1 -1
  204. package/dist/hooks/useListFocus.js +26 -1
  205. package/dist/hooks/useMenuPress.d.ts +2 -2
  206. package/dist/hooks/useMenuPress.d.ts.map +1 -1
  207. package/dist/hooks/useMenuPress.js +5 -6
  208. package/dist/hooks/useTreeFocus.d.ts.map +1 -1
  209. package/dist/hooks/useTreeFocus.js +13 -5
  210. package/dist/hooks/useTypeahead.d.ts +3 -1
  211. package/dist/hooks/useTypeahead.d.ts.map +1 -1
  212. package/dist/hooks/useTypeahead.js +21 -15
  213. package/dist/i18n/generated-locales/en.generated.d.ts.map +1 -1
  214. package/dist/i18n/generated-locales/en.generated.js +6 -0
  215. package/dist/i18n/generated-locales/pseudo.generated.d.ts.map +1 -1
  216. package/dist/i18n/generated-locales/pseudo.generated.js +6 -0
  217. package/dist/i18n/useListFormatter.d.ts +9 -0
  218. package/dist/i18n/useListFormatter.d.ts.map +1 -0
  219. package/dist/i18n/useListFormatter.js +41 -0
  220. package/dist/motion/animatedLayout.d.ts +130 -5
  221. package/dist/motion/animatedLayout.d.ts.map +1 -1
  222. package/dist/motion/animatedLayout.js +771 -44
  223. package/dist/motion/animator.d.ts.map +1 -1
  224. package/dist/motion/animator.js +38 -2
  225. package/dist/privateAttributes/segmentDisabled.stylex.d.ts +5 -0
  226. package/dist/privateAttributes/segmentDisabled.stylex.d.ts.map +1 -0
  227. package/dist/privateAttributes/segmentDisabled.stylex.js +28 -0
  228. package/dist/theme/Theme.d.ts +10 -0
  229. package/dist/theme/Theme.d.ts.map +1 -1
  230. package/dist/theme/Theme.js +8 -4
  231. package/dist/theme/useTheme.d.ts.map +1 -1
  232. package/dist/theme/useTheme.js +7 -1
  233. package/dist/utils/focusReturn.d.ts +5 -3
  234. package/dist/utils/focusReturn.d.ts.map +1 -1
  235. package/dist/utils/focusReturn.js +6 -4
  236. package/dist/utils/getNodeText.d.ts.map +1 -1
  237. package/dist/utils/getNodeText.js +2 -1
  238. package/dist/utils/interactionModality.d.ts +78 -7
  239. package/dist/utils/interactionModality.d.ts.map +1 -1
  240. package/dist/utils/interactionModality.js +125 -42
  241. package/dist/utils/pressDisabled.d.ts +21 -0
  242. package/dist/utils/pressDisabled.d.ts.map +1 -0
  243. package/dist/utils/pressDisabled.js +32 -0
  244. package/dist/utils/pressFeedback.d.ts.map +1 -1
  245. package/dist/utils/pressFeedback.js +5 -3
  246. package/dist/utils/typeaheadMatch.d.ts +24 -0
  247. package/dist/utils/typeaheadMatch.d.ts.map +1 -0
  248. package/dist/utils/typeaheadMatch.js +112 -0
  249. package/locales/en.json +25 -1
  250. package/locales/pseudo.json +18 -0
  251. package/package.json +19 -5
  252. package/src/AppShell/AppShell.tsx +0 -9
  253. package/src/Badge/Badge.tsx +2 -1
  254. package/src/BottomSheet/BottomSheet.tsx +4 -2
  255. package/src/BottomSheet/BottomSheetPanel.tsx +39 -8
  256. package/src/BottomSheet/BottomSheetSwitcher.tsx +1 -1
  257. package/src/BottomSheet/useMobileKeyboard.ts +51 -10
  258. package/src/BottomSheet/useSheetGestures.ts +92 -15
  259. package/src/Breadcrumbs/BreadcrumbItem.tsx +2 -1
  260. package/src/Center/Center.tsx +1 -1
  261. package/src/Chat/ChatComposer.tsx +7 -10
  262. package/src/Chat/ChatComposerDrawer.tsx +1 -1
  263. package/src/Chat/ChatComposerInput.tsx +1 -1
  264. package/src/Chat/ChatLayout.tsx +1 -6
  265. package/src/Chat/ChatMessageList.tsx +1 -0
  266. package/src/Chat/ChatPastedTextToken.tsx +14 -4
  267. package/src/Chat/ChatSystemMessage.tsx +4 -1
  268. package/src/Chat/ChatToolCalls.tsx +5 -0
  269. package/src/CheckboxList/CheckboxList.tsx +7 -1
  270. package/src/CheckboxList/CheckboxListContext.tsx +8 -0
  271. package/src/CheckboxList/CheckboxListItem.tsx +8 -2
  272. package/src/Code/Code.tsx +1 -1
  273. package/src/CommandPalette/CommandPaletteInput.tsx +5 -8
  274. package/src/ComplexSelector/ComplexSelector.tsx +22 -4
  275. package/src/DateInput/DateInput.tsx +2 -2
  276. package/src/DateInput/MonthScroller.tsx +15 -17
  277. package/src/DateRangeInput/DateRangeInput.tsx +20 -5
  278. package/src/Dialog/Dialog.tsx +9 -25
  279. package/src/Drawer/Drawer.tsx +1 -1
  280. package/src/DropdownMenu/DropdownMenu.tsx +24 -9
  281. package/src/DropdownMenu/DropdownMenuGroup.tsx +2 -1
  282. package/src/DropdownMenu/DropdownMenuSubMenu.tsx +3 -1
  283. package/src/DropdownMenu/MenuBottomSheetActionList.tsx +6 -3
  284. package/src/Field/Field.doc.mjs +2 -1
  285. package/src/Field/Field.tsx +2 -2
  286. package/src/Field/FieldLabel.doc.mjs +4 -2
  287. package/src/Field/FieldLabel.tsx +18 -2
  288. package/src/Field/PanelSearchInput.tsx +7 -8
  289. package/src/Field/fieldStatusPlacement.ts +18 -0
  290. package/src/FieldStatus/FieldStatus.tsx +33 -3
  291. package/src/FieldStatus/index.ts +7 -0
  292. package/src/FileInput/FileInput.doc.mjs +3 -3
  293. package/src/FileInput/FileInput.tsx +103 -36
  294. package/src/HoverCard/HoverCard.doc.mjs +3 -3
  295. package/src/HoverCard/useHoverCard.doc.mjs +1 -1
  296. package/src/Item/Item.tsx +5 -5
  297. package/src/Item/ItemSwipeLayer.tsx +8 -1
  298. package/src/Layer/useTouchTrigger.ts +107 -18
  299. package/src/Link/Link.tsx +9 -2
  300. package/src/Markdown/Markdown.tsx +1 -0
  301. package/src/MetadataList/MetadataList.tsx +4 -2
  302. package/src/MetadataList/MetadataListItem.tsx +2 -1
  303. package/src/MultiSelector/MultiSelector.tsx +57 -36
  304. package/src/MultiSelector/hooks.ts +7 -5
  305. package/src/NumberInput/NumberInput.tsx +13 -20
  306. package/src/OverflowList/OverflowList.tsx +2 -1
  307. package/src/Pagination/Pagination.tsx +1 -0
  308. package/src/Popover/Popover.tsx +2 -2
  309. package/src/Popover/usePopover.tsx +7 -2
  310. package/src/PowerSearch/PowerSearch.tsx +4 -2
  311. package/src/RadioList/RadioList.tsx +13 -2
  312. package/src/RadioList/RadioListItem.tsx +9 -1
  313. package/src/RadioList/RadioListReasonContext.ts +25 -0
  314. package/src/Resizable/useResizable.ts +50 -7
  315. package/src/SegmentedControl/SegmentedControl.tsx +43 -8
  316. package/src/SegmentedControl/SegmentedControlContext.ts +6 -0
  317. package/src/SegmentedControl/SegmentedControlItem.tsx +12 -1
  318. package/src/Selector/Selector.tsx +23 -9
  319. package/src/Selector/hooks.ts +2 -2
  320. package/src/Selector/useSelectorPresentation.ts +8 -1
  321. package/src/SideNav/railMotion.ts +73 -179
  322. package/src/Slider/Slider.tsx +116 -19
  323. package/src/Table/BaseTable.tsx +10 -6
  324. package/src/Table/glidingDivider.ts +8 -3
  325. package/src/Table/plugins/sortable/useTableSortable.tsx +2 -0
  326. package/src/Table/plugins/stickyColumns/useTableStickyColumns.tsx +1 -2
  327. package/src/TextArea/TextArea.tsx +3 -2
  328. package/src/TextInput/TextInput.tsx +2 -2
  329. package/src/TimeInput/TimeInput.tsx +2 -2
  330. package/src/Timer/Timer.tsx +1 -1
  331. package/src/Toast/useToast.tsx +8 -3
  332. package/src/ToggleButton/ToggleButton.tsx +10 -3
  333. package/src/Token/Token.tsx +15 -0
  334. package/src/Tokenizer/Tokenizer.doc.mjs +12 -8
  335. package/src/Tokenizer/Tokenizer.tsx +81 -53
  336. package/src/Tokenizer/TokenizerBottomSheet.tsx +9 -15
  337. package/src/Tooltip/Tooltip.doc.mjs +3 -3
  338. package/src/Tooltip/useTooltip.doc.mjs +1 -1
  339. package/src/TopNav/TopNavMegaMenu.tsx +4 -3
  340. package/src/TreeList/TreeList.tsx +6 -3
  341. package/src/TreeList/TreeListItem.tsx +5 -3
  342. package/src/Typeahead/BaseTypeahead.doc.mjs +7 -4
  343. package/src/Typeahead/BaseTypeahead.tsx +314 -40
  344. package/src/Typeahead/Typeahead.doc.mjs +21 -4
  345. package/src/Typeahead/Typeahead.tsx +42 -11
  346. package/src/Typeahead/TypeaheadItem.doc.mjs +6 -3
  347. package/src/Typeahead/index.ts +1 -1
  348. package/src/Typeahead/types.ts +27 -3
  349. package/src/gestures/haptics.ts +1 -8
  350. package/src/hooks/menuPressMarker.ts +15 -0
  351. package/src/hooks/useClickableContainer.ts +48 -17
  352. package/src/hooks/useFocusReturnVisibility.ts +15 -12
  353. package/src/hooks/useInputStatusIcon.tsx +2 -2
  354. package/src/hooks/useListFocus.ts +38 -1
  355. package/src/hooks/useMenuPress.ts +5 -5
  356. package/src/hooks/useTreeFocus.doc.mjs +2 -2
  357. package/src/hooks/useTreeFocus.ts +16 -10
  358. package/src/hooks/useTypeahead.doc.mjs +2 -2
  359. package/src/hooks/useTypeahead.ts +21 -20
  360. package/src/i18n/generated-locales/en.generated.ts +8 -0
  361. package/src/i18n/generated-locales/pseudo.generated.ts +8 -0
  362. package/src/i18n/useListFormatter.ts +42 -0
  363. package/src/motion/animatedLayout.ts +977 -45
  364. package/src/motion/animator.ts +55 -2
  365. package/src/privateAttributes/segmentDisabled.stylex.ts +28 -0
  366. package/src/theme/Theme.tsx +8 -4
  367. package/src/theme/useTheme.ts +6 -1
  368. package/src/utils/focusReturn.ts +6 -4
  369. package/src/utils/getNodeText.ts +2 -1
  370. package/src/utils/interactionModality.ts +165 -44
  371. package/src/utils/pressDisabled.ts +32 -0
  372. package/src/utils/pressFeedback.ts +5 -3
  373. package/src/utils/typeaheadMatch.ts +141 -0
  374. package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.d.ts +0 -2
  375. package/dist/Item/ItemMarkerColumns.a11y.chromium.spec.js +0 -132
  376. package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.d.ts +0 -2
  377. package/dist/Lightbox/LightboxKeyboard.a11y.browser.spec.js +0 -78
  378. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.d.ts +0 -2
  379. package/dist/Lightbox/LightboxViewport.a11y.browser.spec.js +0 -66
  380. package/dist/List/ListMarkers.a11y.chromium.spec.d.ts +0 -2
  381. package/dist/List/ListMarkers.a11y.chromium.spec.js +0 -677
  382. package/dist/Markdown/Markdown.fr23.bench.d.ts +0 -2
  383. package/dist/Markdown/Markdown.fr23.bench.d.ts.map +0 -1
  384. package/dist/Markdown/Markdown.fr23.bench.js +0 -152
  385. package/dist/Markdown/MarkdownTable.a11y.chromium.spec.d.ts +0 -2
  386. package/dist/Markdown/MarkdownTable.a11y.chromium.spec.js +0 -590
  387. package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.d.ts +0 -2
  388. package/dist/Markdown/plugins/headingLinks.a11y.chromium.spec.js +0 -657
  389. package/dist/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.d.ts +0 -2
  390. package/dist/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.js +0 -153
  391. package/dist/MultiSelector/CreateRow.a11y.chromium.spec.d.ts +0 -2
  392. package/dist/MultiSelector/CreateRow.a11y.chromium.spec.js +0 -117
  393. package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.d.ts +0 -2
  394. package/dist/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.js +0 -49
  395. package/dist/MultiSelector/OptionActions.a11y.chromium.spec.d.ts +0 -2
  396. package/dist/MultiSelector/OptionActions.a11y.chromium.spec.js +0 -273
  397. package/dist/Outline/OutlineFocus.a11y.chromium.spec.d.ts +0 -2
  398. package/dist/Outline/OutlineFocus.a11y.chromium.spec.js +0 -421
  399. package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.d.ts +0 -2
  400. package/dist/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.js +0 -118
  401. package/dist/reset.a11y.chromium.spec.d.ts +0 -2
  402. package/dist/reset.a11y.chromium.spec.js +0 -231
  403. package/dist/reset.until-found.a11y.browser.spec.d.ts +0 -2
  404. package/dist/reset.until-found.a11y.browser.spec.js +0 -72
  405. package/src/AlertDialog/AlertDialog.spec.md +0 -259
  406. package/src/AlertDialog/AlertDialog.test.tsx +0 -519
  407. package/src/AlertDialog/__tests__/AlertDialog.a11y.chromium.spec.ts +0 -128
  408. package/src/AlertDialog/__tests__/AlertDialog.a11y.renders.tsx +0 -52
  409. package/src/AlertDialog/__tests__/AlertDialog.a11y.states.ts +0 -68
  410. package/src/AlertDialog/__tests__/AlertDialog.a11y.test.tsx +0 -144
  411. package/src/AlertDialog/useImperativeAlertDialog.test.tsx +0 -163
  412. package/src/AppShell/AppShell.spec.md +0 -331
  413. package/src/AppShell/AppShell.test.tsx +0 -1310
  414. package/src/AppShell/__tests__/AppShellMainKeyboard.a11y.browser.spec.ts +0 -534
  415. package/src/AppShell/observeMainOverflow.test.ts +0 -173
  416. package/src/AspectRatio/AspectRatio.spec.md +0 -179
  417. package/src/AspectRatio/AspectRatio.test.tsx +0 -398
  418. package/src/AspectRatio/__tests__/AspectRatioFitLayers.a11y.browser.spec.ts +0 -158
  419. package/src/Avatar/Avatar.spec.md +0 -253
  420. package/src/Avatar/Avatar.test.tsx +0 -867
  421. package/src/Avatar/AvatarStatusDot.spec.md +0 -208
  422. package/src/Avatar/AvatarStatusDot.test.tsx +0 -306
  423. package/src/Avatar/__tests__/AvatarForcedColors.a11y.chromium.spec.ts +0 -126
  424. package/src/AvatarGroup/AvatarGroup.spec.md +0 -205
  425. package/src/AvatarGroup/AvatarGroup.test.tsx +0 -313
  426. package/src/AvatarGroup/AvatarGroupOverflow.spec.md +0 -210
  427. package/src/AvatarGroup/AvatarGroupOverflow.test.tsx +0 -263
  428. package/src/Badge/Badge.spec.md +0 -207
  429. package/src/Badge/Badge.test.tsx +0 -125
  430. package/src/Badge/__tests__/BadgeLargerText.a11y.chromium.spec.ts +0 -260
  431. package/src/Banner/Banner.spec.md +0 -282
  432. package/src/Banner/Banner.test.tsx +0 -879
  433. package/src/Blockquote/Blockquote.spec.md +0 -171
  434. package/src/Blockquote/Blockquote.test.tsx +0 -121
  435. package/src/BottomSheet/BottomSheet.spec.md +0 -457
  436. package/src/BottomSheet/BottomSheet.test.tsx +0 -3154
  437. package/src/BottomSheet/BottomSheetEdgeTint.test.tsx +0 -248
  438. package/src/BottomSheet/BottomSheetKeyboard.test.tsx +0 -195
  439. package/src/BottomSheet/BottomSheetPanel.test.tsx +0 -549
  440. package/src/BottomSheet/BottomSheetReplace.test.tsx +0 -1638
  441. package/src/BottomSheet/BottomSheetStacking.announce.test.tsx +0 -246
  442. package/src/BottomSheet/BottomSheetStacking.test.tsx +0 -3184
  443. package/src/BottomSheet/BottomSheetSwitcher.spec.md +0 -367
  444. package/src/BottomSheet/BottomSheetSwitcher.test.tsx +0 -2010
  445. package/src/BottomSheet/__tests__/BottomSheetDrag.a11y.chromium.spec.ts +0 -129
  446. package/src/BottomSheet/__tests__/BottomSheetFocusReturn.a11y.browser.spec.ts +0 -97
  447. package/src/BottomSheet/__tests__/BottomSheetKeyboard.a11y.browser.spec.ts +0 -415
  448. package/src/BottomSheet/__tests__/BottomSheetNativeModal.a11y.browser.spec.ts +0 -462
  449. package/src/BottomSheet/__tests__/BottomSheetPinchZoom.a11y.chromium.spec.ts +0 -247
  450. package/src/BottomSheet/__tests__/BottomSheetToastOutlet.a11y.chromium.spec.ts +0 -83
  451. package/src/BottomSheet/snapOffsets.test.ts +0 -254
  452. package/src/BottomSheet/useSheetGestures.test.ts +0 -1690
  453. package/src/Breadcrumbs/BreadcrumbItem.spec.md +0 -269
  454. package/src/Breadcrumbs/Breadcrumbs.spec.md +0 -165
  455. package/src/Breadcrumbs/Breadcrumbs.test.tsx +0 -921
  456. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.chromium.spec.ts +0 -132
  457. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.renders.tsx +0 -45
  458. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.states.ts +0 -81
  459. package/src/Breadcrumbs/__tests__/Breadcrumbs.a11y.test.tsx +0 -125
  460. package/src/Button/Button.spec.md +0 -322
  461. package/src/Button/Button.test.tsx +0 -864
  462. package/src/Button/__tests__/Button.a11y.chromium.spec.ts +0 -355
  463. package/src/Button/__tests__/Button.a11y.harnesses.tsx +0 -47
  464. package/src/Button/__tests__/Button.a11y.known-failures.ts +0 -25
  465. package/src/Button/__tests__/Button.a11y.renders.tsx +0 -139
  466. package/src/Button/__tests__/Button.a11y.states.ts +0 -317
  467. package/src/Button/__tests__/Button.a11y.test.tsx +0 -187
  468. package/src/ButtonGroup/ButtonGroup.spec.md +0 -220
  469. package/src/ButtonGroup/ButtonGroup.test.tsx +0 -1025
  470. package/src/Calendar/Calendar.spec.md +0 -248
  471. package/src/Calendar/Calendar.test.tsx +0 -2186
  472. package/src/Calendar/__tests__/Calendar.a11y.chromium.spec.ts +0 -141
  473. package/src/Calendar/__tests__/Calendar.a11y.known-failures.ts +0 -12
  474. package/src/Calendar/__tests__/Calendar.a11y.renders.tsx +0 -57
  475. package/src/Calendar/__tests__/Calendar.a11y.states.ts +0 -139
  476. package/src/Calendar/__tests__/Calendar.a11y.test.tsx +0 -137
  477. package/src/Calendar/dayCellUtils.test.ts +0 -520
  478. package/src/Calendar/getInitialFocusDate.test.ts +0 -94
  479. package/src/Calendar/getStandaloneShortWeekdayNames.test.ts +0 -54
  480. package/src/Calendar/hooks/useCalendarConstraints.test.ts +0 -153
  481. package/src/Calendar/hooks/useCalendarDays.test.ts +0 -265
  482. package/src/Calendar/hooks/useCalendarNavigation.test.ts +0 -252
  483. package/src/Card/Card.spec.md +0 -205
  484. package/src/Card/Card.test.tsx +0 -202
  485. package/src/Carousel/Carousel.spec.md +0 -146
  486. package/src/Carousel/Carousel.test.tsx +0 -1036
  487. package/src/Carousel/__tests__/Carousel.a11y.chromium.spec.ts +0 -207
  488. package/src/Carousel/__tests__/Carousel.a11y.known-failures.ts +0 -12
  489. package/src/Carousel/__tests__/Carousel.a11y.renders.tsx +0 -39
  490. package/src/Carousel/__tests__/Carousel.a11y.states.ts +0 -139
  491. package/src/Carousel/__tests__/Carousel.a11y.test.tsx +0 -127
  492. package/src/Center/Center.spec.md +0 -205
  493. package/src/Center/Center.test.tsx +0 -406
  494. package/src/Chat/ChatComposer.spec.md +0 -246
  495. package/src/Chat/ChatComposer.test.tsx +0 -364
  496. package/src/Chat/ChatComposerDrawer.spec.md +0 -267
  497. package/src/Chat/ChatComposerDrawer.test.tsx +0 -130
  498. package/src/Chat/ChatComposerInput.spec.md +0 -224
  499. package/src/Chat/ChatComposerInput.test.tsx +0 -2136
  500. package/src/Chat/ChatComposerTokenElement.spec.md +0 -164
  501. package/src/Chat/ChatComposerTokenElement.test.tsx +0 -85
  502. package/src/Chat/ChatDictationButton.spec.md +0 -205
  503. package/src/Chat/ChatDictationButton.test.tsx +0 -108
  504. package/src/Chat/ChatLayout.spec.md +0 -276
  505. package/src/Chat/ChatLayout.test.tsx +0 -311
  506. package/src/Chat/ChatLayoutScrollButton.spec.md +0 -322
  507. package/src/Chat/ChatLayoutScrollButton.test.tsx +0 -99
  508. package/src/Chat/ChatMessage.spec.md +0 -265
  509. package/src/Chat/ChatMessage.test.tsx +0 -344
  510. package/src/Chat/ChatMessageBubble.spec.md +0 -263
  511. package/src/Chat/ChatMessageBubble.test.tsx +0 -294
  512. package/src/Chat/ChatMessageList.spec.md +0 -222
  513. package/src/Chat/ChatMessageList.test.tsx +0 -198
  514. package/src/Chat/ChatMessageMetadata.spec.md +0 -220
  515. package/src/Chat/ChatMessageMetadata.test.tsx +0 -66
  516. package/src/Chat/ChatPastedTextToken.test.tsx +0 -71
  517. package/src/Chat/ChatSendButton.spec.md +0 -238
  518. package/src/Chat/ChatSendButton.test.tsx +0 -309
  519. package/src/Chat/ChatSystemMessage.spec.md +0 -224
  520. package/src/Chat/ChatSystemMessage.test.tsx +0 -189
  521. package/src/Chat/ChatTokenizedText.spec.md +0 -190
  522. package/src/Chat/ChatTokenizedText.test.tsx +0 -163
  523. package/src/Chat/ChatToolCalls.spec.md +0 -194
  524. package/src/Chat/ChatToolCalls.test.tsx +0 -413
  525. package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +0 -577
  526. package/src/Chat/__tests__/ChatLayoutScrollButtonSurface.a11y.chromium.spec.ts +0 -740
  527. package/src/Chat/__tests__/ChatMessageBubble.a11y.chromium.spec.ts +0 -457
  528. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.test.ts +0 -91
  529. package/src/Chat/__tests__/ChatMessageBubble.auditProvenance.ts +0 -91
  530. package/src/Chat/__tests__/ChatMessageList.a11y.chromium.spec.ts +0 -465
  531. package/src/Chat/__tests__/ChatMessageMetadata.a11y.chromium.spec.ts +0 -416
  532. package/src/Chat/__tests__/ChatSendButton.a11y.chromium.spec.ts +0 -1523
  533. package/src/Chat/__tests__/ChatSystemMessage.a11y.chromium.spec.ts +0 -1004
  534. package/src/Chat/__tests__/ChatTokenizedText.a11y.chromium.spec.ts +0 -574
  535. package/src/Chat/__tests__/useChatStreamScroll.a11y.chromium.spec.ts +0 -263
  536. package/src/Chat/__tests__/useChatStreamScroll.fixture.tsx +0 -61
  537. package/src/Chat/useChatDictation.test.tsx +0 -369
  538. package/src/Chat/useChatNewMessages.test.tsx +0 -209
  539. package/src/Chat/useChatPasteAsToken.test.ts +0 -191
  540. package/src/Chat/useChatStreamScroll.test.tsx +0 -381
  541. package/src/Chat/useTriggerMenu.test.tsx +0 -994
  542. package/src/CheckboxInput/CheckboxInput.spec.md +0 -218
  543. package/src/CheckboxInput/CheckboxInput.test.tsx +0 -926
  544. package/src/CheckboxInput/__tests__/Checkbox.a11y.chromium.spec.ts +0 -354
  545. package/src/CheckboxInput/__tests__/Checkbox.a11y.known-failures.ts +0 -54
  546. package/src/CheckboxInput/__tests__/Checkbox.a11y.renders.tsx +0 -310
  547. package/src/CheckboxInput/__tests__/Checkbox.a11y.states.ts +0 -404
  548. package/src/CheckboxInput/__tests__/Checkbox.a11y.test.tsx +0 -128
  549. package/src/CheckboxList/CheckboxList.fr10.test.tsx +0 -51
  550. package/src/CheckboxList/CheckboxList.spec.md +0 -332
  551. package/src/CheckboxList/CheckboxList.test.tsx +0 -1159
  552. package/src/CheckboxList/CheckboxListItem.spec.md +0 -302
  553. package/src/CheckboxList/__tests__/CheckboxList.a11y.chromium.spec.ts +0 -1998
  554. package/src/Citation/Citation.spec.md +0 -179
  555. package/src/Citation/Citation.test.tsx +0 -361
  556. package/src/ClickableCard/ClickableCard.spec.md +0 -241
  557. package/src/ClickableCard/ClickableCard.test.tsx +0 -382
  558. package/src/Code/Code.spec.md +0 -170
  559. package/src/Code/Code.test.tsx +0 -75
  560. package/src/CodeBlock/CodeBlock.highlightStyles.test.tsx +0 -87
  561. package/src/CodeBlock/CodeBlock.spec.md +0 -243
  562. package/src/CodeBlock/CodeBlock.test.tsx +0 -752
  563. package/src/CodeBlock/CodeLanguage.test.tsx +0 -173
  564. package/src/CodeBlock/__tests__/CodeBlockCopyClearance.a11y.chromium.spec.ts +0 -325
  565. package/src/CodeBlock/__tests__/CodeBlockLanguages.a11y.chromium.spec.ts +0 -64
  566. package/src/CodeBlock/__tests__/CodeBlockOverflowStop.a11y.chromium.spec.ts +0 -126
  567. package/src/CodeBlock/__tests__/DiffRowPaint.a11y.chromium.spec.ts +0 -94
  568. package/src/CodeBlock/highlightRanges.test.ts +0 -253
  569. package/src/CodeBlock/languages.test.ts +0 -402
  570. package/src/CodeBlock/tokenizer.test.ts +0 -280
  571. package/src/CodeBlock/tokenizerBudget.test.ts +0 -112
  572. package/src/Collapsible/Collapsible.spec.md +0 -247
  573. package/src/Collapsible/Collapsible.test.tsx +0 -620
  574. package/src/Collapsible/CollapsibleGroup.spec.md +0 -229
  575. package/src/Collapsible/CollapsibleGroup.test.tsx +0 -656
  576. package/src/Collapsible/__tests__/Collapsible.a11y.chromium.spec.ts +0 -194
  577. package/src/Collapsible/__tests__/Collapsible.a11y.states.ts +0 -117
  578. package/src/Collapsible/__tests__/Collapsible.a11y.test.tsx +0 -140
  579. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.chromium.spec.ts +0 -559
  580. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.renders.tsx +0 -45
  581. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.states.ts +0 -139
  582. package/src/Collapsible/__tests__/CollapsibleGroup.a11y.test.tsx +0 -135
  583. package/src/Collapsible/__tests__/CollapsibleGroupAccordion.a11y.chromium.spec.ts +0 -141
  584. package/src/Collapsible/useCollapsible.test.tsx +0 -236
  585. package/src/CommandPalette/CommandPalette.perf.test.ts +0 -100
  586. package/src/CommandPalette/CommandPalette.spec.md +0 -278
  587. package/src/CommandPalette/CommandPalette.test.tsx +0 -750
  588. package/src/CommandPalette/CommandPaletteEmpty.spec.md +0 -190
  589. package/src/CommandPalette/CommandPaletteEmpty.test.tsx +0 -101
  590. package/src/CommandPalette/CommandPaletteFooter.test.tsx +0 -122
  591. package/src/CommandPalette/CommandPaletteGroup.test.tsx +0 -129
  592. package/src/CommandPalette/CommandPaletteInput.spec.md +0 -215
  593. package/src/CommandPalette/CommandPaletteInput.test.tsx +0 -539
  594. package/src/CommandPalette/CommandPaletteItem.spec.md +0 -223
  595. package/src/CommandPalette/CommandPaletteItem.test.tsx +0 -417
  596. package/src/CommandPalette/CommandPaletteList.spec.md +0 -194
  597. package/src/CommandPalette/CommandPaletteList.test.tsx +0 -184
  598. package/src/CommandPalette/__tests__/CommandPalette.a11y.chromium.spec.ts +0 -600
  599. package/src/CommandPalette/__tests__/CommandPaletteEmptyAudit.a11y.chromium.spec.ts +0 -715
  600. package/src/CommandPalette/__tests__/CommandPaletteFooter.a11y.chromium.spec.ts +0 -582
  601. package/src/CommandPalette/__tests__/CommandPaletteInputAudit.a11y.chromium.spec.ts +0 -336
  602. package/src/CommandPalette/__tests__/CommandPaletteItemAudit.a11y.chromium.spec.ts +0 -523
  603. package/src/ComplexSelector/ComplexSelector.focusableDisabled.test.tsx +0 -76
  604. package/src/ComplexSelector/ComplexSelector.fr10.test.tsx +0 -262
  605. package/src/ComplexSelector/ComplexSelector.spec.md +0 -266
  606. package/src/ComplexSelector/ComplexSelector.test.tsx +0 -1171
  607. package/src/ComplexSelector/__tests__/ComplexSelector.a11y.chromium.spec.ts +0 -965
  608. package/src/ContextMenu/ContextMenu.spec.md +0 -279
  609. package/src/ContextMenu/ContextMenu.test.tsx +0 -1617
  610. package/src/ContextMenu/ContextMenu.triggerProps.test.tsx +0 -54
  611. package/src/ContextMenu/ContextMenuItem.spec.md +0 -132
  612. package/src/ContextMenu/__tests__/ContextMenuAudit.a11y.chromium.spec.ts +0 -439
  613. package/src/ContextMenu/__tests__/ContextMenuCursorAnchor.a11y.browser.spec.ts +0 -154
  614. package/src/ContextMenu/__tests__/ContextMenuLongPress.a11y.chromium.spec.ts +0 -117
  615. package/src/DateInput/DateInput.fr10.test.tsx +0 -283
  616. package/src/DateInput/DateInput.spec.md +0 -319
  617. package/src/DateInput/DateInput.test.tsx +0 -1571
  618. package/src/DateInput/DateInputTouch.test.tsx +0 -2948
  619. package/src/DateInput/MonthScrollerSettle.test.tsx +0 -165
  620. package/src/DateInput/NativeDateField.test.tsx +0 -741
  621. package/src/DateInput/Presentation.test.tsx +0 -121
  622. package/src/DateInput/__tests__/DateInputAudit.a11y.chromium.spec.ts +0 -577
  623. package/src/DateInput/__tests__/MonthScrollerSteps.interaction.browser.spec.ts +0 -312
  624. package/src/DateInput/__tests__/WheelPinchZoom.a11y.chromium.spec.ts +0 -171
  625. package/src/DateRangeInput/DateRangeInput.fr10.test.tsx +0 -165
  626. package/src/DateRangeInput/DateRangeInput.spec.md +0 -203
  627. package/src/DateRangeInput/DateRangeInput.test.tsx +0 -1151
  628. package/src/DateTimeInput/DateTimeInput.fr10.test.tsx +0 -319
  629. package/src/DateTimeInput/DateTimeInput.spec.md +0 -270
  630. package/src/DateTimeInput/DateTimeInput.test.tsx +0 -2656
  631. package/src/DateTimeInput/DateTimeInputTouch.test.tsx +0 -1071
  632. package/src/DateTimeInput/NativePickerSegments.test.tsx +0 -657
  633. package/src/DateTimeInput/Presentation.test.tsx +0 -89
  634. package/src/Dialog/Dialog.source-build.test.mjs +0 -43
  635. package/src/Dialog/Dialog.spec.md +0 -189
  636. package/src/Dialog/Dialog.test.tsx +0 -1599
  637. package/src/Dialog/DialogHeader.test.tsx +0 -477
  638. package/src/Dialog/__tests__/Dialog.a11y.browser.spec.ts +0 -111
  639. package/src/Dialog/__tests__/Dialog.a11y.chromium.spec.ts +0 -218
  640. package/src/Dialog/__tests__/Dialog.a11y.known-failures.ts +0 -28
  641. package/src/Dialog/__tests__/Dialog.a11y.renders.tsx +0 -107
  642. package/src/Dialog/__tests__/Dialog.a11y.states.ts +0 -104
  643. package/src/Dialog/__tests__/Dialog.a11y.test.tsx +0 -102
  644. package/src/Dialog/__tests__/Dialog.reduced-motion.a11y.browser.spec.ts +0 -252
  645. package/src/Dialog/__tests__/DialogSecondDocument.a11y.browser.spec.ts +0 -202
  646. package/src/Dialog/modules/DialogHeader.spec.md +0 -211
  647. package/src/Dialog/useImperativeDialog.test.tsx +0 -78
  648. package/src/Divider/Divider.spec.md +0 -252
  649. package/src/Divider/Divider.test.tsx +0 -155
  650. package/src/Drawer/Drawer.spec.md +0 -388
  651. package/src/Drawer/Drawer.test.tsx +0 -1335
  652. package/src/Drawer/DrawerDirection.test.tsx +0 -127
  653. package/src/Drawer/DrawerHeader.spec.md +0 -181
  654. package/src/Drawer/DrawerHeader.test.tsx +0 -102
  655. package/src/Drawer/__tests__/DrawerAudit.a11y.chromium.spec.ts +0 -402
  656. package/src/Drawer/__tests__/DrawerHeaderAudit.a11y.chromium.spec.ts +0 -298
  657. package/src/Drawer/__tests__/DrawerNonModalPageScroll.a11y.chromium.spec.ts +0 -267
  658. package/src/DropdownMenu/DropdownMenu.spec.md +0 -562
  659. package/src/DropdownMenu/DropdownMenu.ssr.test.tsx +0 -93
  660. package/src/DropdownMenu/DropdownMenu.test.tsx +0 -4498
  661. package/src/DropdownMenu/DropdownMenuCheckboxItem.spec.md +0 -192
  662. package/src/DropdownMenu/DropdownMenuContext.public.test.ts +0 -22
  663. package/src/DropdownMenu/DropdownMenuDivider.spec.md +0 -186
  664. package/src/DropdownMenu/DropdownMenuGroup.spec.md +0 -145
  665. package/src/DropdownMenu/DropdownMenuItem.spec.md +0 -215
  666. package/src/DropdownMenu/DropdownMenuLazySheet.test.tsx +0 -126
  667. package/src/DropdownMenu/DropdownMenuRadioGroup.spec.md +0 -187
  668. package/src/DropdownMenu/DropdownMenuRadioItem.spec.md +0 -227
  669. package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +0 -635
  670. package/src/DropdownMenu/DropdownMenuSheetFirstOpen.test.tsx +0 -72
  671. package/src/DropdownMenu/DropdownMenuSubMenu.spec.md +0 -231
  672. package/src/DropdownMenu/DropdownMenuSubMenu.test.tsx +0 -2442
  673. package/src/DropdownMenu/__tests__/DropdownMenuDividerAudit.a11y.chromium.spec.ts +0 -281
  674. package/src/DropdownMenu/__tests__/DropdownMenuDrillIn.a11y.chromium.spec.ts +0 -175
  675. package/src/DropdownMenu/__tests__/DropdownMenuFocusReturn.a11y.browser.spec.ts +0 -74
  676. package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +0 -335
  677. package/src/DropdownMenu/__tests__/DropdownMenuItemAudit.a11y.chromium.spec.ts +0 -367
  678. package/src/DropdownMenu/__tests__/DropdownMenuRadioGroupAudit.a11y.chromium.spec.ts +0 -328
  679. package/src/DropdownMenu/__tests__/DropdownMenuRadioItemAudit.a11y.chromium.spec.ts +0 -540
  680. package/src/DropdownMenu/__tests__/DropdownMenuSubMenuAudit.a11y.chromium.spec.ts +0 -671
  681. package/src/DropdownMenu/__tests__/Menu.a11y.chromium.spec.ts +0 -158
  682. package/src/DropdownMenu/__tests__/Menu.a11y.known-failures.ts +0 -26
  683. package/src/DropdownMenu/__tests__/Menu.a11y.renders.tsx +0 -65
  684. package/src/DropdownMenu/__tests__/Menu.a11y.states.ts +0 -138
  685. package/src/DropdownMenu/__tests__/Menu.a11y.test.tsx +0 -136
  686. package/src/DropdownMenu/__tests__/MenuButton.a11y.chromium.spec.ts +0 -149
  687. package/src/DropdownMenu/__tests__/MenuButton.a11y.known-failures.ts +0 -12
  688. package/src/DropdownMenu/__tests__/MenuButton.a11y.renders.tsx +0 -67
  689. package/src/DropdownMenu/__tests__/MenuButton.a11y.states.ts +0 -141
  690. package/src/DropdownMenu/__tests__/MenuButton.a11y.test.tsx +0 -138
  691. package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +0 -231
  692. package/src/DropdownMenu/__tests__/MenuHoverFocus.a11y.chromium.spec.ts +0 -238
  693. package/src/DropdownMenu/__tests__/MenuPinchZoom.a11y.chromium.spec.ts +0 -173
  694. package/src/DropdownMenu/__tests__/MenuPress.a11y.chromium.spec.ts +0 -503
  695. package/src/DropdownMenu/__tests__/SubMenuHeaderFooter.a11y.chromium.spec.ts +0 -268
  696. package/src/DropdownMenu/__tests__/warmMenuBottomSheet.tsx +0 -43
  697. package/src/DropdownMenu/lazyMenuBottomSheet.test.ts +0 -60
  698. package/src/DropdownMenu/modules/useMenuHover.spec.md +0 -283
  699. package/src/DropdownMenu/modules/useMenuPress.spec.md +0 -348
  700. package/src/EmptyState/EmptyState.announce.test.tsx +0 -227
  701. package/src/EmptyState/EmptyState.spec.md +0 -203
  702. package/src/EmptyState/EmptyState.test.tsx +0 -284
  703. package/src/Field/Field.spec.md +0 -226
  704. package/src/Field/Field.test.tsx +0 -800
  705. package/src/Field/FieldLabel.spec.md +0 -183
  706. package/src/Field/FieldLabel.test.tsx +0 -244
  707. package/src/Field/InputClearButton.test.tsx +0 -395
  708. package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +0 -297
  709. package/src/Field/fieldInputFamily.test.tsx +0 -160
  710. package/src/FieldStatus/FieldStatus.spec.md +0 -224
  711. package/src/FieldStatus/FieldStatus.test.tsx +0 -482
  712. package/src/FieldStatus/__tests__/StatusMessage.a11y.chromium.spec.ts +0 -206
  713. package/src/FieldStatus/__tests__/StatusMessage.a11y.known-failures.ts +0 -13
  714. package/src/FieldStatus/__tests__/StatusMessage.a11y.renders.tsx +0 -287
  715. package/src/FieldStatus/__tests__/StatusMessage.a11y.states.ts +0 -302
  716. package/src/FieldStatus/__tests__/StatusMessage.a11y.test.tsx +0 -154
  717. package/src/FileInput/FileInput.fr10.test.tsx +0 -21
  718. package/src/FileInput/FileInput.spec.md +0 -199
  719. package/src/FileInput/FileInput.test.tsx +0 -1291
  720. package/src/FormLayout/FormLayout.spec.md +0 -209
  721. package/src/FormLayout/FormLayout.test.tsx +0 -444
  722. package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +0 -46
  723. package/src/Grid/Grid.spec.md +0 -193
  724. package/src/Grid/Grid.test.tsx +0 -509
  725. package/src/Grid/GridSpan.spec.md +0 -158
  726. package/src/HStack/HStack.test.tsx +0 -129
  727. package/src/Heading/Heading.spec.md +0 -198
  728. package/src/Heading/Heading.test.tsx +0 -328
  729. package/src/HoverCard/HoverCard.spec.md +0 -244
  730. package/src/HoverCard/HoverCard.test.tsx +0 -1339
  731. package/src/Icon/Icon.spec.md +0 -244
  732. package/src/Icon/Icon.test.tsx +0 -990
  733. package/src/Icon/globalIconRegistry.test.tsx +0 -613
  734. package/src/IconButton/IconButton.spec.md +0 -197
  735. package/src/IconButton/IconButton.test.tsx +0 -156
  736. package/src/Indicator/CheckIndicator.spec.md +0 -164
  737. package/src/Indicator/CheckboxIndicator.spec.md +0 -164
  738. package/src/Indicator/Indicator.spec.md +0 -188
  739. package/src/Indicator/Indicator.test.tsx +0 -389
  740. package/src/Indicator/indicatorRegistry.test.tsx +0 -106
  741. package/src/InputGroup/InputGroup.spec.md +0 -199
  742. package/src/InputGroup/InputGroup.test.tsx +0 -502
  743. package/src/InputGroup/InputGroupText.spec.md +0 -139
  744. package/src/InteractiveRoleContext/InteractiveRoleContext.test.tsx +0 -185
  745. package/src/Item/Item.source-build.test.mjs +0 -88
  746. package/src/Item/Item.spec.md +0 -226
  747. package/src/Item/Item.swipeLayer.test.tsx +0 -74
  748. package/src/Item/Item.test.tsx +0 -1515
  749. package/src/Item/ItemMarkerColumns.a11y.chromium.spec.ts +0 -211
  750. package/src/Item/__tests__/ItemSwipeActions.a11y.chromium.spec.ts +0 -337
  751. package/src/Item/__tests__/ItemSwipePinchZoom.a11y.chromium.spec.ts +0 -186
  752. package/src/Kbd/Kbd.spec.md +0 -185
  753. package/src/Kbd/Kbd.test.tsx +0 -299
  754. package/src/Layer/LayerProvider.spec.md +0 -149
  755. package/src/Layer/LayerProvider.test.tsx +0 -143
  756. package/src/Layer/__tests__/LayerBrowserHide.a11y.chromium.spec.ts +0 -126
  757. package/src/Layer/__tests__/LayerReducedMotion.a11y.chromium.spec.ts +0 -176
  758. package/src/Layer/__tests__/TouchTrigger.a11y.chromium.spec.ts +0 -200
  759. package/src/Layer/anchorName.test.ts +0 -146
  760. package/src/Layer/layerDismissalFamilies.test.tsx +0 -487
  761. package/src/Layer/layerDismissalInvariants.test.tsx +0 -501
  762. package/src/Layer/layerHost.test.ts +0 -117
  763. package/src/Layer/layerScopedContext.test.tsx +0 -499
  764. package/src/Layer/layerViewportInset.source-build.test.mjs +0 -90
  765. package/src/Layer/layerViewportInset.test.ts +0 -120
  766. package/src/Layer/popoverOperations.test.ts +0 -205
  767. package/src/Layer/useLayer.test.tsx +0 -2845
  768. package/src/Layer/useLayerDismissal.test.tsx +0 -580
  769. package/src/Layer/useTouchTrigger.test.ts +0 -70
  770. package/src/Layout/Layout.spec.md +0 -243
  771. package/src/Layout/Layout.test.tsx +0 -375
  772. package/src/Layout/LayoutContent.spec.md +0 -205
  773. package/src/Layout/LayoutFooter.spec.md +0 -190
  774. package/src/Layout/LayoutHeader.spec.md +0 -193
  775. package/src/Layout/LayoutPanel.spec.md +0 -194
  776. package/src/Layout/LayoutSlots.test.tsx +0 -376
  777. package/src/Layout/__tests__/Layout.a11y.chromium.spec.ts +0 -134
  778. package/src/Layout/__tests__/Layout.a11y.renders.tsx +0 -110
  779. package/src/Layout/__tests__/Layout.a11y.states.ts +0 -93
  780. package/src/Layout/__tests__/Layout.a11y.test.tsx +0 -142
  781. package/src/Layout/__tests__/childrenAsContent.test.tsx +0 -59
  782. package/src/Layout/__tests__/contentWidth.test.tsx +0 -303
  783. package/src/Layout/__tests__/edgeCompensation.test.tsx +0 -191
  784. package/src/Layout/__tests__/nestedPadding.test.tsx +0 -264
  785. package/src/Layout/overlayPaddingReset.test.tsx +0 -224
  786. package/src/Lightbox/Lightbox.spec.md +0 -199
  787. package/src/Lightbox/Lightbox.test.tsx +0 -837
  788. package/src/Lightbox/LightboxKeyboard.a11y.browser.spec.ts +0 -78
  789. package/src/Lightbox/LightboxViewport.a11y.browser.spec.ts +0 -74
  790. package/src/Lightbox/useLightbox.test.tsx +0 -263
  791. package/src/Link/Link.spec.md +0 -230
  792. package/src/Link/Link.test.tsx +0 -457
  793. package/src/Link/LinkProvider.spec.md +0 -167
  794. package/src/Link/__tests__/Link.a11y.chromium.spec.ts +0 -153
  795. package/src/Link/__tests__/Link.a11y.renders.tsx +0 -97
  796. package/src/Link/__tests__/Link.a11y.states.ts +0 -104
  797. package/src/Link/__tests__/Link.a11y.test.tsx +0 -127
  798. package/src/Link/__tests__/Link.navigation.a11y.chromium.spec.ts +0 -262
  799. package/src/Link/__tests__/Link.truncation.a11y.chromium.spec.ts +0 -58
  800. package/src/Link/useLinkComponent.test.tsx +0 -572
  801. package/src/Link/useLinkify.test.tsx +0 -146
  802. package/src/List/List.spec.md +0 -336
  803. package/src/List/List.test.tsx +0 -1052
  804. package/src/List/ListItem.source-build.test.mjs +0 -41
  805. package/src/List/ListItem.spec.md +0 -205
  806. package/src/List/ListMarkers.a11y.chromium.spec.ts +0 -904
  807. package/src/Markdown/Markdown.codeLanguages.test.tsx +0 -137
  808. package/src/Markdown/Markdown.fr23.bench.ts +0 -176
  809. package/src/Markdown/Markdown.fr23.perf.test.ts +0 -484
  810. package/src/Markdown/Markdown.helpers.perf.test.ts +0 -495
  811. package/src/Markdown/Markdown.public.test.ts +0 -309
  812. package/src/Markdown/Markdown.renderBoundary.test.tsx +0 -72
  813. package/src/Markdown/Markdown.rerender.test.tsx +0 -143
  814. package/src/Markdown/Markdown.spec.md +0 -595
  815. package/src/Markdown/Markdown.test.tsx +0 -1383
  816. package/src/Markdown/MarkdownTable.a11y.chromium.spec.ts +0 -727
  817. package/src/Markdown/__tests__/MarkdownNesting.a11y.chromium.spec.ts +0 -102
  818. package/src/Markdown/angleDestinations.test.tsx +0 -104
  819. package/src/Markdown/ast.test.ts +0 -374
  820. package/src/Markdown/atxHeadingIndentation.test.tsx +0 -53
  821. package/src/Markdown/blockNesting.test.tsx +0 -124
  822. package/src/Markdown/blockquoteMarkers.test.tsx +0 -69
  823. package/src/Markdown/characterReferences.test.ts +0 -47
  824. package/src/Markdown/codeSpans.test.tsx +0 -65
  825. package/src/Markdown/emphasis.test.tsx +0 -214
  826. package/src/Markdown/fenceInfo.test.tsx +0 -47
  827. package/src/Markdown/imageAlt.test.tsx +0 -84
  828. package/src/Markdown/incremental.test.ts +0 -1198
  829. package/src/Markdown/incrementalIndentation.test.tsx +0 -36
  830. package/src/Markdown/indentedFences.test.tsx +0 -374
  831. package/src/Markdown/indentedTables.test.tsx +0 -53
  832. package/src/Markdown/linkBrackets.test.tsx +0 -116
  833. package/src/Markdown/linkDestinationLinear.test.tsx +0 -54
  834. package/src/Markdown/linkDestinations.test.tsx +0 -127
  835. package/src/Markdown/linkTextCode.test.tsx +0 -65
  836. package/src/Markdown/listBullets.test.tsx +0 -55
  837. package/src/Markdown/listContinuation.test.tsx +0 -94
  838. package/src/Markdown/listThematicBreak.test.tsx +0 -49
  839. package/src/Markdown/longLinkLabels.test.tsx +0 -49
  840. package/src/Markdown/modules/headingLinks.spec.md +0 -262
  841. package/src/Markdown/modules/remark.spec.md +0 -269
  842. package/src/Markdown/modules/softBreaks.spec.md +0 -137
  843. package/src/Markdown/modules/sourceLines.spec.md +0 -162
  844. package/src/Markdown/openFenceFinalNewline.test.tsx +0 -85
  845. package/src/Markdown/parser.perf.test.ts +0 -398
  846. package/src/Markdown/parser.public.test.ts +0 -144
  847. package/src/Markdown/parser.test.ts +0 -1925
  848. package/src/Markdown/plugin-renderer/MarkdownPluginNodeRenderer.test.tsx +0 -300
  849. package/src/Markdown/plugins/frontmatter.test.tsx +0 -150
  850. package/src/Markdown/plugins/headingLinks.a11y.chromium.spec.ts +0 -856
  851. package/src/Markdown/plugins/headingLinks.ssr.test.tsx +0 -143
  852. package/src/Markdown/plugins/headingLinks.test.tsx +0 -637
  853. package/src/Markdown/plugins/protocol.test.tsx +0 -1083
  854. package/src/Markdown/plugins/semanticFence.test.tsx +0 -655
  855. package/src/Markdown/plugins/softBreaks.test.tsx +0 -172
  856. package/src/Markdown/plugins/sourceDecoration.test.tsx +0 -799
  857. package/src/Markdown/plugins/sourceLines.test.tsx +0 -715
  858. package/src/Markdown/plugins/textTransform.test.ts +0 -467
  859. package/src/Markdown/remark.test.tsx +0 -1877
  860. package/src/Markdown/streaming.test.ts +0 -148
  861. package/src/Markdown/streamingListShapes.test.tsx +0 -93
  862. package/src/Markdown/trailingSpaces.test.tsx +0 -45
  863. package/src/MetadataList/MetadataList.spec.md +0 -187
  864. package/src/MetadataList/MetadataList.test.tsx +0 -383
  865. package/src/MetadataList/MetadataListItem.spec.md +0 -165
  866. package/src/MobileNav/MobileNav.spec.md +0 -306
  867. package/src/MobileNav/MobileNav.test.tsx +0 -296
  868. package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +0 -584
  869. package/src/MobileNav/MobileNavCloseTiming.test.ts +0 -58
  870. package/src/MobileNav/MobileNavCloseVisibility.test.tsx +0 -172
  871. package/src/MobileNav/MobileNavEntryAnimation.test.tsx +0 -212
  872. package/src/MobileNav/MobileNavFocus.test.tsx +0 -553
  873. package/src/MobileNav/MobileNavPinchZoom.a11y.chromium.spec.ts +0 -147
  874. package/src/MobileNav/MobileNavReopen.test.tsx +0 -118
  875. package/src/MobileNav/MobileNavScrollbarGutter.test.tsx +0 -109
  876. package/src/MobileNav/MobileNavToggle.test.tsx +0 -179
  877. package/src/MobileNav/__tests__/MobileNavHeader.a11y.chromium.spec.ts +0 -89
  878. package/src/MobileNav/__tests__/MobileNavWidth.a11y.chromium.spec.ts +0 -103
  879. package/src/MoreMenu/MoreMenu.spec.md +0 -268
  880. package/src/MoreMenu/MoreMenu.test.tsx +0 -367
  881. package/src/MultiSelector/CreateRow.a11y.chromium.spec.ts +0 -148
  882. package/src/MultiSelector/MultiSelector.disabledFocus.a11y.chromium.spec.ts +0 -72
  883. package/src/MultiSelector/MultiSelector.focusableDisabled.test.tsx +0 -121
  884. package/src/MultiSelector/MultiSelector.fr10.test.tsx +0 -221
  885. package/src/MultiSelector/MultiSelector.spec.md +0 -357
  886. package/src/MultiSelector/MultiSelector.test.tsx +0 -4971
  887. package/src/MultiSelector/OptionActions.a11y.chromium.spec.ts +0 -310
  888. package/src/MultiSelector/__tests__/MultiSelector.a11y.chromium.spec.ts +0 -46
  889. package/src/MultiSelector/__tests__/MultiSelector.listbox.a11y.test.tsx +0 -135
  890. package/src/NavIcon/NavIcon.spec.md +0 -158
  891. package/src/NavIcon/NavIcon.test.tsx +0 -41
  892. package/src/NavMenu/NavHeadingMenu.spec.md +0 -264
  893. package/src/NavMenu/NavHeadingMenu.test.tsx +0 -714
  894. package/src/NavMenu/__tests__/NavMenusSlideSelection.a11y.chromium.spec.ts +0 -137
  895. package/src/NumberInput/NumberInput.fr10.test.tsx +0 -21
  896. package/src/NumberInput/NumberInput.spec.md +0 -270
  897. package/src/NumberInput/NumberInput.test.tsx +0 -2697
  898. package/src/NumberInput/__tests__/NumberInput.a11y.chromium.spec.ts +0 -124
  899. package/src/NumberInput/__tests__/NumberInput.a11y.known-failures.ts +0 -26
  900. package/src/NumberInput/__tests__/NumberInput.a11y.renders.tsx +0 -77
  901. package/src/NumberInput/__tests__/NumberInput.a11y.states.ts +0 -116
  902. package/src/NumberInput/__tests__/NumberInput.a11y.test.tsx +0 -154
  903. package/src/NumberInput/__tests__/NumberInput.coarse-pointer.a11y.chromium.spec.ts +0 -102
  904. package/src/NumberInput/numberInputCommit.test.ts +0 -72
  905. package/src/NumberInput/numberParser.docblock.test.ts +0 -234
  906. package/src/NumberInput/numberParser.test.ts +0 -222
  907. package/src/Outline/Outline.spec.md +0 -258
  908. package/src/Outline/Outline.test.tsx +0 -1134
  909. package/src/Outline/OutlineFocus.a11y.chromium.spec.ts +0 -551
  910. package/src/Outline/modules/parseOutlineFromMarkdown.spec.md +0 -150
  911. package/src/Outline/parseOutlineFromMarkdown.test.ts +0 -104
  912. package/src/OverflowList/OverflowList.spec.md +0 -204
  913. package/src/OverflowList/OverflowList.test.tsx +0 -1073
  914. package/src/Overlay/Overlay.spec.md +0 -304
  915. package/src/Overlay/Overlay.test.tsx +0 -424
  916. package/src/Pagination/Pagination.spec.md +0 -300
  917. package/src/Pagination/Pagination.test.tsx +0 -1754
  918. package/src/Popover/Popover.spec.md +0 -525
  919. package/src/Popover/Popover.test.tsx +0 -1508
  920. package/src/Popover/PopoverPresentation.test.tsx +0 -952
  921. package/src/Popover/__tests__/PopoverSecondDocument.a11y.chromium.spec.ts +0 -267
  922. package/src/PowerSearch/PowerSearch.spec.md +0 -661
  923. package/src/PowerSearch/PowerSearch.test.tsx +0 -1027
  924. package/src/PowerSearch/PowerSearchEditPopover.test.tsx +0 -1190
  925. package/src/PowerSearch/PowerSearchFilterEditor.test.tsx +0 -377
  926. package/src/PowerSearch/PowerSearchToken.test.tsx +0 -230
  927. package/src/PowerSearch/PowerSearchValueEditor.test.tsx +0 -754
  928. package/src/PowerSearch/formatFilterValue.test.ts +0 -339
  929. package/src/PowerSearch/resolveDateTimeRangePart.test.ts +0 -40
  930. package/src/PowerSearch/usePowerSearchConfig.test.ts +0 -832
  931. package/src/PowerSearch/usePowerSearchSource.test.ts +0 -621
  932. package/src/ProgressBar/ProgressBar.spec.md +0 -261
  933. package/src/ProgressBar/ProgressBar.test.tsx +0 -928
  934. package/src/ProgressBar/__tests__/ProgressBar.a11y.chromium.spec.ts +0 -127
  935. package/src/ProgressBar/__tests__/ProgressBar.a11y.known-failures.ts +0 -12
  936. package/src/ProgressBar/__tests__/ProgressBar.a11y.renders.tsx +0 -38
  937. package/src/ProgressBar/__tests__/ProgressBar.a11y.states.ts +0 -123
  938. package/src/ProgressBar/__tests__/ProgressBar.a11y.test.tsx +0 -107
  939. package/src/ProgressBar/__tests__/ProgressBarMotion.a11y.chromium.spec.ts +0 -831
  940. package/src/RadioList/RadioList.fr10.test.tsx +0 -67
  941. package/src/RadioList/RadioList.spec.md +0 -227
  942. package/src/RadioList/RadioList.test.tsx +0 -1051
  943. package/src/RadioList/RadioListItem.press.source-build.test.mjs +0 -80
  944. package/src/RadioList/RadioListItem.spec.md +0 -223
  945. package/src/RadioList/__tests__/RadioGroup.a11y.chromium.spec.ts +0 -274
  946. package/src/RadioList/__tests__/RadioGroup.a11y.known-failures.ts +0 -12
  947. package/src/RadioList/__tests__/RadioGroup.a11y.renders.tsx +0 -232
  948. package/src/RadioList/__tests__/RadioGroup.a11y.states.ts +0 -503
  949. package/src/RadioList/__tests__/RadioGroup.a11y.test.tsx +0 -217
  950. package/src/Resizable/Resizable.spec.md +0 -298
  951. package/src/Resizable/ResizeHandle.test.tsx +0 -1067
  952. package/src/Resizable/__tests__/ResizeHandle.a11y.chromium.spec.ts +0 -127
  953. package/src/Resizable/__tests__/ResizeHandle.a11y.known-failures.ts +0 -39
  954. package/src/Resizable/__tests__/ResizeHandle.a11y.renders.tsx +0 -80
  955. package/src/Resizable/__tests__/ResizeHandle.a11y.states.ts +0 -197
  956. package/src/Resizable/__tests__/ResizeHandle.a11y.test.tsx +0 -123
  957. package/src/Resizable/shippedDirections.test.ts +0 -462
  958. package/src/Resizable/useResizable.test.ts +0 -1725
  959. package/src/Resizable/utils.test.ts +0 -74
  960. package/src/ScrollableArea/ScrollableArea.spec.md +0 -190
  961. package/src/ScrollableArea/ScrollableArea.test.tsx +0 -304
  962. package/src/ScrollableArea/modules/useScrollableArea.spec.md +0 -149
  963. package/src/Section/Section.spec.md +0 -197
  964. package/src/Section/Section.test.tsx +0 -376
  965. package/src/SegmentedControl/SegmentedControl.spec.md +0 -261
  966. package/src/SegmentedControl/SegmentedControl.test.tsx +0 -981
  967. package/src/SegmentedControl/SegmentedControlItem.spec.md +0 -206
  968. package/src/SelectableCard/SelectableCard.spec.md +0 -230
  969. package/src/SelectableCard/SelectableCard.test.tsx +0 -216
  970. package/src/Selector/Selector.focusableDisabled.test.tsx +0 -127
  971. package/src/Selector/Selector.fr10.test.tsx +0 -149
  972. package/src/Selector/Selector.sheetActiveDescendant.test.tsx +0 -95
  973. package/src/Selector/Selector.source-build.test.mjs +0 -79
  974. package/src/Selector/Selector.spec.md +0 -379
  975. package/src/Selector/Selector.test.tsx +0 -5117
  976. package/src/Selector/__tests__/Combobox.a11y.known-failures.ts +0 -27
  977. package/src/Selector/__tests__/Combobox.a11y.states.ts +0 -145
  978. package/src/Selector/__tests__/ComboboxField.a11y.chromium.spec.ts +0 -458
  979. package/src/Selector/__tests__/EmptyStateAnnouncement.a11y.chromium.spec.ts +0 -91
  980. package/src/Selector/__tests__/Listbox.a11y.chromium.spec.ts +0 -131
  981. package/src/Selector/__tests__/Listbox.a11y.dom.ts +0 -38
  982. package/src/Selector/__tests__/Listbox.a11y.inventory.test.ts +0 -63
  983. package/src/Selector/__tests__/Listbox.a11y.known-failures.ts +0 -12
  984. package/src/Selector/__tests__/Listbox.a11y.states.ts +0 -232
  985. package/src/Selector/__tests__/Selector.a11y.chromium.spec.ts +0 -46
  986. package/src/Selector/__tests__/Selector.combobox.a11y.chromium.spec.ts +0 -109
  987. package/src/Selector/__tests__/Selector.combobox.a11y.test.tsx +0 -133
  988. package/src/Selector/__tests__/Selector.listbox.a11y.test.tsx +0 -128
  989. package/src/Selector/useCombobox.public.test.tsx +0 -89
  990. package/src/Selector/useCombobox.test.tsx +0 -444
  991. package/src/SideNav/SideNav.spec.md +0 -247
  992. package/src/SideNav/SideNav.test.tsx +0 -4275
  993. package/src/SideNav/SideNavCollapseButton.spec.md +0 -161
  994. package/src/SideNav/SideNavHeading.spec.md +0 -226
  995. package/src/SideNav/SideNavItem.spec.md +0 -236
  996. package/src/SideNav/SideNavRailMotion.test.tsx +0 -620
  997. package/src/SideNav/SideNavSection.spec.md +0 -153
  998. package/src/SideNav/__tests__/SideNavRailMotion.interaction.browser.spec.ts +0 -542
  999. package/src/SizeContext/SizeContext.test.tsx +0 -226
  1000. package/src/Skeleton/Skeleton.spec.md +0 -184
  1001. package/src/Skeleton/Skeleton.test.tsx +0 -148
  1002. package/src/Slider/Slider.fr10.test.tsx +0 -93
  1003. package/src/Slider/Slider.spec.md +0 -285
  1004. package/src/Slider/Slider.test.tsx +0 -1661
  1005. package/src/Slider/__tests__/Slider.a11y.chromium.spec.ts +0 -125
  1006. package/src/Slider/__tests__/Slider.a11y.known-failures.ts +0 -12
  1007. package/src/Slider/__tests__/Slider.a11y.renders.tsx +0 -42
  1008. package/src/Slider/__tests__/Slider.a11y.states.ts +0 -106
  1009. package/src/Slider/__tests__/Slider.a11y.test.tsx +0 -105
  1010. package/src/Spinner/Spinner.spec.md +0 -231
  1011. package/src/Spinner/Spinner.test.tsx +0 -484
  1012. package/src/Stack/Stack.spec.md +0 -203
  1013. package/src/Stack/Stack.test.tsx +0 -473
  1014. package/src/Stack/StackItem.test.tsx +0 -105
  1015. package/src/StatusDot/StatusDot.spec.md +0 -224
  1016. package/src/StatusDot/StatusDot.test.tsx +0 -213
  1017. package/src/Stepper/Step.spec.md +0 -200
  1018. package/src/Stepper/Stepper.public.test.ts +0 -91
  1019. package/src/Stepper/Stepper.spec.md +0 -327
  1020. package/src/Stepper/Stepper.test.tsx +0 -2130
  1021. package/src/Switch/Switch.focusRing.source-build.test.mjs +0 -81
  1022. package/src/Switch/Switch.spec.md +0 -277
  1023. package/src/Switch/Switch.test.tsx +0 -1136
  1024. package/src/Switch/__tests__/Switch.a11y.chromium.spec.ts +0 -211
  1025. package/src/Switch/__tests__/Switch.a11y.known-failures.ts +0 -28
  1026. package/src/Switch/__tests__/Switch.a11y.states.ts +0 -275
  1027. package/src/Switch/__tests__/Switch.a11y.test.tsx +0 -144
  1028. package/src/TabList/Tab.spec.md +0 -201
  1029. package/src/TabList/TabList.spec.md +0 -217
  1030. package/src/TabList/TabList.test.tsx +0 -1755
  1031. package/src/TabList/TabMenu.spec.md +0 -198
  1032. package/src/TabList/__tests__/TabListOverflow.interaction.browser.spec.ts +0 -99
  1033. package/src/TabList/__tests__/TabPressPointer.interaction.browser.spec.ts +0 -163
  1034. package/src/TabList/__tests__/Tabs.a11y.chromium.spec.ts +0 -199
  1035. package/src/TabList/__tests__/Tabs.a11y.known-failures.ts +0 -12
  1036. package/src/TabList/__tests__/Tabs.a11y.renders.tsx +0 -92
  1037. package/src/TabList/__tests__/Tabs.a11y.states.ts +0 -247
  1038. package/src/TabList/__tests__/Tabs.a11y.test.tsx +0 -153
  1039. package/src/Table/Table.perf.test.tsx +0 -569
  1040. package/src/Table/Table.spec.md +0 -395
  1041. package/src/Table/Table.test.tsx +0 -1883
  1042. package/src/Table/TableHeaderCell.test.tsx +0 -291
  1043. package/src/Table/TableSelectionToolbar.test.tsx +0 -109
  1044. package/src/Table/__tests__/Table.a11y.chromium.spec.ts +0 -141
  1045. package/src/Table/__tests__/Table.a11y.contracts.ts +0 -35
  1046. package/src/Table/__tests__/Table.a11y.renders.tsx +0 -112
  1047. package/src/Table/__tests__/Table.a11y.states.ts +0 -136
  1048. package/src/Table/__tests__/Table.a11y.test.tsx +0 -198
  1049. package/src/Table/__tests__/TableMinWidth.a11y.chromium.spec.ts +0 -75
  1050. package/src/Table/__tests__/TableRowGlide.a11y.browser.spec.ts +0 -382
  1051. package/src/Table/__tests__/TableScroll.a11y.chromium.spec.ts +0 -138
  1052. package/src/Table/__tests__/TableStickyDividers.a11y.browser.spec.ts +0 -560
  1053. package/src/Table/plugins/activation/useTableActivation.spec.md +0 -254
  1054. package/src/Table/plugins/columnResize/useTableColumnResize.spec.md +0 -373
  1055. package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -941
  1056. package/src/Table/plugins/columnSettings/useTableColumnSettings.test.tsx +0 -175
  1057. package/src/Table/plugins/columnSettings/useTableColumnSettingsState.test.tsx +0 -296
  1058. package/src/Table/plugins/filtering/useTableFilterState.test.tsx +0 -143
  1059. package/src/Table/plugins/filtering/useTableFiltering.test.tsx +0 -357
  1060. package/src/Table/plugins/groupedRows/useTableGroupedRows-perf.test.tsx +0 -112
  1061. package/src/Table/plugins/groupedRows/useTableGroupedRows.test.tsx +0 -409
  1062. package/src/Table/plugins/infiniteScroll/useTableInfiniteScroll.spec.md +0 -244
  1063. package/src/Table/plugins/pagination/useTablePagination-perf.test.tsx +0 -232
  1064. package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -666
  1065. package/src/Table/plugins/rowExpansion/useTableRowExpansion.glide.test.tsx +0 -280
  1066. package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +0 -681
  1067. package/src/Table/plugins/rowIndex/useTableRowIndex.test.tsx +0 -132
  1068. package/src/Table/plugins/rowStatus/useTableRowStatus.public.test.ts +0 -92
  1069. package/src/Table/plugins/rowStatus/useTableRowStatus.spec.md +0 -355
  1070. package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +0 -519
  1071. package/src/Table/plugins/selection/useTableSelection-perf.test.tsx +0 -258
  1072. package/src/Table/plugins/selection/useTableSelection.test.tsx +0 -424
  1073. package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -451
  1074. package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.test.tsx +0 -174
  1075. package/src/Table/plugins/sortable/__tests__/useTableSortable.a11y.ts +0 -86
  1076. package/src/Table/plugins/sortable/useTableSortable.test.tsx +0 -786
  1077. package/src/Table/plugins/sortable/useTableSortableState.test.tsx +0 -580
  1078. package/src/Table/plugins/stickyColumns/useTableStickyColumns.test.tsx +0 -334
  1079. package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +0 -163
  1080. package/src/Table/plugins/tree/useTableTreeData.test.tsx +0 -992
  1081. package/src/Table/plugins/tree/useTableTreeState.test.tsx +0 -740
  1082. package/src/Table/plugins/virtualization/useTableVirtualization.spec.md +0 -266
  1083. package/src/Table/tableContextMenu.test.tsx +0 -208
  1084. package/src/Text/Text.spec.md +0 -193
  1085. package/src/Text/Text.test.tsx +0 -405
  1086. package/src/Text/useTruncation.test.tsx +0 -196
  1087. package/src/TextArea/TextArea.fr10.test.tsx +0 -21
  1088. package/src/TextArea/TextArea.spec.md +0 -300
  1089. package/src/TextArea/TextArea.test.tsx +0 -1317
  1090. package/src/TextInput/TextInput.fr10.test.tsx +0 -21
  1091. package/src/TextInput/TextInput.spec.md +0 -260
  1092. package/src/TextInput/TextInput.test.tsx +0 -1280
  1093. package/src/TextInput/__tests__/TextInput.a11y.chromium.spec.ts +0 -214
  1094. package/src/TextInput/__tests__/TextInput.a11y.known-failures.ts +0 -40
  1095. package/src/TextInput/__tests__/TextInput.a11y.renders.tsx +0 -202
  1096. package/src/TextInput/__tests__/TextInput.a11y.states.ts +0 -384
  1097. package/src/TextInput/__tests__/TextInput.a11y.test.tsx +0 -130
  1098. package/src/Thumbnail/Thumbnail.spec.md +0 -244
  1099. package/src/Thumbnail/Thumbnail.test.tsx +0 -384
  1100. package/src/TimeInput/NativeTimeInput.test.tsx +0 -343
  1101. package/src/TimeInput/Presentation.test.tsx +0 -192
  1102. package/src/TimeInput/TimeInput.fr10.test.tsx +0 -312
  1103. package/src/TimeInput/TimeInput.spec.md +0 -270
  1104. package/src/TimeInput/TimeInput.test.tsx +0 -907
  1105. package/src/Timer/Timer.spec.md +0 -215
  1106. package/src/Timer/Timer.test.tsx +0 -302
  1107. package/src/Timestamp/Timestamp.spec.md +0 -246
  1108. package/src/Timestamp/Timestamp.test.tsx +0 -1661
  1109. package/src/Timestamp/tooltipEntries.test.ts +0 -566
  1110. package/src/Toast/Toast.spec.md +0 -291
  1111. package/src/Toast/ToastViewport.modalHost.test.tsx +0 -891
  1112. package/src/Toast/ToastViewport.test.tsx +0 -3130
  1113. package/src/Toast/useToast.test.tsx +0 -258
  1114. package/src/ToggleButton/ToggleButton.spec.md +0 -256
  1115. package/src/ToggleButton/ToggleButton.test.tsx +0 -1000
  1116. package/src/ToggleButton/ToggleButtonGroup.spec.md +0 -195
  1117. package/src/ToggleButton/__tests__/ToggleButton.a11y.chromium.spec.ts +0 -177
  1118. package/src/ToggleButton/__tests__/ToggleButton.a11y.renders.tsx +0 -133
  1119. package/src/ToggleButton/__tests__/ToggleButton.a11y.states.ts +0 -167
  1120. package/src/ToggleButton/__tests__/ToggleButton.a11y.test.tsx +0 -103
  1121. package/src/Token/Token.spec.md +0 -261
  1122. package/src/Token/Token.test.tsx +0 -707
  1123. package/src/Token/__tests__/TokenRemoveTarget.a11y.chromium.spec.ts +0 -87
  1124. package/src/Tokenizer/Tokenizer.fr10.test.tsx +0 -28
  1125. package/src/Tokenizer/Tokenizer.props-type.test.tsx +0 -126
  1126. package/src/Tokenizer/Tokenizer.spec.md +0 -615
  1127. package/src/Tokenizer/Tokenizer.ssr.test.tsx +0 -196
  1128. package/src/Tokenizer/Tokenizer.test.tsx +0 -2216
  1129. package/src/Tokenizer/TokenizerBottomSheet.test.tsx +0 -2738
  1130. package/src/Tokenizer/__tests__/TokenizerEndLane.a11y.chromium.spec.ts +0 -505
  1131. package/src/Toolbar/Toolbar.spec.md +0 -227
  1132. package/src/Toolbar/Toolbar.test.tsx +0 -600
  1133. package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +0 -176
  1134. package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +0 -63
  1135. package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +0 -142
  1136. package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +0 -126
  1137. package/src/Tooltip/Tooltip.spec.md +0 -267
  1138. package/src/Tooltip/Tooltip.test.tsx +0 -819
  1139. package/src/Tooltip/__tests__/Tooltip.a11y.chromium.spec.ts +0 -141
  1140. package/src/Tooltip/__tests__/Tooltip.a11y.renders.tsx +0 -86
  1141. package/src/Tooltip/__tests__/Tooltip.a11y.states.ts +0 -132
  1142. package/src/Tooltip/__tests__/Tooltip.a11y.test.tsx +0 -126
  1143. package/src/Tooltip/__tests__/TooltipFocusReturn.a11y.chromium.spec.ts +0 -130
  1144. package/src/Tooltip/__tests__/TooltipHoverBridge.a11y.chromium.spec.ts +0 -75
  1145. package/src/Tooltip/__tests__/TooltipTallContent.a11y.browser.spec.ts +0 -673
  1146. package/src/Tooltip/__tests__/TooltipTallContentTracking.a11y.browser.spec.ts +0 -1162
  1147. package/src/Tooltip/useTooltip.test.tsx +0 -483
  1148. package/src/TopNav/TopNav.spec.md +0 -190
  1149. package/src/TopNav/TopNav.test.tsx +0 -863
  1150. package/src/TopNav/TopNavHeading.spec.md +0 -211
  1151. package/src/TopNav/TopNavItem.spec.md +0 -189
  1152. package/src/TopNav/TopNavMegaMenu.reduced-motion.source-build.test.mjs +0 -83
  1153. package/src/TopNav/TopNavMegaMenu.spec.md +0 -190
  1154. package/src/TopNav/TopNavMegaMenu.test.tsx +0 -1153
  1155. package/src/TopNav/TopNavMegaMenuFeaturedCard.spec.md +0 -150
  1156. package/src/TopNav/TopNavMegaMenuItem.spec.md +0 -155
  1157. package/src/TopNav/TopNavMenu.spec.md +0 -202
  1158. package/src/TopNav/TopNavMenu.test.tsx +0 -502
  1159. package/src/TopNav/__tests__/TopNavMobileLongHeading.a11y.chromium.spec.ts +0 -75
  1160. package/src/TreeList/TreeList.spec.md +0 -328
  1161. package/src/TreeList/TreeList.test.tsx +0 -1737
  1162. package/src/TreeList/__tests__/TreeList.a11y.chromium.spec.ts +0 -138
  1163. package/src/TreeList/__tests__/TreeList.a11y.renders.tsx +0 -44
  1164. package/src/TreeList/__tests__/TreeList.a11y.states.ts +0 -164
  1165. package/src/TreeList/__tests__/TreeList.a11y.test.tsx +0 -138
  1166. package/src/TreeList/__tests__/TreeListGuideAlignment.interaction.browser.spec.ts +0 -127
  1167. package/src/TreeList/__tests__/TreeListRowNames.a11y.chromium.spec.ts +0 -83
  1168. package/src/Typeahead/BaseTypeahead.emptyState.a11y.chromium.spec.ts +0 -138
  1169. package/src/Typeahead/BaseTypeahead.spec.md +0 -270
  1170. package/src/Typeahead/BaseTypeahead.test.tsx +0 -553
  1171. package/src/Typeahead/Typeahead.fr10.test.tsx +0 -28
  1172. package/src/Typeahead/Typeahead.spec.md +0 -257
  1173. package/src/Typeahead/Typeahead.test.tsx +0 -2984
  1174. package/src/Typeahead/TypeaheadItem.test.tsx +0 -134
  1175. package/src/VStack/VStack.test.tsx +0 -128
  1176. package/src/VisuallyHidden/VisuallyHidden.spec.md +0 -170
  1177. package/src/VisuallyHidden/VisuallyHidden.test.tsx +0 -115
  1178. package/src/__tests__/BusyOwnerAnnouncement.test.tsx +0 -853
  1179. package/src/__tests__/CardPress.a11y.chromium.spec.ts +0 -353
  1180. package/src/__tests__/DisabledTriggerFocus.a11y.chromium.spec.ts +0 -87
  1181. package/src/__tests__/PressModality.a11y.chromium.spec.ts +0 -392
  1182. package/src/__tests__/PressedState.a11y.chromium.spec.ts +0 -814
  1183. package/src/__tests__/TestIcon.tsx +0 -26
  1184. package/src/__tests__/babelPluginAddExtensions.test.ts +0 -190
  1185. package/src/__tests__/fastRoleQueries.ts +0 -66
  1186. package/src/__tests__/forcedColors.ts +0 -60
  1187. package/src/__tests__/internalModulesNotExperimental.test.ts +0 -88
  1188. package/src/__tests__/pressState.ts +0 -216
  1189. package/src/__tests__/stubMatchMedia.test.ts +0 -79
  1190. package/src/__tests__/stubMatchMedia.ts +0 -95
  1191. package/src/__tests__/stylexDeclarations.ts +0 -51
  1192. package/src/__tests__/unthemedLength.ts +0 -34
  1193. package/src/docPropLiterals.test.ts +0 -964
  1194. package/src/docPropReferences.test.ts +0 -178
  1195. package/src/docs-script.test.mjs +0 -55
  1196. package/src/gestures/__tests__/GestureHost.test.ts +0 -2136
  1197. package/src/gestures/__tests__/LongPressGestureRecognizer.test.ts +0 -141
  1198. package/src/gestures/__tests__/PanGestureRecognizer.test.ts +0 -327
  1199. package/src/gestures/__tests__/PinchGestureRecognizer.test.ts +0 -74
  1200. package/src/gestures/__tests__/PressTracker.test.ts +0 -275
  1201. package/src/gestures/__tests__/ScreenEdgePanGestureRecognizer.test.ts +0 -498
  1202. package/src/gestures/__tests__/SwipeGestureRecognizer.test.ts +0 -108
  1203. package/src/gestures/__tests__/TapGestureRecognizer.test.ts +0 -154
  1204. package/src/gestures/__tests__/VelocityTracker.streams.test.ts +0 -243
  1205. package/src/gestures/__tests__/VelocityTracker.test.ts +0 -261
  1206. package/src/gestures/__tests__/arena.test.ts +0 -864
  1207. package/src/gestures/__tests__/arenaContinuous.test.ts +0 -207
  1208. package/src/gestures/__tests__/arenaPinchSwipeEdge.test.ts +0 -193
  1209. package/src/gestures/__tests__/arenaTapLongPress.test.ts +0 -211
  1210. package/src/gestures/__tests__/browser/build-fixture.mjs +0 -46
  1211. package/src/gestures/__tests__/browser/fixture.ts +0 -307
  1212. package/src/gestures/__tests__/browser/gestures.interaction.browser.spec.ts +0 -1137
  1213. package/src/gestures/__tests__/browserNegotiation.test.ts +0 -293
  1214. package/src/gestures/__tests__/fixtures/pointer-streams/README.md +0 -82
  1215. package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-fling.json +0 -130
  1216. package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-slow.json +0 -860
  1217. package/src/gestures/__tests__/fixtures/pointer-streams/android-chromium-stop-release.json +0 -357
  1218. package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-fling.json +0 -66
  1219. package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-slow.json +0 -268
  1220. package/src/gestures/__tests__/fixtures/pointer-streams/android-firefox-stop-release.json +0 -128
  1221. package/src/gestures/__tests__/fixtures/pointer-streams/capture.html +0 -123
  1222. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-fling.json +0 -64
  1223. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-slow.json +0 -268
  1224. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-mouse-stop-release.json +0 -124
  1225. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-fling.json +0 -64
  1226. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-slow.json +0 -268
  1227. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-pen-stop-release.json +0 -124
  1228. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-fling.json +0 -84
  1229. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-slow.json +0 -381
  1230. package/src/gestures/__tests__/fixtures/pointer-streams/desktop-chromium-touch-stop-release.json +0 -171
  1231. package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-fling.json +0 -45
  1232. package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-slow.json +0 -101
  1233. package/src/gestures/__tests__/fixtures/pointer-streams/ios-safari-stop-release.json +0 -130
  1234. package/src/gestures/__tests__/haptics.test.ts +0 -187
  1235. package/src/gestures/__tests__/harness.ts +0 -252
  1236. package/src/gestures/__tests__/pressTrackerFactory.test.ts +0 -59
  1237. package/src/gestures/__tests__/recognizerKind.test.ts +0 -78
  1238. package/src/gestures/__tests__/touchActionRule.test.ts +0 -54
  1239. package/src/gestures/__tests__/useGestureRecognizer.test.tsx +0 -570
  1240. package/src/hooks/__tests__/MenuPressShadowRoot.a11y.chromium.spec.ts +0 -144
  1241. package/src/hooks/__tests__/menuLinkClicks.ts +0 -71
  1242. package/src/hooks/actionGroupTracker.exports.test.ts +0 -28
  1243. package/src/hooks/actionGroupTracker.test.ts +0 -510
  1244. package/src/hooks/computeOverflow.test.ts +0 -422
  1245. package/src/hooks/deprecatedApis.public.test.ts +0 -33
  1246. package/src/hooks/menuPressGesture.test.ts +0 -253
  1247. package/src/hooks/scrollKeyboardDelegation.test.ts +0 -188
  1248. package/src/hooks/scrollbarGutter.test.ts +0 -167
  1249. package/src/hooks/useAnnounce.modal.test.tsx +0 -1291
  1250. package/src/hooks/useAnnounce.replace.test.tsx +0 -516
  1251. package/src/hooks/useAnnounce.test.tsx +0 -293
  1252. package/src/hooks/useAnnounceRenderedText.adopters.test.tsx +0 -235
  1253. package/src/hooks/useAutoMediaMode.test.ts +0 -109
  1254. package/src/hooks/useBusyAnnouncement.adopters.test.tsx +0 -69
  1255. package/src/hooks/useBusyAnnouncement.test.tsx +0 -112
  1256. package/src/hooks/useClickableContainer.test.tsx +0 -236
  1257. package/src/hooks/useClipboard.test.tsx +0 -151
  1258. package/src/hooks/useContainerReveal.test.tsx +0 -163
  1259. package/src/hooks/useDevWarning.test.tsx +0 -45
  1260. package/src/hooks/useEntryAnimation.ssr.test.tsx +0 -143
  1261. package/src/hooks/useFocusTrap.test.tsx +0 -635
  1262. package/src/hooks/useFocusTrapEscapeShim.test.tsx +0 -166
  1263. package/src/hooks/useGridFocus.test.tsx +0 -165
  1264. package/src/hooks/useHotkeys.test.ts +0 -265
  1265. package/src/hooks/useImageMode.test.ts +0 -133
  1266. package/src/hooks/useInputContainer.test.tsx +0 -180
  1267. package/src/hooks/useInputStatusIcon.test.tsx +0 -240
  1268. package/src/hooks/useIsHydrating.test.tsx +0 -70
  1269. package/src/hooks/useKeyboardHint.test.tsx +0 -92
  1270. package/src/hooks/useListFocus.test.tsx +0 -664
  1271. package/src/hooks/useLongPress.test.tsx +0 -152
  1272. package/src/hooks/useMediaQuery.test.ts +0 -165
  1273. package/src/hooks/useMenuHover.test.tsx +0 -536
  1274. package/src/hooks/useMenuPress.test.tsx +0 -565
  1275. package/src/hooks/useMergedRefs.test.tsx +0 -62
  1276. package/src/hooks/useOverflow.test.ts +0 -504
  1277. package/src/hooks/useRenamedProp.test.tsx +0 -95
  1278. package/src/hooks/useScrollLock.test.ts +0 -248
  1279. package/src/hooks/useScrollOverflow.test.tsx +0 -323
  1280. package/src/hooks/useScrollableArea.test.tsx +0 -836
  1281. package/src/hooks/useStreamingText.test.ts +0 -237
  1282. package/src/hooks/useTreeFocus.test.tsx +0 -351
  1283. package/src/hooks/useTypeahead.test.tsx +0 -351
  1284. package/src/i18n/InternationalizationProvider.spec.md +0 -252
  1285. package/src/i18n/__tests__/InternationalizationProvider.test.tsx +0 -128
  1286. package/src/i18n/__tests__/e2e-pagination.test.tsx +0 -172
  1287. package/src/i18n/__tests__/e2e-powersearch.test.tsx +0 -166
  1288. package/src/i18n/__tests__/getLocaleDirection.test.ts +0 -40
  1289. package/src/i18n/__tests__/resolve.test.ts +0 -325
  1290. package/src/i18n/__tests__/useCollator.test.tsx +0 -115
  1291. package/src/i18n/__tests__/useDirection.test.tsx +0 -66
  1292. package/src/i18n/__tests__/useLocale.test.tsx +0 -52
  1293. package/src/i18n/useTranslator.test.tsx +0 -267
  1294. package/src/inputFontFloor.test.ts +0 -106
  1295. package/src/internal/tokenizer-presentation.test.ts +0 -29
  1296. package/src/internal/touch.test.ts +0 -44
  1297. package/src/motion/__tests__/animatedLayout.interaction.browser.spec.ts +0 -1613
  1298. package/src/motion/__tests__/animatedLayoutAdopters.interaction.browser.spec.ts +0 -217
  1299. package/src/motion/__tests__/animatedLayoutBundle.ts +0 -17
  1300. package/src/motion/__tests__/animator.interaction.browser.spec.ts +0 -843
  1301. package/src/motion/__tests__/motionBundle.ts +0 -14
  1302. package/src/motion/animatedLayout.boundary.test.ts +0 -155
  1303. package/src/motion/animatedLayout.swap.test.ts +0 -827
  1304. package/src/motion/animatedLayout.test.ts +0 -1154
  1305. package/src/motion/animatedLayoutAdopters.test.tsx +0 -500
  1306. package/src/motion/animator.test.ts +0 -3746
  1307. package/src/motion/bezier.test.ts +0 -52
  1308. package/src/motion/coast.test.ts +0 -99
  1309. package/src/motion/decay.test.ts +0 -120
  1310. package/src/motion/projection.test.ts +0 -107
  1311. package/src/motion/reducedMotion.test.ts +0 -129
  1312. package/src/motion/reducedMotionPlan.test.ts +0 -208
  1313. package/src/motion/resolveMotion.test.ts +0 -123
  1314. package/src/motion/resolveSnapPoint.test.ts +0 -189
  1315. package/src/motion/roles.test.ts +0 -273
  1316. package/src/motion/rubberBand.test.ts +0 -88
  1317. package/src/motion/selectPage.test.ts +0 -31
  1318. package/src/motion/spring.test.ts +0 -650
  1319. package/src/motion/uikit/gestures.test.ts +0 -89
  1320. package/src/motion/uikit/springRows.test.ts +0 -71
  1321. package/src/motion/uikit/tableImports.test.ts +0 -293
  1322. package/src/motion/uikit/uikit.test.ts +0 -364
  1323. package/src/motion/uikit/values.test.ts +0 -121
  1324. package/src/naming.test.ts +0 -44
  1325. package/src/postinstall.test.mjs +0 -216
  1326. package/src/reset.a11y.chromium.spec.ts +0 -238
  1327. package/src/reset.test.ts +0 -205
  1328. package/src/reset.until-found.a11y.browser.spec.ts +0 -76
  1329. package/src/reset.until-found.test.ts +0 -32
  1330. package/src/serverSafeComponents.test.ts +0 -675
  1331. package/src/stylexValidation.source-build.test.mjs +0 -42
  1332. package/src/tailwind-theme.test.ts +0 -153
  1333. package/src/theme/MediaTheme.dom.test.tsx +0 -97
  1334. package/src/theme/Theme.test.tsx +0 -337
  1335. package/src/theme/__tests__/SpacingRootFontSize.a11y.chromium.spec.ts +0 -199
  1336. package/src/theme/contrast.test.ts +0 -94
  1337. package/src/theme/declarationBoundary.test.ts +0 -126
  1338. package/src/theme/defineTheme.test.ts +0 -2028
  1339. package/src/theme/derivedVarRegistry.test.ts +0 -592
  1340. package/src/theme/expandColorScale.test.ts +0 -651
  1341. package/src/theme/expandMotionScale.test.ts +0 -145
  1342. package/src/theme/expandRadiusScale.test.ts +0 -59
  1343. package/src/theme/expandTypeScale.test.ts +0 -351
  1344. package/src/theme/extensibleAxes.test.ts +0 -432
  1345. package/src/theme/generateThemeRules.test.ts +0 -1870
  1346. package/src/theme/hct.test.ts +0 -93
  1347. package/src/theme/onMediaTokens.test.ts +0 -431
  1348. package/src/theme/publicThemeHelperContract.test.ts +0 -67
  1349. package/src/theme/syntax/defineSyntaxTheme.diff.test.ts +0 -91
  1350. package/src/theme/syntax/serverSafeSyntax.test.ts +0 -68
  1351. package/src/theme/syntax/useSyntaxTheme.diff.test.tsx +0 -40
  1352. package/src/theme/themeAdaptations.test.ts +0 -1795
  1353. package/src/theme/themeMotion.test.ts +0 -620
  1354. package/src/theme/themeRegistry.test.ts +0 -59
  1355. package/src/theme/themingTargets.test.ts +0 -589
  1356. package/src/theme/tokenValueCompat.test.ts +0 -204
  1357. package/src/theme/tokens.test.ts +0 -441
  1358. package/src/theme/useTheme.test.tsx +0 -322
  1359. package/src/unicodePropertyEscapes.source-build.test.mjs +0 -69
  1360. package/src/utils/chainEventHandlers.test.ts +0 -39
  1361. package/src/utils/characters.test.ts +0 -141
  1362. package/src/utils/color.test.ts +0 -233
  1363. package/src/utils/composeEventHandlers.test.ts +0 -71
  1364. package/src/utils/customPropertyArmLayering.test.ts +0 -131
  1365. package/src/utils/dateParser.test.ts +0 -402
  1366. package/src/utils/dateTypes.test.ts +0 -40
  1367. package/src/utils/devWarning.test.ts +0 -56
  1368. package/src/utils/edgeAutoscroll.test.ts +0 -192
  1369. package/src/utils/focusReturn.test.ts +0 -119
  1370. package/src/utils/getKey.test.ts +0 -25
  1371. package/src/utils/getNodeText.test.ts +0 -24
  1372. package/src/utils/hitArea.test.ts +0 -628
  1373. package/src/utils/ime.test.ts +0 -32
  1374. package/src/utils/inputAria.test.ts +0 -39
  1375. package/src/utils/interactionModality.test.ts +0 -144
  1376. package/src/utils/isApplePlatform.test.ts +0 -61
  1377. package/src/utils/mergeProps.test.ts +0 -166
  1378. package/src/utils/mergeRefs.test.ts +0 -48
  1379. package/src/utils/parseStyleKey.test.ts +0 -56
  1380. package/src/utils/plainDate.test.ts +0 -938
  1381. package/src/utils/presentation.test.ts +0 -380
  1382. package/src/utils/pressFeedback.test.ts +0 -1012
  1383. package/src/utils/pressGesture.test.ts +0 -332
  1384. package/src/utils/pressPaintPriority.test.ts +0 -80
  1385. package/src/utils/pressPaintSource.test.ts +0 -65
  1386. package/src/utils/pressableCoverage.test.ts +0 -192
  1387. package/src/utils/primaryClick.test.ts +0 -38
  1388. package/src/utils/safeUrl.test.ts +0 -101
  1389. package/src/utils/sharedMutationObserver.test.ts +0 -106
  1390. package/src/utils/sharedResizeObserver.test.ts +0 -283
  1391. package/src/utils/themeProps.test.ts +0 -62
  1392. package/src/utils/timeFormat.test.ts +0 -204
  1393. package/src/utils/timeParser.test.ts +0 -295
  1394. package/src/utils/touchPresentation.test.ts +0 -462
  1395. package/src/utils/unicodeDigits.test.ts +0 -44
@@ -1,2948 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file DateInputTouch.test.tsx
5
- * @input Uses vitest, @testing-library/react, DateInput and its helpers
6
- * @output Behavior coverage for the responsive date picker
7
- * @position Test file for /packages/core/src/DateInput/
8
- *
9
- * What jsdom can and cannot see here matters, and the split is deliberate:
10
- *
11
- * - The month math is pure, so it is tested directly and exhaustively.
12
- * - Which surface is chosen, and the field contract each one honors, are
13
- * tested through the DOM with `matchMedia` stubbed per test.
14
- * - Snapping, momentum and the scroll-driven falloff are CSS the browser
15
- * resolves and jsdom does not implement at all. Those are asserted on the
16
- * style DEFINITION instead (the last describe block), which at least fails
17
- * loudly if someone deletes the property; the real verification is a
18
- * browser, and the values it produced are recorded in the doc file.
19
- */
20
-
21
- import {
22
- describe,
23
- it,
24
- expect,
25
- vi,
26
- beforeAll,
27
- afterAll,
28
- beforeEach,
29
- afterEach,
30
- } from 'vitest';
31
- import {
32
- act,
33
- render,
34
- screen,
35
- fireEvent,
36
- within,
37
- waitFor,
38
- } from '@testing-library/react';
39
- import {readFileSync} from 'node:fs';
40
- import path from 'node:path';
41
- import {fileURLToPath} from 'node:url';
42
- import {useState} from 'react';
43
- import type {ISODateString} from '../utils';
44
- import {InputGroup} from '../InputGroup';
45
- import {InternationalizationProvider} from '../i18n';
46
- import {DateInput} from './DateInput';
47
- import {
48
- toMonthIndex,
49
- monthIndexOf,
50
- fromMonthIndex,
51
- clampIndex,
52
- rowAtScrollOffset,
53
- scrollOffsetForRow,
54
- paneWindow,
55
- rowsIn,
56
- DEFAULT_MONTH_REACH,
57
- } from './monthGeometry';
58
- import {SWIPE_DISTANCE} from './useOwnScrollGesture';
59
- import {DRAG_SLOP} from './usePointerDragScroll';
60
- import {SCROLL_QUIET_MS} from './useScrollSettle';
61
-
62
- // ---------------------------------------------------------------------------
63
- // jsdom scaffolding
64
- // ---------------------------------------------------------------------------
65
-
66
- class MockResizeObserver {
67
- observe() {}
68
- unobserve() {}
69
- disconnect() {}
70
- }
71
-
72
- /** An arbitrary pane size for the pure geometry tests. */
73
- const PANE = 264;
74
- /**
75
- * What the month scrollport measures as. The scroller pages along the INLINE
76
- * axis, so its pane size is a WIDTH — a phone's worth, since the value only
77
- * has to be non-zero and consistent for the virtualization to mount panes.
78
- */
79
- const SCROLLPORT_WIDTH = 360;
80
-
81
- /** Matches the repo-wide setup polyfill, so hover-gated behavior still works. */
82
- const HOVER_CAPABLE = /\(\s*hover\s*:\s*hover\s*\)/;
83
-
84
- /**
85
- * Answer media queries the way a given device would.
86
- *
87
- * Width queries are answered HONESTLY against `width`, so a width bound
88
- * creeping back into the surface switch fails a test rather than passing
89
- * silently on a stub that ignores it.
90
- */
91
- function stubMedia({
92
- pointer,
93
- anyPointer,
94
- width,
95
- }: {
96
- pointer: 'coarse' | 'fine';
97
- anyPointer?: 'coarse' | 'fine';
98
- width: number;
99
- }): void {
100
- vi.stubGlobal('matchMedia', (query: string) => {
101
- let matches: boolean;
102
- const maxWidth = /\(\s*max-width:\s*(\d+)px\s*\)/.exec(query);
103
- const minWidth = /\(\s*min-width:\s*(\d+)px\s*\)/.exec(query);
104
- if (/any-pointer:\s*coarse/.test(query)) {
105
- matches = (anyPointer ?? pointer) === 'coarse';
106
- } else if (/pointer:\s*coarse/.test(query)) {
107
- matches = pointer === 'coarse';
108
- } else if (/pointer:\s*fine/.test(query)) {
109
- matches = pointer === 'fine';
110
- } else if (maxWidth) {
111
- matches = width <= Number(maxWidth[1]);
112
- } else if (minWidth) {
113
- matches = width >= Number(minWidth[1]);
114
- } else {
115
- matches = HOVER_CAPABLE.test(query);
116
- }
117
- // A compound query is only true when every part of it is.
118
- if (matches && maxWidth && query.includes('pointer:')) {
119
- matches = width <= Number(maxWidth[1]);
120
- }
121
- return {
122
- matches,
123
- media: query,
124
- onchange: null,
125
- addListener: () => {},
126
- removeListener: () => {},
127
- addEventListener: () => {},
128
- removeEventListener: () => {},
129
- dispatchEvent: () => false,
130
- };
131
- });
132
- }
133
-
134
- function setViewport(kind: 'mobile' | 'desktop'): void {
135
- stubMedia(
136
- kind === 'mobile'
137
- ? {pointer: 'coarse', width: 393}
138
- : {pointer: 'fine', width: 1280},
139
- );
140
- }
141
-
142
- /**
143
- * Run `fn` with the month scrollport reporting the height its CSS gives it.
144
- *
145
- * jsdom lays nothing out, so `clientHeight` is 0 and the scroller mounts no
146
- * panes at all. The override is installed only around the work that needs it
147
- * and torn down immediately: a getter left on `HTMLElement.prototype` makes
148
- * EVERY later DOM read take a slow path, measured at ~2.4s per test here.
149
- */
150
- /** One animation frame, for the rAF-throttled scroll handlers. */
151
- async function frame(): Promise<void> {
152
- return new Promise(resolve => requestAnimationFrame(() => resolve()));
153
- }
154
-
155
- function withLayout<T>(fn: () => T): T {
156
- // jsdom defines clientWidth on Element.prototype; shadowing it on
157
- // HTMLElement.prototype and deleting the shadow afterwards restores the
158
- // original getter without having to copy its descriptor.
159
- Object.defineProperty(HTMLElement.prototype, 'clientWidth', {
160
- configurable: true,
161
- get(this: HTMLElement) {
162
- return this.dataset?.scroller === 'months' ? SCROLLPORT_WIDTH : 0;
163
- },
164
- });
165
- try {
166
- return fn();
167
- } finally {
168
- delete (HTMLElement.prototype as {clientWidth?: unknown}).clientWidth;
169
- }
170
- }
171
-
172
- beforeAll(() => {
173
- Element.prototype.scrollTo = vi.fn();
174
- // jsdom's <dialog> implements none of these.
175
- HTMLDialogElement.prototype.showModal = vi.fn(function (
176
- this: HTMLDialogElement,
177
- ) {
178
- this.open = true;
179
- });
180
- HTMLDialogElement.prototype.show = vi.fn(function (this: HTMLDialogElement) {
181
- this.open = true;
182
- });
183
- HTMLDialogElement.prototype.close = vi.fn(function (this: HTMLDialogElement) {
184
- this.open = false;
185
- });
186
- });
187
-
188
- afterAll(() => {
189
- vi.useRealTimers();
190
- });
191
-
192
- beforeEach(() => {
193
- vi.setSystemTime(new Date(2026, 2, 15)); // 15 March 2026, local
194
- // Re-stubbed per test, not once at module scope: the afterEach below clears
195
- // every stub, and matchMedia has to be re-pointed for each test anyway.
196
- vi.stubGlobal('ResizeObserver', MockResizeObserver);
197
- setViewport('mobile');
198
- });
199
-
200
- afterEach(() => {
201
- vi.unstubAllGlobals();
202
- });
203
-
204
- /**
205
- * Bounded by default. Unbounded, the scroller mounts seven month panes — 294
206
- * day buttons — for every render, and under jsdom with StyleX's dev runtime
207
- * that is seconds a test. Three months exercises the same paths; the tests
208
- * that are actually about the range pass their own.
209
- */
210
- const DEFAULT_RANGE = {min: '2026-02-01', max: '2026-04-30'} as const;
211
-
212
- function Controlled({
213
- initial,
214
- onChange,
215
- ...props
216
- }: {initial?: ISODateString} & Partial<
217
- React.ComponentProps<typeof DateInput>
218
- >) {
219
- const [value, setValue] = useState<ISODateString | undefined>(initial);
220
- return (
221
- <DateInput
222
- label="Event date"
223
- // This suite is about Astryx's own touch surface, which is now opt-out:
224
- // `nativePicker` defaults to 'touch', so a coarse pointer gets the
225
- // platform's picker unless a field says otherwise. Tests that assert on
226
- // the surface selection itself pass their own value over this.
227
- nativePicker="never"
228
- {...DEFAULT_RANGE}
229
- {...props}
230
- value={value}
231
- // Composed, not overridden: spreading a test's `onChange` over this one
232
- // used to replace the state setter, so the field silently stopped
233
- // updating in exactly the tests that were watching it most closely.
234
- onChange={next => {
235
- setValue(next);
236
- onChange?.(next);
237
- }}
238
- />
239
- );
240
- }
241
-
242
- /** The closed field — a real input on both surfaces. */
243
- const field = (): HTMLInputElement => screen.getByRole('combobox');
244
-
245
- /** Render, then open the picker. Both mount panes, so both need the layout. */
246
- function renderAndOpen(
247
- ui: React.ReactElement = <Controlled initial="2026-03-21" />,
248
- ): void {
249
- withLayout(() => {
250
- render(ui);
251
- fireEvent.click(field());
252
- });
253
- }
254
-
255
- /**
256
- * One month's grid. Panes for neighbouring months are mounted too, so every
257
- * day query has to say which month it means.
258
- */
259
- function pane(label: string): HTMLElement {
260
- const grid = screen
261
- .getAllByRole('grid')
262
- .find(g => g.getAttribute('aria-label') === label);
263
- if (grid == null) {
264
- throw new Error(`no pane for ${label}`);
265
- }
266
- return grid;
267
- }
268
-
269
- /** The decorative weekday row: the only 7-cell aria-hidden block. */
270
- function weekdayRow(): HTMLElement {
271
- const row = [...document.querySelectorAll('[aria-hidden="true"]')].find(
272
- el => el.children.length === 7,
273
- );
274
- if (row == null) {
275
- throw new Error('no weekday row');
276
- }
277
- return row as HTMLElement;
278
- }
279
-
280
- describe('DateInput — open-sheet disablement', () => {
281
- it('blocks selection after the open sheet becomes disabled, retaining value and focus', () => {
282
- const onChange = vi.fn();
283
- const changeAction = vi.fn();
284
- const props = {initial: '2026-02-21' as const, onChange, changeAction};
285
- withLayout(() => {
286
- const {rerender} = render(<Controlled {...props} />);
287
- fireEvent.click(field());
288
- const day = within(pane('February 2026')).getByRole('button', {
289
- name: /February 22, 2026/,
290
- });
291
- act(() => day.focus());
292
- rerender(<Controlled {...props} isDisabled />);
293
- expect(day).toHaveFocus();
294
- fireEvent.click(day);
295
-
296
- expect(onChange).not.toHaveBeenCalled();
297
- expect(changeAction).not.toHaveBeenCalled();
298
- expect(field()).toHaveValue('February 21, 2026');
299
- expect(field()).toHaveAttribute('aria-expanded', 'true');
300
- expect(day).toHaveFocus();
301
- expect(
302
- document.querySelector('[data-title="month-year"]'),
303
- ).toHaveTextContent('February 2026');
304
-
305
- rerender(<Controlled {...props} />);
306
- fireEvent.click(day);
307
- expect(onChange).toHaveBeenCalledExactlyOnceWith('2026-02-22');
308
- expect(changeAction).toHaveBeenCalledExactlyOnceWith('2026-02-22');
309
- expect(field()).toHaveAttribute('aria-expanded', 'true');
310
- });
311
- });
312
-
313
- it('blocks disabled reset but preserves busy-only month navigation', () => {
314
- const onChange = vi.fn();
315
- const changeAction = vi.fn();
316
- const props = {initial: '2026-02-21' as const, onChange, changeAction};
317
- withLayout(() => {
318
- const {rerender} = render(<Controlled {...props} />);
319
- fireEvent.click(field());
320
- const reset = screen.getByRole('button', {name: 'Reset'});
321
- act(() => reset.focus());
322
- rerender(<Controlled {...props} isDisabled />);
323
- expect(reset).toHaveFocus();
324
- fireEvent.click(reset);
325
-
326
- expect(onChange).not.toHaveBeenCalled();
327
- expect(changeAction).not.toHaveBeenCalled();
328
- expect(field()).toHaveValue('February 21, 2026');
329
- expect(field()).toHaveAttribute('aria-expanded', 'true');
330
- expect(reset).toHaveFocus();
331
- expect(
332
- document.querySelector('[data-title="month-year"]'),
333
- ).toHaveTextContent('February 2026');
334
-
335
- rerender(<Controlled {...props} isLoading />);
336
- fireEvent.click(reset);
337
- expect(onChange).not.toHaveBeenCalled();
338
- expect(changeAction).not.toHaveBeenCalled();
339
- expect(field()).toHaveValue('February 21, 2026');
340
- expect(field()).toHaveAttribute('aria-expanded', 'true');
341
- expect(reset).toHaveFocus();
342
- expect(
343
- document.querySelector('[data-title="month-year"]'),
344
- ).toHaveTextContent('March 2026');
345
-
346
- rerender(<Controlled {...props} />);
347
- fireEvent.click(reset);
348
- expect(onChange).toHaveBeenCalledExactlyOnceWith(undefined);
349
- expect(changeAction).toHaveBeenCalledExactlyOnceWith(undefined);
350
- expect(field()).toHaveAttribute('aria-expanded', 'true');
351
- });
352
- });
353
- });
354
-
355
- // ---------------------------------------------------------------------------
356
- // Which surface, and why
357
- // ---------------------------------------------------------------------------
358
-
359
- describe('DateInput — surface selection', () => {
360
- it('hands a touch device to the platform picker by default', () => {
361
- // `nativePicker` defaults to 'touch': the OS draws the picker unless a
362
- // field opts out. Everything else in this file passes 'never', so this is
363
- // the one place the default itself is asserted.
364
- stubMedia({pointer: 'coarse', width: 393});
365
- render(<DateInput label="Event date" onChange={() => {}} />);
366
- const input = document.querySelector('input');
367
- expect(input).toHaveAttribute('type', 'date');
368
- });
369
-
370
- it('switches on the pointer alone, so a tablet gets the picker too', () => {
371
- // `pointer` is the PRIMARY device, which is what makes it the whole test:
372
- // a touchscreen laptop reports `fine` (its trackpad) and keeps the
373
- // typable field, and a narrowed desktop window is still a mouse. A width
374
- // bound would only re-exclude tablets — the clearest case for a thumb
375
- // picker there is — so there deliberately is not one.
376
- //
377
- // An 1194px tablet in landscape: coarse pointer, far wider than any
378
- // handset breakpoint. It answers width queries honestly, so a width bound
379
- // creeping back in would fail here rather than pass silently.
380
- stubMedia({pointer: 'coarse', width: 1194});
381
- render(<Controlled initial="2026-03-21" />);
382
- expect(field()).toHaveAttribute('readonly');
383
- });
384
-
385
- it('keeps the typable field for a touchscreen laptop', () => {
386
- // Touch available, but the trackpad is primary — so `pointer` is `fine`
387
- // and only `any-pointer` is coarse. The keyboard is right there, and
388
- // typing a date beats scrolling to it.
389
- stubMedia({pointer: 'fine', anyPointer: 'coarse', width: 1366});
390
- render(<Controlled initial="2026-03-21" />);
391
- expect(field()).not.toHaveAttribute('readonly');
392
- });
393
-
394
- it('keeps the typable field in a narrowed desktop window', () => {
395
- stubMedia({pointer: 'fine', width: 500});
396
- render(<Controlled initial="2026-03-21" />);
397
- expect(field()).not.toHaveAttribute('readonly');
398
- });
399
-
400
- it('renders the desktop DateInput when the query does not match', () => {
401
- setViewport('desktop');
402
- render(<Controlled initial="2026-03-21" />);
403
- // The desktop control is a text field you can type into...
404
- expect(field()).not.toHaveAttribute('readonly');
405
- expect(field()).not.toHaveAttribute('inputmode');
406
- // ...and it opens a popover, not a sheet.
407
- expect(document.querySelector('dialog')).toBeNull();
408
- });
409
-
410
- it('accepts typed input on the desktop surface', () => {
411
- setViewport('desktop');
412
- const onChange = vi.fn();
413
- render(
414
- <DateInput
415
- nativePicker="never"
416
- label="Event date"
417
- onChange={onChange}
418
- min={undefined}
419
- />,
420
- );
421
- fireEvent.change(field(), {target: {value: '2026-03-25'}});
422
- expect(onChange).toHaveBeenCalledWith('2026-03-25');
423
- });
424
-
425
- it('renders the touch field when the query matches', () => {
426
- render(<Controlled initial="2026-03-21" />);
427
- const input = field();
428
- // Still an input — same element, same role, so `ref` stays honest and the
429
- // label associates natively — but the picker is the only way to change it.
430
- expect(input.tagName).toBe('INPUT');
431
- expect(input).toHaveAttribute('readonly');
432
- // What actually keeps the virtual keyboard from covering the sheet.
433
- expect(input).toHaveAttribute('inputmode', 'none');
434
- });
435
-
436
- it('updates the field and open picker when the provider locale changes', () => {
437
- const renderDateInput = (locale: 'en-US' | 'es-ES') => (
438
- <InternationalizationProvider locale={locale}>
439
- <Controlled initial="2026-03-21" />
440
- </InternationalizationProvider>
441
- );
442
-
443
- withLayout(() => {
444
- const {rerender} = render(renderDateInput('en-US'));
445
- expect(field()).toHaveValue('March 21, 2026');
446
-
447
- fireEvent.click(field());
448
- expect(
449
- document.querySelector('[data-title="month-year"]'),
450
- ).toHaveTextContent('March 2026');
451
- expect(pane('March 2026')).toBeInTheDocument();
452
-
453
- rerender(renderDateInput('es-ES'));
454
- expect(field()).toHaveValue('21 de marzo de 2026');
455
- expect(
456
- document.querySelector('[data-title="month-year"]'),
457
- ).toHaveTextContent('marzo de 2026');
458
- expect(pane('marzo de 2026')).toBeInTheDocument();
459
-
460
- const nextButton = document.querySelector<HTMLButtonElement>(
461
- '[data-arrows="months"] button:last-child',
462
- );
463
- expect(nextButton).not.toBeNull();
464
- fireEvent.click(nextButton!);
465
- expect(
466
- document.querySelector('[data-title="month-year"]'),
467
- ).toHaveTextContent('abril de 2026');
468
- });
469
- });
470
-
471
- it('forwards ref to the input on both surfaces', () => {
472
- const desktopRef = {current: null as HTMLInputElement | null};
473
- setViewport('desktop');
474
- const {unmount} = render(
475
- <Controlled initial="2026-03-21" ref={desktopRef} />,
476
- );
477
- expect(desktopRef.current).toBeInstanceOf(HTMLInputElement);
478
- unmount();
479
-
480
- const mobileRef = {current: null as HTMLInputElement | null};
481
- setViewport('mobile');
482
- render(<Controlled initial="2026-03-21" ref={mobileRef} />);
483
- expect(mobileRef.current).toBeInstanceOf(HTMLInputElement);
484
- });
485
- });
486
-
487
- // ---------------------------------------------------------------------------
488
- // The field contract, honored identically on the touch surface
489
- // ---------------------------------------------------------------------------
490
-
491
- describe('DateInput — field parity', () => {
492
- it('shows a placeholder until a date is chosen, then the formatted value', () => {
493
- const {rerender} = render(
494
- <DateInput nativePicker="never" label="Ship date" onChange={() => {}} />,
495
- );
496
- expect(field()).toHaveValue('');
497
- expect(field()).toHaveAttribute('placeholder', 'Select a date');
498
- rerender(
499
- <DateInput
500
- nativePicker="never"
501
- label="Ship date"
502
- value="2026-03-21"
503
- onChange={() => {}}
504
- />,
505
- );
506
- expect(field()).toHaveValue('March 21, 2026');
507
- });
508
-
509
- it('honors a named format', () => {
510
- render(
511
- <DateInput
512
- nativePicker="never"
513
- label="Ship date"
514
- value="2026-03-21"
515
- format="system_date"
516
- onChange={() => {}}
517
- />,
518
- );
519
- expect(field()).toHaveValue('2026-03-21');
520
- });
521
-
522
- it('honors a function format', () => {
523
- render(
524
- <DateInput
525
- nativePicker="never"
526
- label="Ship date"
527
- value="2026-03-21"
528
- format={iso => `ISO:${iso}`}
529
- onChange={() => {}}
530
- />,
531
- );
532
- expect(field()).toHaveValue('ISO:2026-03-21');
533
- });
534
-
535
- it('honors a custom placeholder', () => {
536
- render(
537
- <DateInput
538
- nativePicker="never"
539
- label="Ship date"
540
- placeholder="Pick a day"
541
- onChange={() => {}}
542
- />,
543
- );
544
- expect(field()).toHaveAttribute('placeholder', 'Pick a day');
545
- });
546
-
547
- it('clears from the field', () => {
548
- const onChange = vi.fn();
549
- render(
550
- <DateInput
551
- nativePicker="never"
552
- label="Ship date"
553
- value="2026-03-21"
554
- hasClear
555
- onChange={onChange}
556
- />,
557
- );
558
- fireEvent.click(screen.getByRole('button', {name: /Clear Ship date/}));
559
- expect(onChange).toHaveBeenCalledWith(undefined);
560
- });
561
-
562
- it('returns focus to the field without letting the page scroll', async () => {
563
- // Clearing unmounts the clear button, and focusing another element in the
564
- // same task as that unmount makes iOS Safari scroll the document to the
565
- // top. Measured on the iOS 26 simulator against the live docsite, field at
566
- // scrollY 2055: synchronous focus lands at 0, deferred focus stays at
567
- // 2055. `preventScroll` alone does not fix it, so both halves are
568
- // asserted: the focus is deferred past the unmount, and it is passed
569
- // preventScroll. jsdom implements no scrolling, so the guard is asserted
570
- // at the call.
571
- vi.useFakeTimers();
572
- try {
573
- render(
574
- <DateInput
575
- nativePicker="never"
576
- label="Ship date"
577
- value="2026-03-21"
578
- hasClear
579
- onChange={() => {}}
580
- />,
581
- );
582
- const input = field();
583
- const focus = vi.spyOn(input, 'focus');
584
-
585
- fireEvent.click(screen.getByRole('button', {name: /Clear Ship date/}));
586
-
587
- // Not synchronous — that is the whole point.
588
- expect(focus).not.toHaveBeenCalled();
589
-
590
- vi.runAllTimers();
591
-
592
- expect(focus).toHaveBeenCalledWith({preventScroll: true});
593
- focus.mockRestore();
594
- } finally {
595
- vi.useRealTimers();
596
- }
597
- });
598
-
599
- it('does not open the picker until the field is tapped', () => {
600
- withLayout(() => {
601
- render(
602
- <DateInput
603
- nativePicker="never"
604
- label="Ship date"
605
- onChange={() => {}}
606
- />,
607
- );
608
- expect(field()).toHaveAttribute('aria-expanded', 'false');
609
- expect(screen.queryByRole('grid')).not.toBeInTheDocument();
610
- fireEvent.click(field());
611
- expect(field()).toHaveAttribute('aria-expanded', 'true');
612
- expect(screen.getAllByRole('grid').length).toBeGreaterThan(0);
613
- });
614
- });
615
-
616
- it('opens from the keyboard, APG combobox style', () => {
617
- withLayout(() => {
618
- render(<Controlled initial="2026-03-21" />);
619
- fireEvent.keyDown(field(), {key: 'ArrowDown'});
620
- expect(field()).toHaveAttribute('aria-expanded', 'true');
621
- });
622
- });
623
-
624
- it('is not openable while disabled', () => {
625
- render(
626
- <DateInput
627
- nativePicker="never"
628
- label="Ship date"
629
- isDisabled
630
- onChange={() => {}}
631
- />,
632
- );
633
- // Focusable, never natively disabled (AST-085 FR1), and still inert.
634
- expect(field()).not.toHaveAttribute('disabled');
635
- expect(field()).toHaveAttribute('aria-disabled', 'true');
636
- field().focus();
637
- expect(field()).toHaveFocus();
638
- fireEvent.click(field());
639
- fireEvent.keyDown(field(), {key: 'Enter'});
640
- expect(field()).toHaveAttribute('aria-expanded', 'false');
641
- expect(screen.queryByRole('grid')).not.toBeInTheDocument();
642
- });
643
-
644
- it('does not call a caller onClick while disabled (AST-085 FR2)', () => {
645
- const onClick = vi.fn();
646
- render(
647
- <DateInput
648
- nativePicker="never"
649
- label="Ship date"
650
- isDisabled
651
- onClick={onClick}
652
- onChange={() => {}}
653
- />,
654
- );
655
- fireEvent.click(field());
656
- expect(onClick).not.toHaveBeenCalled();
657
- });
658
-
659
- it('stays focusable and explains itself when disabled with a reason', () => {
660
- render(
661
- <DateInput
662
- nativePicker="never"
663
- label="Ship date"
664
- isDisabled
665
- disabledMessage="You need the Editor role"
666
- onChange={() => {}}
667
- />,
668
- );
669
- // aria-disabled, not disabled: the reason has to be reachable by keyboard.
670
- expect(field()).not.toBeDisabled();
671
- expect(field()).toHaveAttribute('aria-disabled', 'true');
672
- fireEvent.click(field());
673
- expect(screen.queryByRole('grid')).not.toBeInTheDocument();
674
- });
675
-
676
- it('renders label, description and status through Field', () => {
677
- render(
678
- <DateInput
679
- nativePicker="never"
680
- label="Ship date"
681
- description="When it leaves the warehouse"
682
- status={{type: 'error', message: 'Pick a date'}}
683
- onChange={() => {}}
684
- />,
685
- );
686
- expect(screen.getByText('Ship date')).toBeInTheDocument();
687
- expect(
688
- screen.getByText('When it leaves the warehouse'),
689
- ).toBeInTheDocument();
690
- expect(screen.getByText('Pick a date')).toBeInTheDocument();
691
- expect(field()).toHaveAttribute('aria-invalid', 'true');
692
- expect(field().getAttribute('aria-describedby')).toBeTruthy();
693
- });
694
-
695
- it('marks required for assistive technology', () => {
696
- render(
697
- <DateInput
698
- nativePicker="never"
699
- label="Ship date"
700
- isRequired
701
- onChange={() => {}}
702
- />,
703
- );
704
- expect(field()).toHaveAttribute('aria-required', 'true');
705
- });
706
-
707
- it('associates the label natively, so the field is named without ARIA', () => {
708
- render(
709
- <DateInput nativePicker="never" label="Ship date" onChange={() => {}} />,
710
- );
711
- expect(screen.getByLabelText('Ship date')).toBe(field());
712
- });
713
-
714
- it('runs changeAction and shows a busy state', async () => {
715
- let resolve: () => void = () => {};
716
- const changeAction = vi.fn(
717
- async () =>
718
- new Promise<void>(r => {
719
- resolve = r;
720
- }),
721
- );
722
- withLayout(() => {
723
- render(
724
- <DateInput
725
- nativePicker="never"
726
- label="Ship date"
727
- value="2026-03-10"
728
- min="2026-03-01"
729
- max="2026-03-31"
730
- onChange={() => {}}
731
- changeAction={changeAction}
732
- />,
733
- );
734
- fireEvent.click(field());
735
- fireEvent.click(
736
- within(pane('March 2026')).getByRole('button', {
737
- name: /March 25, 2026/,
738
- }),
739
- );
740
- });
741
- expect(changeAction).toHaveBeenCalledWith('2026-03-25');
742
- resolve();
743
- });
744
-
745
- it('announces a busy start from a pick inside the open sheet, not behind it (spec:AST-086 FR8, Platform support)', async () => {
746
- // The stubbed showModal() opens the dialog but jsdom cannot match
747
- // `:modal`; stand in for the sheet the browser presents modally.
748
- const matches = Element.prototype.matches;
749
- const modalSpy = vi
750
- .spyOn(Element.prototype, 'matches')
751
- .mockImplementation(function (this: Element, selector: string) {
752
- return selector === ':modal'
753
- ? this instanceof HTMLDialogElement && this.open
754
- : matches.call(this, selector);
755
- });
756
- const changeAction = vi.fn(async () => new Promise<void>(() => {}));
757
- withLayout(() => {
758
- render(
759
- <DateInput
760
- nativePicker="never"
761
- label="Ship date"
762
- value="2026-03-10"
763
- min="2026-03-01"
764
- max="2026-03-31"
765
- onChange={() => {}}
766
- changeAction={changeAction}
767
- />,
768
- );
769
- fireEvent.click(field());
770
- fireEvent.click(
771
- within(pane('March 2026')).getByRole('button', {
772
- name: /March 25, 2026/,
773
- }),
774
- );
775
- });
776
- const sheet = document.querySelector('dialog[open]');
777
- expect(sheet).not.toBeNull();
778
- await waitFor(() => {
779
- expect(
780
- sheet!.querySelector('[data-astryx-modal-live-region="polite"]'),
781
- ).toHaveTextContent('Loading');
782
- });
783
- expect(
784
- document.querySelector('[data-astryx-live-region="polite"]')
785
- ?.textContent ?? '',
786
- ).toBe('');
787
- modalSpy.mockRestore();
788
- });
789
-
790
- it('drops the Field wrapper inside an InputGroup', () => {
791
- render(
792
- <InputGroup label="Range">
793
- <DateInput nativePicker="never" label="Start" onChange={() => {}} />
794
- </InputGroup>,
795
- );
796
- // Named by the group label plus its own, the way core's inputs are.
797
- expect(field().getAttribute('aria-labelledby')).toBeTruthy();
798
- });
799
- });
800
-
801
- // ---------------------------------------------------------------------------
802
- // The picker surface
803
- // ---------------------------------------------------------------------------
804
-
805
- describe('DateInput — calendar surface', () => {
806
- it('opens on the selected month', () => {
807
- renderAndOpen();
808
- expect(pane('March 2026')).toBeInTheDocument();
809
- });
810
-
811
- it('opens on the current month when there is no value', () => {
812
- renderAndOpen(<Controlled />);
813
- expect(pane('March 2026')).toBeInTheDocument();
814
- });
815
-
816
- it('mounts a window of months around the visible one, not the whole century', () => {
817
- // The one test that wants the full default reach.
818
- renderAndOpen(
819
- <Controlled initial="2026-03-21" min={undefined} max={undefined} />,
820
- );
821
- const grids = screen.getAllByRole('grid');
822
- expect(grids.length).toBeGreaterThan(1);
823
- expect(grids.length).toBeLessThanOrEqual(7);
824
- expect(grids.map(g => g.getAttribute('aria-label'))).toContain(
825
- 'March 2026',
826
- );
827
- });
828
-
829
- it('renders every month as six rows, so panes cannot differ in height', () => {
830
- renderAndOpen(<Controlled initial="2026-02-01" />);
831
- // February 2026 needs only five rows; the pane still has six.
832
- const february = within(pane('February 2026'));
833
- expect(february.getAllByRole('row')).toHaveLength(6);
834
- expect(february.getAllByRole('gridcell')).toHaveLength(42);
835
- });
836
-
837
- it('commits the tapped day and LEAVES the sheet open', () => {
838
- const onChange = vi.fn();
839
- renderAndOpen(<Controlled initial="2026-03-21" onChange={onChange} />);
840
- fireEvent.click(
841
- within(pane('March 2026')).getByRole('button', {name: /March 25, 2026/}),
842
- );
843
- // The tap is the commit. Staying open lets a mistake be corrected in
844
- // place, and a nearby date reconsidered, without reopening.
845
- expect(onChange).toHaveBeenCalledWith('2026-03-25');
846
- expect(field()).toHaveAttribute('aria-expanded', 'true');
847
- });
848
-
849
- it('lets a second tap correct the first, still without closing', () => {
850
- const onChange = vi.fn();
851
- renderAndOpen(<Controlled initial="2026-03-21" onChange={onChange} />);
852
- const march = within(pane('March 2026'));
853
- fireEvent.click(march.getByRole('button', {name: /March 25, 2026/}));
854
- fireEvent.click(march.getByRole('button', {name: /March 26, 2026/}));
855
- expect(onChange).toHaveBeenLastCalledWith('2026-03-26');
856
- expect(field()).toHaveAttribute('aria-expanded', 'true');
857
- });
858
-
859
- it('Save closes the sheet without touching the value', () => {
860
- const onChange = vi.fn();
861
- renderAndOpen(<Controlled initial="2026-03-21" onChange={onChange} />);
862
- fireEvent.click(
863
- within(pane('March 2026')).getByRole('button', {name: /March 25, 2026/}),
864
- );
865
- onChange.mockClear();
866
- fireEvent.click(screen.getByRole('button', {name: 'Save'}));
867
- expect(field()).toHaveAttribute('aria-expanded', 'false');
868
- // Named for what it means to someone finishing a form, not for what it
869
- // does internally: the value was already committed by the tap that chose
870
- // it, so this fires nothing of its own.
871
- expect(onChange).not.toHaveBeenCalled();
872
- expect(field()).toHaveValue('March 25, 2026');
873
- });
874
-
875
- it('Save closes even with no date chosen, committing nothing', () => {
876
- const onChange = vi.fn();
877
- withLayout(() => {
878
- render(
879
- <DateInput
880
- nativePicker="never"
881
- label="Ship date"
882
- onChange={onChange}
883
- />,
884
- );
885
- fireEvent.click(field());
886
- fireEvent.click(screen.getByRole('button', {name: 'Save'}));
887
- });
888
- expect(field()).toHaveAttribute('aria-expanded', 'false');
889
- expect(onChange).not.toHaveBeenCalled();
890
- });
891
-
892
- it('has Save in the footer and no Today button', () => {
893
- renderAndOpen();
894
- expect(screen.getByRole('button', {name: 'Save'})).toBeInTheDocument();
895
- // "Today" moved the calendar to the current month WITHOUT selecting it,
896
- // which read as broken — the one thing the name promises is the thing it
897
- // did not do. Removed until it can be navigate-or-select on purpose.
898
- expect(screen.queryByRole('button', {name: 'Today'})).toBeNull();
899
- });
900
-
901
- it('marks the selection and today', () => {
902
- renderAndOpen();
903
- const march = within(pane('March 2026'));
904
- expect(
905
- march
906
- .getByRole('button', {name: /March 21, 2026/})
907
- .closest('[role="gridcell"]'),
908
- ).toHaveAttribute('aria-selected', 'true');
909
- expect(march.getByRole('button', {name: /March 15, 2026/})).toHaveAttribute(
910
- 'aria-current',
911
- 'date',
912
- );
913
- });
914
-
915
- it('leaves exactly one day per month tab-reachable', () => {
916
- renderAndOpen();
917
- const tabbable = within(pane('March 2026'))
918
- .getAllByRole('button')
919
- .filter(b => b.getAttribute('tabindex') === '0');
920
- expect(tabbable).toHaveLength(1);
921
- // The selected day, when the month holds one.
922
- expect(tabbable[0]).toHaveAttribute('data-date', '2026-03-21');
923
- });
924
-
925
- it('pages a month with the header arrows', () => {
926
- renderAndOpen();
927
- const header = () =>
928
- document.querySelector<HTMLElement>('[data-title="month-year"]')!;
929
- expect(header()).toHaveTextContent('March 2026');
930
- fireEvent.click(screen.getByRole('button', {name: 'Next month'}));
931
- expect(header()).toHaveTextContent('April 2026');
932
- fireEvent.click(screen.getByRole('button', {name: 'Previous month'}));
933
- expect(header()).toHaveTextContent('March 2026');
934
- });
935
-
936
- /**
937
- * A programmatic scroll must not report its own arrival as if the user had
938
- * scrolled there. Nothing needs the report — whatever asked for the scroll
939
- * already knows the month — and trusting it turns a steer into a cycle.
940
- *
941
- * The case that bit: a wheel commit steers this scroller while it is hidden
942
- * behind the wheels, and a hidden scroller does not reliably stay put. On
943
- * iOS the position is re-snapped when the panel becomes visible again,
944
- * firing a scroll just as the wheels close and reports start being trusted
945
- * again — so the month drifted on the way back to the calendar.
946
- */
947
- it('does not report a scroll it was told to make', async () => {
948
- await withLayout(async () => {
949
- render(
950
- <Controlled initial="2026-03-21" min="2026-01-01" max="2026-12-31" />,
951
- );
952
- fireEvent.click(field());
953
- const header = () =>
954
- document.querySelector<HTMLElement>('[data-title="month-year"]')!;
955
- const scroller = document.querySelector<HTMLElement>(
956
- '[data-scroller="months"]',
957
- )!;
958
-
959
- // An arrow steers it to April. The scroll that lands there is our own
960
- // doing, so whatever it reports must not move the month again.
961
- fireEvent.click(screen.getByRole('button', {name: 'Next month'}));
962
- expect(header()).toHaveTextContent('April 2026');
963
- // Row 3 of a range starting in January is April: the month the steer
964
- // was aiming at, arriving.
965
- scroller.scrollLeft = 3 * SCROLLPORT_WIDTH;
966
- fireEvent.scroll(scroller);
967
- await frame();
968
- expect(header()).toHaveTextContent('April 2026');
969
-
970
- // A finger ends the steering: from here the months it passes are the
971
- // user's, and every one of them counts.
972
- // jsdom has no constructible TouchEvent, and a bare Event carries no
973
- // `touches` — which crashes the gesture hook that reads `touches[0]`.
974
- // That crash is the fake event's fault, not the hook's: a browser's
975
- // touchstart always has the list.
976
- const touchStart = new Event('touchstart', {bubbles: true});
977
- const point = {identifier: 1, clientX: 100, clientY: 100};
978
- Object.defineProperties(touchStart, {
979
- touches: {value: [point]},
980
- targetTouches: {value: [point]},
981
- changedTouches: {value: [point]},
982
- });
983
- scroller.dispatchEvent(touchStart);
984
- scroller.scrollLeft = 5 * SCROLLPORT_WIDTH;
985
- fireEvent.scroll(scroller);
986
- // The report is made from inside a rAF callback, so the state update
987
- // lands outside the act() that fireEvent wraps.
988
- await waitFor(() => expect(header()).toHaveTextContent('June 2026'));
989
- });
990
- });
991
-
992
- /**
993
- * An arrow with nowhere to go is hidden, not greyed. A disabled control
994
- * still says "this is a thing you could do", and at the edge of a range it
995
- * is not — there is no state the user can reach where it becomes
996
- * available, so a permanently greyed chevron just reads as broken.
997
- *
998
- * It keeps its box, though: `visibility: hidden` rather than unmounting,
999
- * so the remaining arrow does not slide sideways as an edge is reached.
1000
- */
1001
- it('hides an arrow at the end of the reachable range', () => {
1002
- renderAndOpen(
1003
- <Controlled initial="2026-03-10" min="2026-03-01" max="2026-03-31" />,
1004
- );
1005
- // Queried by attribute, not by role: `visibility: hidden` is exactly what
1006
- // strips an element of its accessible name, so a role query cannot see
1007
- // these — which is the point of the assertion below.
1008
- const arrow = (name: string) =>
1009
- document.querySelector<HTMLElement>(
1010
- `dialog[open] button[aria-label="${name}"]`,
1011
- );
1012
- for (const name of ['Previous month', 'Next month']) {
1013
- // Still mounted, so the header cannot reflow...
1014
- expect(arrow(name)).toBeInTheDocument();
1015
- expect(arrow(name)).toBeDisabled();
1016
- // ...but gone from the accessibility tree, and so unreachable.
1017
- expect(screen.queryByRole('button', {name})).toBeNull();
1018
- }
1019
- });
1020
-
1021
- it('shows both arrows when there is somewhere to go in each direction', () => {
1022
- renderAndOpen(
1023
- <Controlled initial="2026-03-10" min="2026-01-01" max="2026-12-31" />,
1024
- );
1025
- expect(
1026
- screen.getByRole('button', {name: 'Previous month'}),
1027
- ).toBeInTheDocument();
1028
- expect(
1029
- screen.getByRole('button', {name: 'Next month'}),
1030
- ).toBeInTheDocument();
1031
- });
1032
-
1033
- it('does not change the selection when paging', () => {
1034
- const onChange = vi.fn();
1035
- renderAndOpen(<Controlled initial="2026-03-21" onChange={onChange} />);
1036
- fireEvent.click(screen.getByRole('button', {name: 'Next month'}));
1037
- // Navigating is not selecting — the mistake the old Today button made.
1038
- expect(onChange).not.toHaveBeenCalled();
1039
- expect(field()).toHaveValue('March 21, 2026');
1040
- });
1041
-
1042
- it('disables days outside min/max and refuses to commit them', () => {
1043
- const onChange = vi.fn();
1044
- renderAndOpen(
1045
- <Controlled
1046
- initial="2026-03-10"
1047
- min="2026-03-05"
1048
- max="2026-03-20"
1049
- onChange={onChange}
1050
- />,
1051
- );
1052
- const outOfRange = within(pane('March 2026')).getByRole('button', {
1053
- name: /March 25, 2026/,
1054
- });
1055
- expect(outOfRange).toHaveAttribute('aria-disabled', 'true');
1056
- fireEvent.click(outOfRange);
1057
- expect(onChange).not.toHaveBeenCalled();
1058
- });
1059
-
1060
- it('honors custom date constraints', () => {
1061
- renderAndOpen(
1062
- <Controlled
1063
- initial="2026-03-10"
1064
- dateConstraints={[date => date.getDay() !== 0]}
1065
- />,
1066
- );
1067
- // 2026-03-01 is a Sunday.
1068
- expect(
1069
- within(pane('March 2026')).getByRole('button', {name: /March 1, 2026/}),
1070
- ).toHaveAttribute('aria-disabled', 'true');
1071
- });
1072
-
1073
- /**
1074
- * Spill days, the way the desktop calendar has them.
1075
- *
1076
- * These were left out at first, on the theory that a horizontal scroller
1077
- * would show the same date twice — greyed at the foot of one pane and
1078
- * again at the head of the next. Measured, that cannot happen: both panes
1079
- * are exactly the scrollport wide and share one 7-column grid, so a given
1080
- * weekday column is only ever visible in ONE pane at a time, and a date
1081
- * always sits in its weekday's column. Swept across a full pane boundary
1082
- * in 10% steps — 42 dates on screen throughout, zero duplicated.
1083
- */
1084
- it('renders adjacent-month days, muted, like the desktop calendar', () => {
1085
- renderAndOpen();
1086
- const march = within(pane('March 2026'));
1087
- // Every cell in the 6x7 grid is now a real day.
1088
- expect(march.getAllByRole('gridcell')).toHaveLength(42);
1089
- expect(march.getAllByRole('button')).toHaveLength(42);
1090
- // March 2026 begins on a Sunday, so it spills only forwards.
1091
- expect(
1092
- march.getByRole('button', {name: /April 1, 2026/}),
1093
- ).toBeInTheDocument();
1094
- });
1095
-
1096
- it('spills backwards too, on a month that does not start the week', () => {
1097
- renderAndOpen(
1098
- <Controlled initial="2026-04-15" min="2026-01-01" max="2026-12-31" />,
1099
- );
1100
- const april = within(pane('April 2026'));
1101
- // April 2026 starts on a Wednesday: the first row opens with late March.
1102
- expect(
1103
- april.getByRole('button', {name: /March 29, 2026/}),
1104
- ).toBeInTheDocument();
1105
- expect(april.getAllByRole('button')).toHaveLength(42);
1106
- });
1107
-
1108
- /**
1109
- * A spilled day is shown, not offered.
1110
- *
1111
- * It was pickable at first, on the reasoning that a date you can see is a
1112
- * date you should be able to tap. The desktop calendar disagrees — it makes
1113
- * its own outside days unselectable — and on a pane that IS the month, so
1114
- * does the interaction: committing April 1 from March's pane would move the
1115
- * calendar out from under the thumb that just tapped it. The swipe and the
1116
- * arrows say "next month" without that ambiguity.
1117
- */
1118
- it('does not commit an adjacent-month day', () => {
1119
- const onChange = vi.fn();
1120
- renderAndOpen(<Controlled initial="2026-03-21" onChange={onChange} />);
1121
- const april1 = within(pane('March 2026')).getByRole('button', {
1122
- name: /April 1, 2026/,
1123
- });
1124
- expect(april1).toHaveAttribute('aria-disabled', 'true');
1125
- fireEvent.click(april1);
1126
- expect(onChange).not.toHaveBeenCalled();
1127
- });
1128
-
1129
- /**
1130
- * Being outside is enough on its own — a spill day inside the allowed range
1131
- * is still not a choice. Worth its own case because `isDateDisabled` says
1132
- * nothing about April 1 here, so only the outside test can be what disables
1133
- * it.
1134
- */
1135
- it('disables a spilled day the range would otherwise allow', () => {
1136
- renderAndOpen(
1137
- <Controlled initial="2026-03-21" min="2026-01-01" max="2026-12-31" />,
1138
- );
1139
- const march = within(pane('March 2026'));
1140
- // In range, and in its own pane it is perfectly pickable.
1141
- expect(
1142
- within(pane('April 2026')).getByRole('button', {name: /April 1, 2026/}),
1143
- ).not.toHaveAttribute('aria-disabled');
1144
- // Borrowed by March, it is not.
1145
- expect(march.getByRole('button', {name: /April 1, 2026/})).toHaveAttribute(
1146
- 'aria-disabled',
1147
- 'true',
1148
- );
1149
- });
1150
-
1151
- /**
1152
- * The selection and today's ring belong to the month that owns the date.
1153
- * Calendar guards both on `!isOutside`; without the same guard a date would
1154
- * wear its puck twice, once in its own pane and once in the neighbour that
1155
- * only borrows it.
1156
- */
1157
- it('leaves the puck and the today ring with the month that owns the day', () => {
1158
- renderAndOpen(
1159
- <Controlled initial="2026-04-01" min="2026-01-01" max="2026-12-31" />,
1160
- );
1161
- const spilled = within(pane('March 2026')).getByRole('button', {
1162
- name: /April 1, 2026/,
1163
- });
1164
- const owned = within(pane('April 2026')).getByRole('button', {
1165
- name: /April 1, 2026/,
1166
- });
1167
- // Same date, same label — and only one of them is dressed as selected.
1168
- expect(owned.className).not.toBe(spilled.className);
1169
- expect(
1170
- within(pane('April 2026')).getAllByRole('gridcell', {selected: true}),
1171
- ).toHaveLength(1);
1172
- expect(
1173
- within(pane('March 2026')).queryAllByRole('gridcell', {selected: true}),
1174
- ).toHaveLength(0);
1175
- });
1176
-
1177
- /**
1178
- * A date shown in two panes must not be two tab stops — the pane that owns
1179
- * it, and the neighbour that merely spills it. That holds because
1180
- * `tabbableISO` is resolved per pane and only names dates in that pane's
1181
- * own month, so it is worth pinning: the case below is the one that would
1182
- * break if it ever became global, since April 1 is both the selection and
1183
- * a spill day in March's pane.
1184
- */
1185
- it('gives a spilled date no tab stop in the pane that borrows it', () => {
1186
- renderAndOpen(
1187
- <Controlled initial="2026-04-01" min="2026-01-01" max="2026-12-31" />,
1188
- );
1189
- const tabbableIn = (label: string) =>
1190
- within(pane(label))
1191
- .getAllByRole('button')
1192
- .filter(b => b.getAttribute('tabindex') === '0')
1193
- .map(b => b.getAttribute('aria-label'));
1194
-
1195
- // April owns the date, so its stop is the date itself.
1196
- expect(tabbableIn('April 2026')).toHaveLength(1);
1197
- expect(tabbableIn('April 2026')[0]).toMatch(/April 1, 2026/);
1198
- // March shows the same date, but its stop falls back to a March date —
1199
- // every pane keeps exactly one, and never on a day it does not own.
1200
- expect(tabbableIn('March 2026')).toHaveLength(1);
1201
- expect(tabbableIn('March 2026')[0]).toMatch(/March/);
1202
- });
1203
-
1204
- const weekdayNames = () =>
1205
- [...weekdayRow().children].map(c => c.textContent?.trim());
1206
-
1207
- /**
1208
- * Three letters, where Calendar's own header uses two.
1209
- *
1210
- * The sheet is full width — ~51px a column against Calendar's popover — so
1211
- * there is room for the form people actually read, and a picker driven by
1212
- * thumb should not make anyone decode "Tu" against "Th".
1213
- *
1214
- * Exact equality, not `toHaveTextContent`: that matches substrings, so it
1215
- * passes against "Sun" while asserting "Su" and would not have noticed this
1216
- * change at all.
1217
- */
1218
- it('labels the columns with three-letter weekday names', () => {
1219
- renderAndOpen();
1220
- expect(weekdayNames()).toEqual([
1221
- 'Sun',
1222
- 'Mon',
1223
- 'Tue',
1224
- 'Wed',
1225
- 'Thu',
1226
- 'Fri',
1227
- 'Sat',
1228
- ]);
1229
- });
1230
-
1231
- it('rotates the weekday header with weekStartsOn', () => {
1232
- renderAndOpen(<Controlled initial="2026-03-21" weekStartsOn={1} />);
1233
- expect(weekdayNames()).toEqual([
1234
- 'Mon',
1235
- 'Tue',
1236
- 'Wed',
1237
- 'Thu',
1238
- 'Fri',
1239
- 'Sat',
1240
- 'Sun',
1241
- ]);
1242
- });
1243
-
1244
- it('accepts weekStartsOn as a day name, like Calendar and DateInput', () => {
1245
- renderAndOpen(<Controlled initial="2026-03-21" weekStartsOn="sat" />);
1246
- expect(weekdayNames()[0]).toBe('Sat');
1247
- // The columns rotate with the header, or the grid would be mislabelled.
1248
- expect(
1249
- within(pane('March 2026'))
1250
- .getAllByRole('button')[0]
1251
- .getAttribute('aria-label'),
1252
- ).toMatch(/February 28, 2026|March/);
1253
- });
1254
-
1255
- it('moves keyboard focus by date, across the month boundary', () => {
1256
- renderAndOpen(<Controlled initial="2026-03-31" />);
1257
- const last = within(pane('March 2026')).getByRole('button', {
1258
- name: /March 31, 2026/,
1259
- });
1260
- last.focus();
1261
- fireEvent.keyDown(last, {key: 'ArrowRight'});
1262
- expect(document.activeElement).toHaveAttribute('data-date', '2026-04-01');
1263
- // The real April 1, not its dimmed spill copy at the foot of the March
1264
- // pane (which is disabled and selects nothing).
1265
- expect(pane('April 2026')).toContainElement(
1266
- document.activeElement as HTMLElement,
1267
- );
1268
- expect(document.activeElement).not.toHaveAttribute('aria-disabled');
1269
- });
1270
-
1271
- it('follows the reading direction under RTL: ArrowLeft moves to the next day', () => {
1272
- renderAndOpen(
1273
- <InternationalizationProvider locale="en-US" dir="rtl">
1274
- <Controlled initial="2026-03-10" />
1275
- </InternationalizationProvider>,
1276
- );
1277
- const day = within(pane('March 2026')).getByRole('button', {
1278
- name: /March 10, 2026/,
1279
- });
1280
- day.focus();
1281
- fireEvent.keyDown(day, {key: 'ArrowLeft'});
1282
- expect(document.activeElement).toHaveAttribute('data-date', '2026-03-11');
1283
- fireEvent.keyDown(document.activeElement!, {key: 'ArrowRight'});
1284
- expect(document.activeElement).toHaveAttribute('data-date', '2026-03-10');
1285
- });
1286
-
1287
- it('moves a week at a time with the vertical arrows', () => {
1288
- renderAndOpen(<Controlled initial="2026-03-10" />);
1289
- const day = within(pane('March 2026')).getByRole('button', {
1290
- name: /March 10, 2026/,
1291
- });
1292
- day.focus();
1293
- fireEvent.keyDown(day, {key: 'ArrowDown'});
1294
- expect(document.activeElement).toHaveAttribute('data-date', '2026-03-17');
1295
- });
1296
- });
1297
-
1298
- // ---------------------------------------------------------------------------
1299
- // Resting between two months — the iOS snap failure
1300
- // ---------------------------------------------------------------------------
1301
-
1302
- /**
1303
- * `scroll-snap-type: mandatory` is supposed to make all of this unnecessary,
1304
- * and on a static list it does. This list is virtualized: seven panes exist
1305
- * out of twelve hundred, and THE PANES ARE THE SNAP AREAS — so every month
1306
- * the finger crosses mounts one and unmounts another, mid-fling.
1307
- *
1308
- * iOS scrolls off the main thread. It picks a landing place from the snap
1309
- * points it knows about at the time, and a React re-render that lands after
1310
- * that decision moves them; the scroller comes to rest where no snap point
1311
- * exists any more and nothing re-snaps it. Reported from a device as the
1312
- * calendar sitting between two months with the weekday header still square —
1313
- * which is exactly the shape of it: the grid is not skewed, the scrollport is
1314
- * parked a couple of columns into a pane, so the left of March and the right
1315
- * of April are on screen under one Sun-to-Sat header.
1316
- *
1317
- * Chrome never shows it, because it snaps again after the mutation. jsdom
1318
- * implements no snapping at all, which makes it a fine place to test the
1319
- * correction: the scroller can simply be PUT at a bad offset, exactly as iOS
1320
- * leaves it, and the settle has to fix it.
1321
- */
1322
- describe('DateInput — a rest position between two months', () => {
1323
- /** The row March 2026 occupies in a range that opens in January 2024. */
1324
- const MARCH_2026_ROW = 26;
1325
- const FIVE_YEARS = {min: '2024-01-01', max: '2028-12-31'} as const;
1326
-
1327
- /** A touchstart carrying the list a real one always has. */
1328
- const touchEvent = (type: string) => {
1329
- const event = new Event(type, {bubbles: true});
1330
- const point = {identifier: 1, clientX: 100, clientY: 100};
1331
- Object.defineProperties(event, {
1332
- touches: {value: type === 'touchend' ? [] : [point]},
1333
- targetTouches: {value: type === 'touchend' ? [] : [point]},
1334
- changedTouches: {value: [point]},
1335
- });
1336
- return event;
1337
- };
1338
-
1339
- /** Drag, release, and let the quiet period elapse. */
1340
- const swipeTo = async (scroller: HTMLElement, offset: number) => {
1341
- scroller.dispatchEvent(touchEvent('touchstart'));
1342
- scroller.scrollLeft = offset;
1343
- fireEvent.scroll(scroller);
1344
- scroller.dispatchEvent(touchEvent('touchend'));
1345
- };
1346
-
1347
- const openCalendar = () => {
1348
- render(<Controlled initial="2026-03-21" {...FIVE_YEARS} />);
1349
- fireEvent.click(screen.getByLabelText('Event date'));
1350
- const scroller = document.querySelector<HTMLElement>(
1351
- '[data-scroller="months"]',
1352
- )!;
1353
- const scrollTo = vi.mocked(Element.prototype.scrollTo);
1354
- scrollTo.mockClear();
1355
- return {scroller, scrollTo};
1356
- };
1357
-
1358
- it('puts the calendar back on a pane once the swipe is over', async () => {
1359
- await withLayout(async () => {
1360
- const {scroller, scrollTo} = openCalendar();
1361
- // Two columns in — what the device screenshot showed.
1362
- const stray = Math.round((SCROLLPORT_WIDTH * 2) / 7);
1363
- await swipeTo(scroller, MARCH_2026_ROW * SCROLLPORT_WIDTH + stray);
1364
-
1365
- await waitFor(() =>
1366
- expect(scrollTo).toHaveBeenCalledWith(
1367
- expect.objectContaining({left: MARCH_2026_ROW * SCROLLPORT_WIDTH}),
1368
- ),
1369
- );
1370
- });
1371
- });
1372
-
1373
- it('goes to the nearer pane, not back the way it came', async () => {
1374
- await withLayout(async () => {
1375
- const {scroller, scrollTo} = openCalendar();
1376
- // Three quarters of the way to April: April is the honest answer, and a
1377
- // correction that always rounded down would drag the user backwards.
1378
- await swipeTo(
1379
- scroller,
1380
- MARCH_2026_ROW * SCROLLPORT_WIDTH + SCROLLPORT_WIDTH * 0.75,
1381
- );
1382
-
1383
- await waitFor(() =>
1384
- expect(scrollTo).toHaveBeenCalledWith(
1385
- expect.objectContaining({
1386
- left: (MARCH_2026_ROW + 1) * SCROLLPORT_WIDTH,
1387
- }),
1388
- ),
1389
- );
1390
- });
1391
- });
1392
-
1393
- /**
1394
- * A scroller the browser snapped for itself must be left alone. Correcting
1395
- * it anyway would mean every swipe on Chrome — where snapping works — ended
1396
- * with a second, pointless scroll.
1397
- */
1398
- it('leaves a scroller that snapped properly alone', async () => {
1399
- await withLayout(async () => {
1400
- const {scroller, scrollTo} = openCalendar();
1401
- await swipeTo(scroller, (MARCH_2026_ROW + 2) * SCROLLPORT_WIDTH);
1402
-
1403
- // Long enough that the settle has certainly run.
1404
- await new Promise(resolve => setTimeout(resolve, SCROLL_QUIET_MS * 2));
1405
- expect(scrollTo).not.toHaveBeenCalled();
1406
- });
1407
- });
1408
-
1409
- /**
1410
- * Sub-pixel drift is the browser's own rounding on a fractional viewport,
1411
- * not a failed snap. Correcting it would fire a scroll after every gesture
1412
- * on any device whose width is not a whole number of pixels.
1413
- */
1414
- it('ignores sub-pixel drift', async () => {
1415
- await withLayout(async () => {
1416
- const {scroller, scrollTo} = openCalendar();
1417
- await swipeTo(scroller, MARCH_2026_ROW * SCROLLPORT_WIDTH + 0.4);
1418
-
1419
- await new Promise(resolve => setTimeout(resolve, SCROLL_QUIET_MS * 2));
1420
- expect(scrollTo).not.toHaveBeenCalled();
1421
- });
1422
- });
1423
-
1424
- /**
1425
- * The correction waits for the finger. Firing it mid-drag would fight the
1426
- * hand that is still moving the scroller — the same mistake that made the
1427
- * wheels climb a month at a time on iOS, and the reason `useScrollSettle`
1428
- * waits for a release rather than for quiet alone.
1429
- */
1430
- it('does not correct while the finger is still down', async () => {
1431
- await withLayout(async () => {
1432
- const {scroller, scrollTo} = openCalendar();
1433
- scroller.dispatchEvent(touchEvent('touchstart'));
1434
- scroller.scrollLeft = MARCH_2026_ROW * SCROLLPORT_WIDTH + 120;
1435
- fireEvent.scroll(scroller);
1436
-
1437
- await new Promise(resolve => setTimeout(resolve, SCROLL_QUIET_MS * 2));
1438
- expect(scrollTo).not.toHaveBeenCalled();
1439
-
1440
- // And on release it does the correction it was holding back.
1441
- scroller.dispatchEvent(touchEvent('touchend'));
1442
- await waitFor(() => expect(scrollTo).toHaveBeenCalled());
1443
- });
1444
- });
1445
-
1446
- /**
1447
- * The one that actually reverses a swipe, and the reason the correction
1448
- * re-checks stillness rather than trusting the quiet period.
1449
- *
1450
- * iOS runs its own snap animation for ~150-300ms after the finger lifts,
1451
- * and fires scroll events irregularly while it does — a gap longer than the
1452
- * quiet period is routine in the slow tail. The settle lands mid-animation,
1453
- * reads an offset still travelling toward April, rounds THAT to the nearest
1454
- * pane (still March, since the animation is not yet halfway), and drags the
1455
- * calendar back where it came from. Swipe forward, get pulled backward.
1456
- *
1457
- * Simulated by moving the scroller between the two samples, which is what
1458
- * an animation in flight looks like from here.
1459
- */
1460
- it('does not correct a scroller that is still travelling', async () => {
1461
- await withLayout(async () => {
1462
- const {scroller, scrollTo} = openCalendar();
1463
- // A quarter of the way to April, and still going.
1464
- let offset = MARCH_2026_ROW * SCROLLPORT_WIDTH + SCROLLPORT_WIDTH * 0.25;
1465
- Object.defineProperty(scroller, 'scrollLeft', {
1466
- configurable: true,
1467
- get: () => {
1468
- // Every read advances it, the way an in-flight animation does.
1469
- offset += 8;
1470
- return offset;
1471
- },
1472
- set: (value: number) => {
1473
- offset = value;
1474
- },
1475
- });
1476
-
1477
- await swipeTo(scroller, offset);
1478
- await new Promise(resolve => setTimeout(resolve, SCROLL_QUIET_MS * 3));
1479
- // Nothing. Correcting here would have sent it back to March, undoing a
1480
- // swipe the browser was already completing.
1481
- expect(scrollTo).not.toHaveBeenCalled();
1482
- });
1483
- });
1484
- });
1485
-
1486
- // ---------------------------------------------------------------------------
1487
- // Month / year wheels
1488
- // ---------------------------------------------------------------------------
1489
-
1490
- describe('DateInput — month/year wheels', () => {
1491
- /**
1492
- * The header title. Queried by attribute rather than by role and accessible
1493
- * name: every role query in here walks a tree of ~150 elements and computes
1494
- * a name for each, which dominates the runtime of these tests.
1495
- */
1496
- const title = () =>
1497
- document.querySelector<HTMLElement>('[data-title="month-year"]')!;
1498
-
1499
- const openWheels = () => fireEvent.click(title());
1500
-
1501
- /** The swap panel holding the calendar, or the one holding the wheels. */
1502
- const panel = (which: 'calendar' | 'wheels') =>
1503
- document.querySelector(`[data-panel="${which}"]`)!;
1504
-
1505
- const ONE_YEAR = {min: '2026-01-01', max: '2026-12-31'} as const;
1506
- const FIVE_YEARS = {min: '2024-01-01', max: '2028-12-31'} as const;
1507
-
1508
- it('the header title opens them, and says so', () => {
1509
- renderAndOpen();
1510
- expect(title()).toHaveAttribute('aria-expanded', 'false');
1511
- expect(panel('wheels')).toHaveAttribute('inert');
1512
- expect(panel('calendar')).not.toHaveAttribute('inert');
1513
- openWheels();
1514
- expect(title()).toHaveAttribute('aria-expanded', 'true');
1515
- expect(panel('wheels')).not.toHaveAttribute('inert');
1516
- expect(panel('calendar')).toHaveAttribute('inert');
1517
- expect(screen.getByRole('listbox', {name: 'Month'})).toBeInTheDocument();
1518
- expect(screen.getByRole('listbox', {name: 'Year'})).toBeInTheDocument();
1519
- });
1520
-
1521
- /**
1522
- * The arrows step the calendar, and the calendar is not on screen. They
1523
- * keep their layout box rather than unmounting: they are the tallest thing
1524
- * in the header on a coarse pointer (44px against the title's 36), so
1525
- * dropping them would shorten it and shift the sheet mid-cross-fade.
1526
- */
1527
- it('hides the month arrows while the wheels are up', () => {
1528
- renderAndOpen();
1529
- const arrows = document.querySelector<HTMLElement>(
1530
- '[data-arrows="months"]',
1531
- )!;
1532
- expect(arrows).not.toHaveAttribute('inert');
1533
-
1534
- openWheels();
1535
- expect(arrows).toHaveAttribute('inert');
1536
- // Still in the layout, so the header cannot change height.
1537
- expect(arrows).toBeInTheDocument();
1538
-
1539
- fireEvent.click(title());
1540
- expect(arrows).not.toHaveAttribute('inert');
1541
- });
1542
-
1543
- it('offers twelve months, with the current one selected', () => {
1544
- renderAndOpen();
1545
- openWheels();
1546
- const options = within(
1547
- screen.getByRole('listbox', {name: 'Month'}),
1548
- ).getAllByRole('option');
1549
- expect(options).toHaveLength(12);
1550
- expect(options[2]).toHaveTextContent('March');
1551
- expect(options[2]).toHaveAttribute('aria-selected', 'true');
1552
- });
1553
-
1554
- it('tapping a wheel row moves the calendar to that month', () => {
1555
- renderAndOpen(<Controlled initial="2026-03-21" {...ONE_YEAR} />);
1556
- openWheels();
1557
- fireEvent.click(
1558
- within(screen.getByRole('listbox', {name: 'Month'})).getByText(
1559
- 'September',
1560
- ),
1561
- );
1562
- expect(title()).toHaveTextContent('September 2026');
1563
- });
1564
-
1565
- /**
1566
- * A wheel commit steers the hidden calendar, and the calendar reports the
1567
- * month it lands on. Taking that report back while the wheels are open
1568
- * closes a cycle: commit -> echo -> the echo moves the wheel's selected row
1569
- * -> the wheel is repositioned -> that scroll reads as another commit.
1570
- *
1571
- * Whether it converges comes down to how precisely a browser says
1572
- * "scrolling stopped". Chrome has `scrollend` and settles at once; iOS
1573
- * below Safari 26 has none, and its momentum runs on after the finger
1574
- * lifts, so each lap committed the next month along and the month climbed
1575
- * on its own — reported from a device, invisible in Chrome.
1576
- *
1577
- * Driven here through the calendar's real scroll listener, which needs a
1578
- * measured scrollport (`withLayout`) and a `scrollLeft` far enough to land
1579
- * on another pane, then a frame for the rAF throttle. No timing assumptions:
1580
- * the echo either reaches the month or it does not.
1581
- */
1582
- it('ignores the calendar echo while the wheels are steering it', async () => {
1583
- await withLayout(async () => {
1584
- render(<Controlled initial="2026-03-21" {...FIVE_YEARS} />);
1585
- fireEvent.click(field());
1586
- openWheels();
1587
- fireEvent.click(
1588
- within(screen.getByRole('listbox', {name: 'Month'})).getByText(
1589
- 'January',
1590
- ),
1591
- );
1592
- expect(title()).toHaveTextContent('January 2026');
1593
-
1594
- // The calendar, scrolling to January behind the wheels, reports each
1595
- // month it passes. None of it may move the month the wheels just set.
1596
- const scroller = document.querySelector<HTMLElement>(
1597
- '[data-scroller="months"]',
1598
- )!;
1599
- for (const row of [5, 6, 7, 8]) {
1600
- scroller.scrollLeft = row * SCROLLPORT_WIDTH;
1601
- fireEvent.scroll(scroller);
1602
- await frame();
1603
- }
1604
- expect(title()).toHaveTextContent('January 2026');
1605
- });
1606
- // The other direction — the calendar reporting its month when it IS the
1607
- // surface — is what every test in the calendar-surface block above
1608
- // depends on, so it is covered rather than restated here.
1609
- });
1610
-
1611
- /**
1612
- * The reveal case, which is the one that was reported from a device.
1613
- *
1614
- * A wheel commit steers the calendar while it is hidden behind the wheels,
1615
- * and `visibility: hidden` keeps the layout box but does not guarantee the
1616
- * scroll position survives: iOS re-snaps the scroller when it becomes
1617
- * visible again, and not necessarily onto the pane it was put on. That
1618
- * fires a scroll exactly as the wheels close and reports start being
1619
- * trusted again — so the month drifted on the way back to the dates.
1620
- *
1621
- * Simulated here by moving the hidden scroller somewhere it was never sent,
1622
- * which is what the re-snap amounts to.
1623
- */
1624
- it('holds the month when the hidden calendar is re-snapped on reveal', async () => {
1625
- await withLayout(async () => {
1626
- render(<Controlled initial="2026-03-21" {...FIVE_YEARS} />);
1627
- fireEvent.click(field());
1628
- openWheels();
1629
- fireEvent.click(
1630
- within(screen.getByRole('listbox', {name: 'Month'})).getByText(
1631
- 'January',
1632
- ),
1633
- );
1634
- expect(title()).toHaveTextContent('January 2026');
1635
-
1636
- // iOS moves the hidden scroller off the pane it was steered to.
1637
- const scroller = document.querySelector<HTMLElement>(
1638
- '[data-scroller="months"]',
1639
- )!;
1640
- scroller.scrollLeft += 4 * SCROLLPORT_WIDTH;
1641
-
1642
- // Back to the dates. Two things have to hold: the stray position must
1643
- // not become the month, and the calendar must be put back on the pane
1644
- // the month names — or the header would say January over a different
1645
- // month's grid.
1646
- const scrollTo = vi.mocked(Element.prototype.scrollTo);
1647
- scrollTo.mockClear();
1648
- fireEvent.click(title());
1649
- fireEvent.scroll(scroller);
1650
- await frame();
1651
- await frame();
1652
- expect(title()).toHaveTextContent('January 2026');
1653
-
1654
- // January 2026 is row 24 of a range that starts in January 2024.
1655
- expect(scrollTo).toHaveBeenCalledWith(
1656
- expect.objectContaining({left: 24 * SCROLLPORT_WIDTH}),
1657
- );
1658
- });
1659
- });
1660
-
1661
- /**
1662
- * The wheels are a detour to reach a far month, not a mode. Reopening into
1663
- * them would answer a question the user has not asked yet, and hide the
1664
- * dates they came back for behind another tap.
1665
- */
1666
- it('always reopens on the calendar, whatever was showing last time', () => {
1667
- renderAndOpen();
1668
- openWheels();
1669
- expect(title()).toHaveAttribute('aria-expanded', 'true');
1670
-
1671
- // Dismissed from the wheels, where Done is deliberately not offered —
1672
- // the handle, the scrim and Escape are the ways out.
1673
- fireEvent.keyDown(document.querySelector('dialog[open]')!, {key: 'Escape'});
1674
- expect(field()).toHaveAttribute('aria-expanded', 'false');
1675
-
1676
- fireEvent.click(field());
1677
- expect(title()).toHaveAttribute('aria-expanded', 'false');
1678
- expect(panel('calendar')).not.toHaveAttribute('inert');
1679
- expect(panel('wheels')).toHaveAttribute('inert');
1680
- });
1681
-
1682
- it('the year wheel keeps the month', () => {
1683
- renderAndOpen(<Controlled initial="2026-03-21" {...FIVE_YEARS} />);
1684
- openWheels();
1685
- fireEvent.click(
1686
- within(screen.getByRole('listbox', {name: 'Year'})).getByText('2025'),
1687
- );
1688
- expect(title()).toHaveTextContent('March 2025');
1689
- });
1690
-
1691
- it('is a single tab stop driven by the arrow keys', () => {
1692
- renderAndOpen();
1693
- openWheels();
1694
- const months = screen.getByRole('listbox', {name: 'Month'});
1695
- expect(months).toHaveAttribute('tabindex', '0');
1696
- fireEvent.keyDown(months, {key: 'ArrowDown'});
1697
- expect(title()).toHaveTextContent('April 2026');
1698
- fireEvent.keyDown(months, {key: 'ArrowUp'});
1699
- expect(title()).toHaveTextContent('March 2026');
1700
- });
1701
-
1702
- it('will not commit a row outside min/max', () => {
1703
- renderAndOpen(<Controlled initial="2026-03-10" />);
1704
- openWheels();
1705
- const december = within(screen.getByRole('listbox', {name: 'Month'}))
1706
- .getByText('December')
1707
- .closest('[role="option"]')!;
1708
- expect(december).toHaveAttribute('aria-disabled', 'true');
1709
- fireEvent.click(december);
1710
- expect(title()).toHaveTextContent('March 2026');
1711
- });
1712
-
1713
- it('bounds the year wheel to the reachable range', () => {
1714
- renderAndOpen(
1715
- <Controlled initial="2026-03-10" min="2025-01-01" max="2027-12-31" />,
1716
- );
1717
- openWheels();
1718
- expect(
1719
- within(screen.getByRole('listbox', {name: 'Year'}))
1720
- .getAllByRole('option')
1721
- .map(o => o.textContent),
1722
- ).toEqual(['2025', '2026', '2027']);
1723
- });
1724
-
1725
- it('the title is what closes them again', () => {
1726
- renderAndOpen();
1727
- openWheels();
1728
- expect(title()).toHaveAttribute('aria-expanded', 'true');
1729
- fireEvent.click(title());
1730
- expect(title()).toHaveAttribute('aria-expanded', 'false');
1731
- expect(panel('wheels')).toHaveAttribute('inert');
1732
- });
1733
-
1734
- it('Reset empties the field and brings the calendar home', () => {
1735
- const onChange = vi.fn();
1736
- // Today is 15 March 2026 in these tests; open on a month away from it.
1737
- renderAndOpen(
1738
- <Controlled
1739
- initial="2026-08-21"
1740
- onChange={onChange}
1741
- min="2026-01-01"
1742
- max="2026-12-31"
1743
- />,
1744
- );
1745
- expect(title()).toHaveTextContent('August 2026');
1746
-
1747
- fireEvent.click(screen.getByRole('button', {name: 'Reset'}));
1748
- expect(onChange).toHaveBeenCalledWith(undefined);
1749
- expect(field()).toHaveValue('');
1750
- // Clearing the date and leaving the calendar on the month of the date you
1751
- // just cleared is a half-finished action.
1752
- expect(title()).toHaveTextContent('March 2026');
1753
- // And it does not dismiss — the sheet is still there to pick again.
1754
- expect(field()).toHaveAttribute('aria-expanded', 'true');
1755
- });
1756
-
1757
- /**
1758
- * "If possible" is the whole subtlety. A range can exclude the current
1759
- * month entirely — a booking window that opens next quarter — and there is
1760
- * then no honest month to go to. Clamping would land on the nearest edge
1761
- * and present a different month as though it were today's, so the move is
1762
- * skipped and the calendar stays put. The value clears either way.
1763
- */
1764
- it('clears without moving when the current month is out of range', () => {
1765
- const onChange = vi.fn();
1766
- renderAndOpen(
1767
- <Controlled
1768
- initial="2027-05-10"
1769
- onChange={onChange}
1770
- min="2027-01-01"
1771
- max="2027-12-31"
1772
- />,
1773
- );
1774
- expect(title()).toHaveTextContent('May 2027');
1775
-
1776
- fireEvent.click(screen.getByRole('button', {name: 'Reset'}));
1777
- expect(onChange).toHaveBeenCalledWith(undefined);
1778
- expect(field()).toHaveValue('');
1779
- expect(title()).toHaveTextContent('May 2027');
1780
- });
1781
-
1782
- it('offers Reset only on the calendar, in the header corner', () => {
1783
- renderAndOpen();
1784
- const reset = screen.getByRole('button', {name: 'Reset'});
1785
- // The trailing-most thing in the header, past the arrows — not the
1786
- // footer, which is Save's alone.
1787
- const header = reset.closest('[data-action="reset"]')!.parentElement!;
1788
- expect(header.querySelector('[data-title="month-year"]')).not.toBeNull();
1789
- expect(header.lastElementChild).toHaveAttribute('data-action', 'reset');
1790
-
1791
- openWheels();
1792
- // The wheels choose a month; there is no date there to clear.
1793
- expect(screen.queryByRole('button', {name: 'Reset'})).toBeNull();
1794
- // Hidden, not unmounted, so the header cannot change height mid-swap.
1795
- expect(
1796
- document.querySelector('dialog[open] [data-action="reset"]'),
1797
- ).toHaveAttribute('inert');
1798
- });
1799
-
1800
- it('Save closes the whole picker; Done only leaves the wheels', () => {
1801
- // Two finishes, deliberately different: Save ends the task, Done ends a
1802
- // step. They never appear together — each belongs to the surface it is
1803
- // shown on — so neither has to carry two meanings by position.
1804
- renderAndOpen();
1805
- openWheels();
1806
- fireEvent.click(screen.getByRole('button', {name: 'Done'}));
1807
- // Back on the calendar, still open.
1808
- expect(field()).toHaveAttribute('aria-expanded', 'true');
1809
- expect(title()).toHaveAttribute('aria-expanded', 'false');
1810
-
1811
- fireEvent.click(screen.getByRole('button', {name: 'Save'}));
1812
- expect(field()).toHaveAttribute('aria-expanded', 'false');
1813
- });
1814
-
1815
- /**
1816
- * The two actions share one grid cell and take turns, so the footer is one
1817
- * button tall whichever is showing and the sheet never changes height
1818
- * mid-swap. `inert` is what keeps the hidden one out of the accessibility
1819
- * tree, rather than merely out of sight.
1820
- */
1821
- /**
1822
- * `inert` disables everything INSIDE it, so an inert ancestor is enough to
1823
- * kill a button that looks perfectly fine on its own.
1824
- *
1825
- * This is not hypothetical. The footer kept an `inert` from an earlier
1826
- * version where it was hidden wholesale on the wheels; once the wheels grew
1827
- * their own Done button inside that same footer, the button rendered,
1828
- * looked right, passed every attribute assertion — and did nothing when
1829
- * tapped.
1830
- *
1831
- * Nothing above caught it: `inert` has no behavioural effect in jsdom, so
1832
- * role queries still found the button, and the cell's own attribute was
1833
- * correct. The only honest check is to walk the ancestors, which is what
1834
- * this does.
1835
- */
1836
- it('leaves no inert ancestor over whichever action is showing', () => {
1837
- const inertAncestorsOf = (label: string) => {
1838
- const el = [...document.querySelectorAll('dialog[open] button')].find(
1839
- b => b.textContent?.trim() === label,
1840
- )!;
1841
- const blocking: string[] = [];
1842
- // Start above the button's own cell, which is legitimately inert for
1843
- // the action that is currently hidden.
1844
- let node = el.parentElement?.parentElement ?? null;
1845
- while (node != null && node.tagName !== 'BODY') {
1846
- if (node.hasAttribute('inert')) {
1847
- blocking.push(node.tagName.toLowerCase());
1848
- }
1849
- node = node.parentElement;
1850
- }
1851
- return blocking;
1852
- };
1853
-
1854
- renderAndOpen();
1855
- expect(inertAncestorsOf('Save')).toEqual([]);
1856
- expect(inertAncestorsOf('Reset')).toEqual([]);
1857
-
1858
- openWheels();
1859
- expect(inertAncestorsOf('Done')).toEqual([]);
1860
- });
1861
-
1862
- it('shows exactly one footer action, and only the visible one is reachable', () => {
1863
- renderAndOpen();
1864
- // Queried off the DOM rather than by role: every role-with-name query
1865
- // walks this tree computing accessible names, and there are ~150
1866
- // elements in it. Same reason the title helper above does it this way.
1867
- const button = (label: string) =>
1868
- [...document.querySelectorAll('dialog[open] button')].find(
1869
- el => el.textContent?.trim() === label,
1870
- );
1871
- // Which ANCESTOR carries `inert`, not which parent — the wheels' action
1872
- // sits inside a fading wrapper, and asserting on `parentElement` would
1873
- // pass or fail on that nesting rather than on reachability.
1874
- const isBlocked = (label: string) =>
1875
- button(label)?.closest('[inert]') != null;
1876
-
1877
- expect(isBlocked('Save')).toBe(false);
1878
- expect(isBlocked('Done')).toBe(true);
1879
- expect(screen.queryByRole('button', {name: 'Done'})).toBeNull();
1880
-
1881
- openWheels();
1882
- expect(isBlocked('Save')).toBe(true);
1883
- expect(isBlocked('Done')).toBe(false);
1884
- expect(screen.queryByRole('button', {name: 'Save'})).toBeNull();
1885
-
1886
- // Both stay mounted throughout, which is what keeps the row's height
1887
- // fixed across the swap.
1888
- expect(button('Save')).toBeInTheDocument();
1889
- expect(button('Done')).toBeInTheDocument();
1890
- });
1891
-
1892
- it('the year wheel keeps the month', () => {
1893
- renderAndOpen(<Controlled initial="2026-03-21" {...FIVE_YEARS} />);
1894
- openWheels();
1895
- fireEvent.click(
1896
- within(screen.getByRole('listbox', {name: 'Year'})).getByText('2025'),
1897
- );
1898
- expect(title()).toHaveTextContent('March 2025');
1899
- });
1900
-
1901
- it('is a single tab stop driven by the arrow keys', () => {
1902
- renderAndOpen();
1903
- openWheels();
1904
- const months = screen.getByRole('listbox', {name: 'Month'});
1905
- expect(months).toHaveAttribute('tabindex', '0');
1906
- fireEvent.keyDown(months, {key: 'ArrowDown'});
1907
- expect(title()).toHaveTextContent('April 2026');
1908
- fireEvent.keyDown(months, {key: 'ArrowUp'});
1909
- expect(title()).toHaveTextContent('March 2026');
1910
- });
1911
-
1912
- it('will not commit a row outside min/max', () => {
1913
- renderAndOpen(<Controlled initial="2026-03-10" />);
1914
- openWheels();
1915
- const december = within(screen.getByRole('listbox', {name: 'Month'}))
1916
- .getByText('December')
1917
- .closest('[role="option"]')!;
1918
- expect(december).toHaveAttribute('aria-disabled', 'true');
1919
- fireEvent.click(december);
1920
- expect(title()).toHaveTextContent('March 2026');
1921
- });
1922
-
1923
- it('bounds the year wheel to the reachable range', () => {
1924
- renderAndOpen(
1925
- <Controlled initial="2026-03-10" min="2025-01-01" max="2027-12-31" />,
1926
- );
1927
- openWheels();
1928
- expect(
1929
- within(screen.getByRole('listbox', {name: 'Year'}))
1930
- .getAllByRole('option')
1931
- .map(o => o.textContent),
1932
- ).toEqual(['2025', '2026', '2027']);
1933
- });
1934
-
1935
- it('the title is what closes them again', () => {
1936
- renderAndOpen();
1937
- openWheels();
1938
- expect(title()).toHaveAttribute('aria-expanded', 'true');
1939
- fireEvent.click(title());
1940
- expect(title()).toHaveAttribute('aria-expanded', 'false');
1941
- expect(panel('wheels')).toHaveAttribute('inert');
1942
- });
1943
- });
1944
-
1945
- // ---------------------------------------------------------------------------
1946
- // Gesture ownership — the nested scrollers vs. the sheet's swipe-to-dismiss
1947
- // ---------------------------------------------------------------------------
1948
-
1949
- describe('DateInput — nested scrollers keep their own touch gesture', () => {
1950
- /**
1951
- * Stand-in for `BottomSheet`'s swipe-to-dismiss listener: a NATIVE listener
1952
- * on an ancestor, in the bubble phase, which is exactly how the sheet
1953
- * attaches its own (`addEventListener('touchstart', …, {passive: true})` on
1954
- * its scrolling body). If an event reaches this, the sheet would have read
1955
- * it as a drag.
1956
- */
1957
- function watchAncestor(): {seen: string[]; stop: () => void} {
1958
- const seen: string[] = [];
1959
- const listener = (event: Event) => seen.push(event.type);
1960
- for (const type of ['touchstart', 'touchmove', 'touchend']) {
1961
- document.body.addEventListener(type, listener);
1962
- }
1963
- return {
1964
- seen,
1965
- stop: () => {
1966
- for (const type of ['touchstart', 'touchmove', 'touchend']) {
1967
- document.body.removeEventListener(type, listener);
1968
- }
1969
- },
1970
- };
1971
- }
1972
-
1973
- /**
1974
- * A touch event realistic enough for a real listener to read.
1975
- *
1976
- * jsdom has no constructible `TouchEvent`, and a bare `Event` carries no
1977
- * `changedTouches` — which crashes any handler that reads one, including
1978
- * BottomSheet's (`event.changedTouches[0]`). That crash is an artifact of
1979
- * the fake event, not a bug: a browser's touchstart always has the list.
1980
- * The stand-in below carries it, so an event that DOES reach the sheet
1981
- * exercises the sheet rather than blowing up inside it.
1982
- */
1983
- const touch = (
1984
- el: Element,
1985
- type: string,
1986
- at: {x: number; y: number} = {x: 100, y: 200},
1987
- ) => {
1988
- const event = new Event(type, {
1989
- bubbles: true,
1990
- cancelable: type !== 'touchend',
1991
- });
1992
- const point = {identifier: 1, clientX: at.x, clientY: at.y, target: el};
1993
- Object.defineProperties(event, {
1994
- changedTouches: {value: [point]},
1995
- touches: {value: type === 'touchend' ? [] : [point]},
1996
- targetTouches: {value: type === 'touchend' ? [] : [point]},
1997
- });
1998
- return el.dispatchEvent(event);
1999
- };
2000
-
2001
- /**
2002
- * A whole gesture: down at the origin, drag by (dx, dy), lift. Returns the
2003
- * scroller's stubbed `scrollBy`, which is how the paging fallback shows up
2004
- * (jsdom implements neither scrolling nor `scrollBy`).
2005
- */
2006
- const swipe = (
2007
- el: Element,
2008
- dx: number,
2009
- dy: number,
2010
- {scrollsBy = 0}: {scrollsBy?: number} = {},
2011
- ) => {
2012
- // jsdom has no layout, so `scrollLeft` never moves on its own. Shadow it
2013
- // on this instance for the gesture: a native pan is the browser moving
2014
- // the scroller mid-drag, and watching for exactly that is how the hook
2015
- // knows whether its claim was honoured. Instance-only and deleted after,
2016
- // per the prototype-getter cost noted at the top of this file.
2017
- let offset = 0;
2018
- const scrollBy = vi.fn();
2019
- Object.defineProperties(el, {
2020
- scrollLeft: {
2021
- configurable: true,
2022
- get: () => offset,
2023
- set: (value: number) => {
2024
- offset = value;
2025
- },
2026
- },
2027
- scrollBy: {configurable: true, value: scrollBy},
2028
- });
2029
- const origin = {x: 150, y: 200};
2030
- touch(el, 'touchstart', origin);
2031
- for (const step of [0.5, 1]) {
2032
- if (step === 0.5) {
2033
- offset += scrollsBy;
2034
- }
2035
- touch(el, 'touchmove', {
2036
- x: origin.x + dx * step,
2037
- y: origin.y + dy * step,
2038
- });
2039
- }
2040
- touch(el, 'touchend');
2041
- // @ts-expect-error - removing the shadows restores the prototype's
2042
- delete el.scrollLeft;
2043
- // @ts-expect-error - same
2044
- delete el.scrollBy;
2045
- return scrollBy;
2046
- };
2047
-
2048
- it('lets a touch on the calendar reach the sheet, now that it pages sideways', () => {
2049
- renderAndOpen();
2050
- const scroller = document.querySelector('[data-scroller="months"]')!;
2051
- const ancestor = watchAncestor();
2052
- touch(scroller, 'touchstart');
2053
- touch(scroller, 'touchmove');
2054
- // The calendar used to claim the gesture, because it scrolled vertically
2055
- // and the sheet read every downward drag as a dismiss. Paging sideways
2056
- // removes the conflict: horizontal pans stay here and vertical ones go to
2057
- // the sheet, so a downward drag can go back to meaning swipe-to-dismiss.
2058
- // A move with no direction yet (the same point twice) is nobody's.
2059
- expect(ancestor.seen).toEqual(['touchstart', 'touchmove']);
2060
- ancestor.stop();
2061
- });
2062
-
2063
- it('claims a horizontal drag on the calendar', () => {
2064
- renderAndOpen();
2065
- const scroller = document.querySelector('[data-scroller="months"]')!;
2066
- const ancestor = watchAncestor();
2067
- swipe(scroller, -80, 0);
2068
- // touchstart still propagates — 'inline' cannot know the direction yet —
2069
- // but every move after the axis locks is ours.
2070
- expect(ancestor.seen).not.toContain('touchmove');
2071
- ancestor.stop();
2072
- });
2073
-
2074
- it('leaves a downward drag on the calendar to the sheet', () => {
2075
- renderAndOpen();
2076
- const scroller = document.querySelector('[data-scroller="months"]')!;
2077
- const ancestor = watchAncestor();
2078
- swipe(scroller, 0, 80);
2079
- expect(ancestor.seen).toContain('touchmove');
2080
- ancestor.stop();
2081
- });
2082
-
2083
- it('keeps a diagonal drag, because a thumb arcs as it swipes', () => {
2084
- renderAndOpen();
2085
- const scroller = document.querySelector('[data-scroller="months"]')!;
2086
- const ancestor = watchAncestor();
2087
- // ~50° off horizontal: past the browser's own pan-x cone, still ours.
2088
- swipe(scroller, -60, 72);
2089
- expect(ancestor.seen).not.toContain('touchmove');
2090
- ancestor.stop();
2091
- });
2092
-
2093
- /**
2094
- * The band between our claim and the browser's `pan-x` cone. The sheet has
2095
- * been told to keep off, and the compositor refuses to pan, so without the
2096
- * fallback these gestures would do nothing at all — measured as a dead zone
2097
- * from 45° to 60° on an iPhone 15 profile.
2098
- */
2099
- it('pages a month itself when the browser refuses to pan a claimed swipe', () => {
2100
- renderAndOpen();
2101
- const scroller = document.querySelector('[data-scroller="months"]')!;
2102
-
2103
- const forward = swipe(scroller, -60, 72);
2104
- expect(forward).toHaveBeenCalledTimes(1);
2105
- expect(forward.mock.calls[0][0]).toMatchObject({behavior: 'smooth'});
2106
- // Swiping left advances: the offset moves towards the end of the line.
2107
- expect(forward.mock.calls[0][0].left).toBeGreaterThan(0);
2108
-
2109
- const back = swipe(scroller, 60, 72);
2110
- expect(back.mock.calls[0][0].left).toBeLessThan(0);
2111
- });
2112
-
2113
- it('stays out of the way when the browser did pan', () => {
2114
- renderAndOpen();
2115
- const scroller = document.querySelector('[data-scroller="months"]')!;
2116
- // Native momentum and snapping own this one; a second nudge would fight
2117
- // them. `scrollsBy` stands in for the compositor moving the scroller.
2118
- expect(swipe(scroller, -120, 0, {scrollsBy: 40})).not.toHaveBeenCalled();
2119
- });
2120
-
2121
- it('ignores a claimed gesture too short to be a swipe', () => {
2122
- renderAndOpen();
2123
- const scroller = document.querySelector('[data-scroller="months"]')!;
2124
- expect(swipe(scroller, -SWIPE_DISTANCE + 4, 0)).not.toHaveBeenCalled();
2125
- });
2126
-
2127
- it('never pages from a drag it gave to the sheet', () => {
2128
- renderAndOpen();
2129
- const scroller = document.querySelector('[data-scroller="months"]')!;
2130
- expect(swipe(scroller, -40, 200)).not.toHaveBeenCalled();
2131
- });
2132
-
2133
- it('stops a touch on a wheel from reaching the sheet', () => {
2134
- renderAndOpen();
2135
- fireEvent.click(
2136
- document.querySelector<HTMLElement>('[data-title="month-year"]')!,
2137
- );
2138
- const wheel = screen.getByRole('listbox', {name: 'Month'});
2139
- const ancestor = watchAncestor();
2140
- touch(wheel, 'touchstart');
2141
- touch(wheel, 'touchmove');
2142
- expect(ancestor.seen).not.toContain('touchstart');
2143
- expect(ancestor.seen).not.toContain('touchmove');
2144
- ancestor.stop();
2145
- });
2146
-
2147
- it('lets touchend through, so the sheet can reset its own bookkeeping', () => {
2148
- renderAndOpen();
2149
- const scroller = document.querySelector('[data-scroller="months"]')!;
2150
- const ancestor = watchAncestor();
2151
- touch(scroller, 'touchend');
2152
- expect(ancestor.seen).toContain('touchend');
2153
- ancestor.stop();
2154
- });
2155
-
2156
- it('leaves the rest of the picker to the sheet', () => {
2157
- renderAndOpen();
2158
- // The header is not a scroller: a drag there is the sheet's to interpret,
2159
- // and it is one of the two places a dismiss can still start from.
2160
- const title = document.querySelector<HTMLElement>(
2161
- '[data-title="month-year"]',
2162
- )!;
2163
- const ancestor = watchAncestor();
2164
- touch(title, 'touchstart');
2165
- touch(title, 'touchmove');
2166
- expect(ancestor.seen).toEqual(['touchstart', 'touchmove']);
2167
- ancestor.stop();
2168
- });
2169
-
2170
- it('does not swallow taps — stopPropagation must not reach click', () => {
2171
- const onChange = vi.fn();
2172
- renderAndOpen(<Controlled initial="2026-03-21" onChange={onChange} />);
2173
- fireEvent.click(
2174
- within(pane('March 2026')).getByRole('button', {name: /March 25, 2026/}),
2175
- );
2176
- expect(onChange).toHaveBeenCalledWith('2026-03-25');
2177
- });
2178
- });
2179
-
2180
- // ---------------------------------------------------------------------------
2181
- // Dragging a wheel with a mouse
2182
- // ---------------------------------------------------------------------------
2183
-
2184
- /**
2185
- * A wheel is a scroll container, so a finger pans it for free. A mouse gets
2186
- * nothing: browsers do not drag-scroll an overflow container, so pressing and
2187
- * pulling on the one control shaped like a thing you spin did nothing at all.
2188
- *
2189
- * It matters on desktop specifically because that is where this surface is
2190
- * reviewed, themed and screenshotted.
2191
- */
2192
- describe('DateInput — a mouse can drag a wheel', () => {
2193
- const title = () =>
2194
- document.querySelector<HTMLElement>('[data-title="month-year"]')!;
2195
-
2196
- /** jsdom has no layout: give the rows a height and the wheel a scrollTop. */
2197
- function withWheelLayout<T>(fn: () => T): T {
2198
- Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
2199
- configurable: true,
2200
- get(this: HTMLElement) {
2201
- return this.getAttribute('role') === 'option' ? 28 : 0;
2202
- },
2203
- });
2204
- try {
2205
- return fn();
2206
- } finally {
2207
- // @ts-expect-error - deleting the shadow restores Element's own getter
2208
- delete HTMLElement.prototype.offsetHeight;
2209
- }
2210
- }
2211
-
2212
- const wheel = () => screen.getByRole('listbox', {name: 'Month'});
2213
-
2214
- const pointer = (
2215
- type: string,
2216
- {y = 0, pointerType = 'mouse', button = 0, id = 1} = {},
2217
- ) => {
2218
- const event = new Event(type, {bubbles: true, cancelable: true});
2219
- Object.defineProperties(event, {
2220
- clientY: {value: y},
2221
- pointerId: {value: id},
2222
- pointerType: {value: pointerType},
2223
- button: {value: button},
2224
- });
2225
- return event;
2226
- };
2227
-
2228
- /** Stubs jsdom's missing capture API and records scrollTop as it moves. */
2229
- function trackable(el: HTMLElement) {
2230
- let top = 0;
2231
- Object.defineProperty(el, 'scrollTop', {
2232
- configurable: true,
2233
- get: () => top,
2234
- set: (v: number) => {
2235
- top = v;
2236
- },
2237
- });
2238
- el.setPointerCapture = () => {};
2239
- el.releasePointerCapture = () => {};
2240
- el.hasPointerCapture = () => true;
2241
- el.scrollTo = ((opts: ScrollToOptions) => {
2242
- top = opts.top ?? top;
2243
- }) as HTMLElement['scrollTo'];
2244
- return {
2245
- get scrollTop() {
2246
- return top;
2247
- },
2248
- };
2249
- }
2250
-
2251
- it('scrolls the wheel when a mouse drags it', () => {
2252
- renderAndOpen();
2253
- fireEvent.click(title());
2254
- withWheelLayout(() => {
2255
- const el = wheel();
2256
- const track = trackable(el);
2257
- el.dispatchEvent(pointer('pointerdown', {y: 200}));
2258
- el.dispatchEvent(pointer('pointermove', {y: 160}));
2259
- // Content follows the hand: pulling up scrolls further down the list.
2260
- expect(track.scrollTop).toBe(40);
2261
- el.dispatchEvent(pointer('pointerup', {y: 160}));
2262
- // And the release lands on a row boundary rather than between two.
2263
- expect(track.scrollTop % 28).toBe(0);
2264
- });
2265
- });
2266
-
2267
- it('ignores movement too small to be a drag, so a click is still a click', () => {
2268
- renderAndOpen();
2269
- fireEvent.click(title());
2270
- withWheelLayout(() => {
2271
- const el = wheel();
2272
- const track = trackable(el);
2273
- el.dispatchEvent(pointer('pointerdown', {y: 200}));
2274
- el.dispatchEvent(pointer('pointermove', {y: 200 - (DRAG_SLOP - 1)}));
2275
- expect(track.scrollTop).toBe(0);
2276
- });
2277
- });
2278
-
2279
- it('leaves touch and pen alone — they pan natively, and better', () => {
2280
- renderAndOpen();
2281
- fireEvent.click(title());
2282
- withWheelLayout(() => {
2283
- for (const pointerType of ['touch', 'pen']) {
2284
- const el = wheel();
2285
- const track = trackable(el);
2286
- el.dispatchEvent(pointer('pointerdown', {y: 200, pointerType}));
2287
- el.dispatchEvent(pointer('pointermove', {y: 120, pointerType}));
2288
- expect(track.scrollTop).toBe(0);
2289
- el.dispatchEvent(pointer('pointerup', {y: 120, pointerType}));
2290
- }
2291
- });
2292
- });
2293
-
2294
- it('ignores a secondary button, which belongs to the context menu', () => {
2295
- renderAndOpen();
2296
- fireEvent.click(title());
2297
- withWheelLayout(() => {
2298
- const el = wheel();
2299
- const track = trackable(el);
2300
- el.dispatchEvent(pointer('pointerdown', {y: 200, button: 2}));
2301
- el.dispatchEvent(pointer('pointermove', {y: 120}));
2302
- expect(track.scrollTop).toBe(0);
2303
- });
2304
- });
2305
-
2306
- /**
2307
- * `scroll-snap-type: y mandatory` re-snaps after every scroll, programmatic
2308
- * ones included. Measured with it left on: 7 of 8 five-pixel drag steps were
2309
- * yanked back to a snap position, so the wheel stuck to a row and then
2310
- * jumped a whole one.
2311
- */
2312
- it('suspends snapping for the drag, and restores it after', () => {
2313
- renderAndOpen();
2314
- fireEvent.click(title());
2315
- withWheelLayout(() => {
2316
- const el = wheel();
2317
- trackable(el);
2318
- expect(el.style.scrollSnapType).toBe('');
2319
- el.dispatchEvent(pointer('pointerdown', {y: 200}));
2320
- el.dispatchEvent(pointer('pointermove', {y: 160}));
2321
- expect(el.style.scrollSnapType).toBe('none');
2322
- el.dispatchEvent(pointer('pointerup', {y: 160}));
2323
- el.dispatchEvent(new Event('scrollend'));
2324
- expect(el.style.scrollSnapType).toBe('');
2325
- });
2326
- });
2327
-
2328
- /**
2329
- * Every `scrollTop` the drag assigns ends in its own `scrollend` in
2330
- * Chromium, and the hand can rest with the button still down. Neither is
2331
- * rest: the month the wheel lands on commits at the release, as it does
2332
- * when a finger lifts.
2333
- */
2334
- it('commits nothing while a mouse drag holds the wheel, and commits at the release', () => {
2335
- vi.useFakeTimers();
2336
- try {
2337
- // A year of months, so May is reachable (the default range ends in April).
2338
- renderAndOpen(
2339
- <Controlled initial="2026-03-21" min="2026-01-01" max="2026-12-31" />,
2340
- );
2341
- fireEvent.click(title());
2342
- withWheelLayout(() => {
2343
- const el = wheel();
2344
- trackable(el);
2345
- // March, the committed month, sits under the band.
2346
- el.scrollTop = 2 * 28;
2347
- const selected = () =>
2348
- el.querySelector('[aria-selected="true"]')?.textContent;
2349
- el.dispatchEvent(pointer('pointerdown', {y: 200}));
2350
- // Two rows on: May is under the band.
2351
- el.dispatchEvent(pointer('pointermove', {y: 144}));
2352
- fireEvent.scroll(el);
2353
- el.dispatchEvent(new Event('scrollend'));
2354
- act(() => {
2355
- vi.advanceTimersByTime(SCROLL_QUIET_MS * 2);
2356
- });
2357
- expect(selected()).toBe('March');
2358
- expect(title()).toHaveTextContent('March 2026');
2359
-
2360
- el.dispatchEvent(pointer('pointerup', {y: 144}));
2361
- act(() => {
2362
- vi.advanceTimersByTime(SCROLL_QUIET_MS * 2);
2363
- });
2364
- expect(selected()).toBe('May');
2365
- expect(title()).toHaveTextContent('May 2026');
2366
- });
2367
- } finally {
2368
- vi.useRealTimers();
2369
- }
2370
- });
2371
-
2372
- /**
2373
- * BottomSheet starts its own drag-to-dismiss from a `pointerdown` on its
2374
- * body and CAPTURES the pointer for it, which retargets every later pointer
2375
- * event — including the click. Measured before this: a click on a wheel row
2376
- * that wobbled more than a pixel selected nothing at all.
2377
- */
2378
- it('keeps the press away from the sheet, which would capture the pointer', () => {
2379
- renderAndOpen();
2380
- fireEvent.click(title());
2381
- withWheelLayout(() => {
2382
- const el = wheel();
2383
- trackable(el);
2384
- const seen: string[] = [];
2385
- const listener = (event: Event) => seen.push(event.type);
2386
- document.body.addEventListener('pointerdown', listener);
2387
- el.dispatchEvent(pointer('pointerdown', {y: 200}));
2388
- expect(seen).not.toContain('pointerdown');
2389
- document.body.removeEventListener('pointerdown', listener);
2390
- });
2391
- });
2392
- });
2393
-
2394
- // ---------------------------------------------------------------------------
2395
- // Pure month arithmetic
2396
- // ---------------------------------------------------------------------------
2397
-
2398
- describe('monthGeometry', () => {
2399
- it('round-trips a month through its index', () => {
2400
- for (const [year, month] of [
2401
- [2026, 1],
2402
- [2026, 12],
2403
- [1976, 7],
2404
- [2100, 2],
2405
- ] as const) {
2406
- expect(fromMonthIndex(toMonthIndex(year, month))).toEqual({year, month});
2407
- }
2408
- });
2409
-
2410
- it('orders months across a year boundary', () => {
2411
- expect(toMonthIndex(2027, 1) - toMonthIndex(2026, 12)).toBe(1);
2412
- });
2413
-
2414
- it('reads a month index off a PlainDate, ignoring the day', () => {
2415
- expect(monthIndexOf({year: 2026, month: 3, day: 1})).toBe(
2416
- monthIndexOf({year: 2026, month: 3, day: 31}),
2417
- );
2418
- });
2419
-
2420
- it('clamps to the reachable range', () => {
2421
- expect(clampIndex(5, 10, 20)).toBe(10);
2422
- expect(clampIndex(25, 10, 20)).toBe(20);
2423
- expect(clampIndex(15, 10, 20)).toBe(15);
2424
- });
2425
-
2426
- describe('rowAtScrollOffset', () => {
2427
- it('maps an exact offset to its row', () => {
2428
- expect(rowAtScrollOffset(0, PANE, 100)).toBe(0);
2429
- expect(rowAtScrollOffset(PANE * 7, PANE, 100)).toBe(7);
2430
- });
2431
-
2432
- it('rounds to the nearest row mid-scroll', () => {
2433
- expect(rowAtScrollOffset(PANE * 7 + 10, PANE, 100)).toBe(7);
2434
- expect(rowAtScrollOffset(PANE * 7 - 10, PANE, 100)).toBe(7);
2435
- expect(rowAtScrollOffset(PANE * 6.6, PANE, 100)).toBe(7);
2436
- });
2437
-
2438
- it('never leaves the list', () => {
2439
- expect(rowAtScrollOffset(-500, PANE, 100)).toBe(0);
2440
- expect(rowAtScrollOffset(PANE * 1000, PANE, 100)).toBe(99);
2441
- });
2442
-
2443
- it('is 0 before the pane size is known, rather than dividing by zero', () => {
2444
- expect(rowAtScrollOffset(1234, 0, 100)).toBe(0);
2445
- });
2446
-
2447
- it('reads RTL scrollLeft, which counts down from zero', () => {
2448
- // The spec puts the inline start at 0 and runs negative from there, so
2449
- // an unsigned read would pin an RTL calendar to month zero forever.
2450
- expect(rowAtScrollOffset(-PANE * 7, PANE, 100, true)).toBe(7);
2451
- expect(rowAtScrollOffset(-PANE * 7 - 10, PANE, 100, true)).toBe(7);
2452
- expect(rowAtScrollOffset(0, PANE, 100, true)).toBe(0);
2453
- });
2454
- });
2455
-
2456
- describe('scrollOffsetForRow', () => {
2457
- it('is the inverse of rowAtScrollOffset, in both directions', () => {
2458
- for (const isRTL of [false, true]) {
2459
- for (const row of [0, 1, 7, 99]) {
2460
- const offset = scrollOffsetForRow(row, PANE, isRTL);
2461
- expect(rowAtScrollOffset(offset, PANE, 100, isRTL)).toBe(row);
2462
- }
2463
- }
2464
- });
2465
-
2466
- it('runs negative under RTL', () => {
2467
- expect(scrollOffsetForRow(3, PANE, false)).toBe(PANE * 3);
2468
- expect(scrollOffsetForRow(3, PANE, true)).toBe(-PANE * 3);
2469
- });
2470
- });
2471
-
2472
- describe('paneWindow', () => {
2473
- it('mounts the overscan on both sides', () => {
2474
- expect(paneWindow(50, 100, 3)).toEqual({start: 47, end: 53});
2475
- expect(rowsIn(paneWindow(50, 100, 1))).toEqual([49, 50, 51]);
2476
- });
2477
-
2478
- it('truncates at the ends of the list instead of going out of bounds', () => {
2479
- expect(paneWindow(0, 100, 3)).toEqual({start: 0, end: 3});
2480
- expect(paneWindow(99, 100, 3)).toEqual({start: 96, end: 99});
2481
- });
2482
- });
2483
-
2484
- it('reaches a century in each direction by default', () => {
2485
- expect(DEFAULT_MONTH_REACH).toBe(600);
2486
- });
2487
- });
2488
-
2489
- // ---------------------------------------------------------------------------
2490
- // Styles jsdom cannot resolve — assert the definition, not the effect
2491
- // ---------------------------------------------------------------------------
2492
-
2493
- describe('DateInput — scroll CSS (definition-level)', () => {
2494
- const dir = path.dirname(fileURLToPath(import.meta.url));
2495
- const read = (file: string) => readFileSync(path.join(dir, file), 'utf8');
2496
-
2497
- /**
2498
- * The declarations inside one named style object, comments stripped and
2499
- * trailing commas removed.
2500
- *
2501
- * Crude on purpose — a nested value object contributes its own inner lines
2502
- * too. Every caller looks for a specific property prefix, so the noise is
2503
- * harmless, and a real parser here would be more machinery than the
2504
- * question deserves.
2505
- */
2506
- const declarations = (source: string, object: string) => {
2507
- const open = source.indexOf(` ${object}: {`);
2508
- expect(open).toBeGreaterThan(-1);
2509
- return source
2510
- .slice(open, source.indexOf('\n },', open))
2511
- .replace(/\/\/.*$/gm, '')
2512
- .split('\n')
2513
- .map(line => line.trim())
2514
- .filter(line => line.endsWith(','))
2515
- .map(line => line.slice(0, -1));
2516
- };
2517
-
2518
- /** The same, scoped to one `export const <group> = stylex.create({...})`. */
2519
- const declarationsIn = (source: string, group: string, object: string) => {
2520
- const at = source.indexOf(`export const ${group}`);
2521
- expect(at).toBeGreaterThan(-1);
2522
- return declarations(source.slice(at), object);
2523
- };
2524
-
2525
- it('pages the month scroller one whole pane at a time, horizontally', () => {
2526
- const source = read('MonthScroller.tsx');
2527
- expect(source).toContain("scrollSnapType: 'x mandatory'");
2528
- expect(source).toContain("scrollSnapAlign: 'start'");
2529
- // pan-x is what splits the gesture by axis: horizontal pans stay with the
2530
- // calendar, vertical ones reach the sheet as swipe-to-dismiss. Without it
2531
- // the two would fight again, the way they did when this scrolled down.
2532
- expect(source).toContain("touchAction: 'pan-x'");
2533
- // The pane and the scrollport must come from the same expression, or a
2534
- // pane stops being exactly one screen and every snap offset drifts.
2535
- expect(
2536
- source.match(/blockSize: dateInputTouchGeometry\.paneBlockSize/g),
2537
- ).toHaveLength(2);
2538
- });
2539
-
2540
- it('positions panes with logical properties, so RTL mirrors', () => {
2541
- const source = read('MonthScroller.tsx');
2542
- // Scoped to the style objects: `scrollTo({left})` is the DOM API and is
2543
- // supposed to say left — it is the CSS that must stay logical, because
2544
- // physical `left` would lay the months out identically in both directions
2545
- // while the scroll math mirrored, and the two would disagree under RTL.
2546
- const styles = source
2547
- .slice(
2548
- source.indexOf('const styles = stylex.create('),
2549
- source.indexOf('export interface MonthScrollerHandle'),
2550
- )
2551
- // Comments explain the rule ("insetInlineStart, not left") and would
2552
- // otherwise trip it.
2553
- .replace(/\/\/.*$/gm, '');
2554
- expect(styles).toContain('insetInlineStart');
2555
- expect(styles).not.toMatch(/\bleft:/);
2556
- expect(styles).not.toMatch(/\bright:/);
2557
- });
2558
-
2559
- /**
2560
- * Adjacent days take the desktop calendar's exact treatment, and the point
2561
- * of reading BOTH files is that "exact" stays true if the desktop's changes.
2562
- *
2563
- * Calendar splits the treatment across two style objects — `dayCellTheme`
2564
- * carries the colour, `dayCellStyles` the opacity — so copying it by eye
2565
- * gets you one half and not the other. This pane took only the colour at
2566
- * first, and the spill days came out visibly heavier than the desktop's.
2567
- *
2568
- * Both surfaces now render the same three tiers, measured in a browser on
2569
- * the same story: 23 for a day you can pick, 185 for one the range rules
2570
- * out, 203 for a spilled day (which is outside AND therefore disabled, so
2571
- * it takes both). The enabled-adjacent tier that used to sit at 169 is gone
2572
- * with the tap that produced it.
2573
- */
2574
- it('mutes adjacent days exactly as the desktop calendar does', () => {
2575
- // The desktop's two halves, read out of its own source rather than
2576
- // restated here — so the check follows the desktop if its treatment
2577
- // moves, instead of freezing today's values into this file.
2578
- const desktop = read('../Calendar/styles.ts');
2579
- const structural = declarationsIn(desktop, 'dayCellStyles', 'dayOutside');
2580
- const theme = declarationsIn(desktop, 'dayCellTheme', 'dayOutside');
2581
- // Both halves must have found something, or the parity check below is
2582
- // `arrayContaining([])` and passes on anything at all.
2583
- expect(structural.length).toBeGreaterThan(0);
2584
- expect(theme.length).toBeGreaterThan(0);
2585
-
2586
- // And the touch pane carries all of it, in its one object.
2587
- const touch = declarations(read('MonthScroller.tsx'), 'dayOutside');
2588
- expect(touch).toEqual(expect.arrayContaining([...structural, ...theme]));
2589
- });
2590
-
2591
- /**
2592
- * Disabled days follow the desktop too, and this is the half that actually
2593
- * answered "the disabled dates and the adjacent ones look the same".
2594
- *
2595
- * The desktop FADES a disabled day rather than recolouring it: `opacity:
2596
- * 0.3` over whatever colour the day already had. This pane used to paint a
2597
- * flat `--color-text-disabled` instead, which on white put a disabled
2598
- * in-month day at ~163 and an ENABLED adjacent day at ~168 — five levels
2599
- * apart, indistinguishable, and with the disabled one the darker of the
2600
- * two, so the unpickable date read as the more solid one. Fading instead
2601
- * puts every disabled day lighter than every enabled one, which is the
2602
- * ordering a calendar needs.
2603
- */
2604
- it('fades disabled days as the desktop does, rather than recolouring them', () => {
2605
- const desktop = read('../Calendar/styles.ts');
2606
- const theme = declarationsIn(desktop, 'dayCellTheme', 'dayDisabled');
2607
- const touch = declarations(read('MonthScroller.tsx'), 'dayDisabled');
2608
-
2609
- // Whatever opacity the desktop fades to, this pane fades to the same one.
2610
- const fade = theme.find(d => d.startsWith('opacity:'));
2611
- expect(fade).toBeDefined();
2612
- expect(touch).toContain(fade);
2613
-
2614
- // And no colour of its own: a colour would override the secondary one a
2615
- // spilled day carries, collapsing "disabled" and "disabled and adjacent"
2616
- // into a single shade.
2617
- expect(touch.some(d => d.startsWith('color:'))).toBe(false);
2618
- // Parity, not a rule of this pane's own — if the desktop ever starts
2619
- // recolouring a disabled day, this fires and says to follow it.
2620
- expect(theme.some(d => d.startsWith('color:'))).toBe(false);
2621
- });
2622
-
2623
- /**
2624
- * Order matters as much as the values: `dayDisabled` is applied AFTER
2625
- * `dayOutside`, so a spilled day beyond min/max paints disabled rather than
2626
- * merely outside. Reversed, an unselectable date would look more available
2627
- * than the selectable ones beside it.
2628
- */
2629
- it('lets disabled win over outside on a spilled day past min/max', () => {
2630
- const applied = read('MonthScroller.tsx');
2631
- const outside = applied.indexOf('day.isOutside && styles.dayOutside');
2632
- const disabled = applied.indexOf('isDisabled && styles.dayDisabled');
2633
- expect(outside).toBeGreaterThan(-1);
2634
- expect(outside).toBeLessThan(disabled);
2635
- });
2636
-
2637
- it('keeps the month scroller and the wheels on border-box', () => {
2638
- // Load-bearing: clientHeight is the pane height, the snap offsets and the
2639
- // virtualization all at once.
2640
- expect(read('MonthScroller.tsx')).toContain("boxSizing: 'border-box'");
2641
- expect(read('Wheel.tsx')).toContain("boxSizing: 'border-box'");
2642
- });
2643
-
2644
- it('sizes the wheel row tighter than a day cell, with larger text', () => {
2645
- const tokens = read('tokens.stylex.ts');
2646
- // Scroll-first rows: closer together than day cells and closer to the
2647
- // text they hold, the way a platform picker packs them.
2648
- expect(tokens).toContain("wheelItemSize: '28px'");
2649
- expect(read('Wheel.tsx')).toContain(
2650
- "fontSize: typeScaleVars['--text-large-size']",
2651
- );
2652
- });
2653
-
2654
- it('centers wheel rows and pads both ends so either extreme can reach the band', () => {
2655
- const source = read('Wheel.tsx');
2656
- expect(source).toContain("scrollSnapType: 'y mandatory'");
2657
- expect(source).toContain("scrollSnapAlign: 'center'");
2658
- expect(source).toContain(
2659
- 'paddingBlock: dateInputTouchGeometry.wheelEdgePadding',
2660
- );
2661
- });
2662
-
2663
- it('leaves a two-finger pinch to the page over the wheels', () => {
2664
- // `pan-y` alone also refuses the browser's pinch-zoom over the wheel, so
2665
- // the page could not be zoomed over an open picker. The real-engine proof
2666
- // is WheelPinchZoom.a11y.chromium.spec.ts; jsdom computes no touch-action.
2667
- expect(declarations(read('Wheel.tsx'), 'scroller')).toContain(
2668
- "touchAction: 'pan-y pinch-zoom'",
2669
- );
2670
- });
2671
-
2672
- it('guards the scroll-driven falloff behind @supports', () => {
2673
- const source = read('Wheel.tsx');
2674
- // Without the guard, a browser that does not understand animation-timeline
2675
- // runs these keyframes once on the document timeline instead.
2676
- expect(source).toContain("'@supports (animation-timeline: view())'");
2677
- expect(source).toContain("animationTimeline: 'view(y)'");
2678
- expect(source).toContain("'@media (prefers-reduced-motion: reduce)'");
2679
- });
2680
-
2681
- it('keeps the falloff off the snap area itself', () => {
2682
- // A transform moves the snap area, and the wheel then settles a few
2683
- // pixels off the row it is showing.
2684
- const source = read('Wheel.tsx');
2685
- const itemInner = source.slice(source.indexOf('itemInner: {'));
2686
- expect(itemInner).toContain("animationTimeline: 'view(y)'");
2687
- const item = source.slice(
2688
- source.indexOf(' item: {'),
2689
- source.indexOf('itemInner: {'),
2690
- );
2691
- expect(item).not.toContain('animationTimeline');
2692
- expect(item).not.toContain("overflow: 'hidden'");
2693
- });
2694
-
2695
- it('insets the sheet content equally on every edge', () => {
2696
- const source = read('TouchDateField.tsx');
2697
- // One inset, and the header/footer must not add their own on top of it —
2698
- // that is what put the title and Done 4px off the day grid's line.
2699
- expect(source).toContain("paddingInline: spacingVars['--spacing-4']");
2700
- expect(source).toContain("paddingBlockEnd: spacingVars['--spacing-4']");
2701
- // The block-start is the documented exception: the grab handle floats out
2702
- // of flow, so the content wrapper owes it the height it occupies.
2703
- expect(source).toContain("paddingBlockStart: spacingVars['--spacing-6']");
2704
- const header = source.slice(
2705
- source.indexOf(' header: {'),
2706
- source.indexOf(' title: {'),
2707
- );
2708
- expect(header).not.toContain('paddingInline');
2709
- const footer = source.slice(
2710
- source.indexOf(' footer: {'),
2711
- source.indexOf(' sheetBody: {'),
2712
- );
2713
- expect(footer).not.toContain('paddingInline');
2714
- });
2715
-
2716
- it('sizes the closed field the same on both surfaces', () => {
2717
- // `DateInput` picks a surface from the pointer and `nativePicker`, so a
2718
- // field that sizes itself differently on one of them changes height for
2719
- // a reason the call site never asked about.
2720
- //
2721
- // Compared with formatting normalized away: the two files write the same
2722
- // declarations differently, one entry per line against one per size.
2723
- const sizeMap = (file: string) => {
2724
- const source = read(file);
2725
- const start = source.indexOf('const sizeStyles = stylex.create(');
2726
- expect(start).toBeGreaterThan(-1);
2727
- return source
2728
- .slice(start, source.indexOf('});', start))
2729
- .replace(/\/\/.*$/gm, '')
2730
- .replace(/\/\*[\s\S]*?\*\//g, '')
2731
- .replace(/\s+/g, '')
2732
- .replace(/,(?=[}\]])/g, '');
2733
- };
2734
- expect(sizeMap('TouchDateField.tsx')).toEqual(
2735
- sizeMap('NativeDateField.tsx'),
2736
- );
2737
- });
2738
-
2739
- it('floors the month arrows too — Button tops out at 36px', () => {
2740
- const source = read('TouchDateField.tsx');
2741
- const arrow = source.slice(
2742
- source.indexOf(' monthArrow: {'),
2743
- source.indexOf(' monthArrowIcon: {'),
2744
- );
2745
- expect(arrow).toContain(
2746
- "minBlockSize: {default: null, '@media (pointer: coarse)': TOUCH_TARGET}",
2747
- );
2748
- expect(arrow).toContain(
2749
- "minInlineSize: {default: null, '@media (pointer: coarse)': TOUCH_TARGET}",
2750
- );
2751
- });
2752
-
2753
- /**
2754
- * A bare inline wrapper puts the glyph on the text baseline, a few px above
2755
- * the button's optical centre. Core's Calendar carries the same rule on its
2756
- * own nav icons.
2757
- */
2758
- it('keeps the mirrored arrow glyph centred', () => {
2759
- const source = read('TouchDateField.tsx');
2760
- const icon = source.slice(source.indexOf(' monthArrowIcon: {'));
2761
- expect(icon.slice(0, icon.indexOf('}'))).toContain(
2762
- "display: 'inline-flex'",
2763
- );
2764
- });
2765
-
2766
- /**
2767
- * The wheels are a layer that fades in and out on top; the calendar under
2768
- * them is covered and uncovered, and never seen to move.
2769
- *
2770
- * The swap used to take turns — the outgoing surface faded out over a first
2771
- * leg, the incoming one waited and faded in over a second — because the
2772
- * wheels' panel was transparent. Overlapping them put the wheels'
2773
- * translucent selection band over the live calendar grid and tinted one
2774
- * band-shaped strip of it: "the grey area animates differently from the
2775
- * content".
2776
- *
2777
- * An opaque plate INSIDE the fading layer fixes that at the root. The layer
2778
- * renders as one finished opaque image and the fade applies to the image,
2779
- * so the band crosses at the same rate as everything beside it. And once it
2780
- * is opaque, the calendar needs no fade of its own — animating it too would
2781
- * be animating the date picker rather than the thing arriving over it.
2782
- *
2783
- * What this pins is that split: the layer above gets opacity and a
2784
- * background, the layer beneath gets visibility and nothing else.
2785
- */
2786
- it('fades the wheels in and out over a calendar that does not animate', () => {
2787
- const source = read('TouchDateField.tsx');
2788
- const overlay = declarations(source, 'panelOverlay');
2789
- const beneath = declarations(source, 'panelBeneath');
2790
-
2791
- // The layer fades, both directions — the transition is on the shown
2792
- // state, so entering and leaving it both animate.
2793
- expect(overlay).toContain("transitionProperty: 'opacity, visibility'");
2794
- expect(overlay).toContain('transitionDuration: SWAP_DURATION');
2795
- expect(declarations(source, 'panelOverlayHidden')).toContain('opacity: 0');
2796
-
2797
- // And it is opaque while it does, in the token the sheet paints itself
2798
- // with, so the fade is uniform across the band and the text alike.
2799
- expect(overlay).toContain(
2800
- "backgroundColor: colorVars['--color-background-surface']",
2801
- );
2802
-
2803
- // The layer beneath moves nothing that can be SEEN moving: visibility
2804
- // only, no opacity, so it is covered and uncovered rather than faded.
2805
- expect(beneath).toContain("transitionProperty: 'visibility'");
2806
- expect(beneath.some(d => d.startsWith('opacity'))).toBe(false);
2807
- expect(
2808
- declarations(source, 'panelBeneathHidden').some(d =>
2809
- d.startsWith('opacity'),
2810
- ),
2811
- ).toBe(false);
2812
-
2813
- // Two of each, and never the same one twice: the calendar panel and the
2814
- // calendar's footer actions are beneath, the wheels and their Done above.
2815
- const count = (name: string) =>
2816
- source.match(new RegExp(`styles\\.${name},`, 'g'))?.length ?? 0;
2817
- expect(count('panelBeneath')).toBe(2);
2818
- expect(count('panelOverlay')).toBe(2);
2819
- });
2820
-
2821
- /**
2822
- * The layer paints as a unit, which is what makes "opaque" mean "covers".
2823
- *
2824
- * Backgrounds and text paint in separate phases, so without a stacking
2825
- * context a later sibling's background lands UNDER an earlier sibling's
2826
- * text — the plate went in opaque and the calendar's day numbers showed
2827
- * straight through it. Measured before and after: ink inside the grid's box
2828
- * at full cover went from the calendar's 2.5% of pixels to the wheels' own.
2829
- *
2830
- * Easy to lose, because any opacity below 1 makes a stacking context by
2831
- * accident. It only breaks at the two ends of the fade, where opacity is
2832
- * exactly 1 — which is to say, whenever anyone is actually looking.
2833
- */
2834
- it('paints the layer as a unit, so the plate really covers', () => {
2835
- expect(declarations(read('TouchDateField.tsx'), 'panelOverlay')).toContain(
2836
- "isolation: 'isolate'",
2837
- );
2838
- });
2839
-
2840
- /**
2841
- * The weekday row and the header arrows are the one part of the calendar
2842
- * the layer cannot cover — the plate starts below the header. They fade on
2843
- * the layer's own timing rather than clearing instantly, so the change
2844
- * reads as one motion instead of chrome blinking out a beat ahead of the
2845
- * grid being covered.
2846
- */
2847
- it('fades the uncovered chrome on the same timing as the layer', () => {
2848
- const source = read('TouchDateField.tsx');
2849
- for (const name of ['weekdays', 'monthArrows']) {
2850
- expect(declarations(source, name)).toContain(
2851
- 'transitionDuration: SWAP_DURATION',
2852
- );
2853
- expect(declarations(source, `${name}Hidden`)).toContain('opacity: 0');
2854
- }
2855
- });
2856
-
2857
- /**
2858
- * The footer action spans the sheet. A full-width primary is the shape a
2859
- * phone form ends with, and it puts the target under the thumb wherever
2860
- * the hand is.
2861
- */
2862
- it('spans the footer with its action', () => {
2863
- const source = read('TouchDateField.tsx');
2864
- // One per surface: Save on the calendar, Done on the wheels.
2865
- expect(source.match(/width="100%"/g)).toHaveLength(2);
2866
- });
2867
-
2868
- /**
2869
- * The selection band has to be visible enough that fading it reads as a
2870
- * fade. At `--color-background-muted` (4.7% alpha) the whole plate sat 17
2871
- * units of colour from the sheet behind it, so its animation had 17 units
2872
- * to happen in while the text beside it travelled 412 — it did not look
2873
- * like it was fading, it looked like it appeared at the end.
2874
- * `--color-neutral` (10%) doubles that to 36 and is still quiet enough to
2875
- * sit under text.
2876
- */
2877
- it('gives the wheel band enough contrast for its fade to read', () => {
2878
- const source = read('Wheel.tsx');
2879
- const band = source.slice(
2880
- source.indexOf(' band: {'),
2881
- source.indexOf('});', source.indexOf(' band: {')),
2882
- );
2883
- expect(band).toContain("backgroundColor: colorVars['--color-neutral']");
2884
- });
2885
-
2886
- /**
2887
- * `--ease-standard` is `cubic-bezier(0.24, 1, 0.4, 1)`: right for something
2888
- * travelling a distance, wrong for a fade. Measured with it, opacity hit
2889
- * 50% in 91ms and 95% in 241ms of a 410ms transition — the fade was over
2890
- * long before the duration was, so lengthening the duration bought an
2891
- * imperceptible tail rather than a slower fade. A fade covers no distance,
2892
- * so its progress should simply be its progress.
2893
- *
2894
- * The title chevron is the exception and keeps the token: it rotates, and
2895
- * rotation is travel.
2896
- */
2897
- it('fades linearly, and eases only the thing that travels', () => {
2898
- const source = read('TouchDateField.tsx');
2899
- const styles = source.slice(
2900
- source.indexOf('const styles = stylex.create('),
2901
- );
2902
- const chevron = styles.slice(
2903
- styles.indexOf(' titleChevron: {'),
2904
- styles.indexOf(' titleChevronOpen: {'),
2905
- );
2906
- expect(chevron).toContain("transitionProperty: 'transform'");
2907
- expect(chevron).toContain(
2908
- "transitionTimingFunction: easeVars['--ease-standard']",
2909
- );
2910
- // The chevron is the only eased transition; every fade is linear.
2911
- expect(
2912
- styles.match(/transitionTimingFunction: easeVars\['--ease-standard'\]/g),
2913
- ).toHaveLength(1);
2914
- expect(styles.match(/transitionTimingFunction: 'linear'/g)).toHaveLength(4);
2915
- });
2916
-
2917
- /**
2918
- * Everything in the swap runs for the same time, so it reads as one change
2919
- * rather than several. The chevron included — it used to run
2920
- * `--duration-medium` against a 220ms swap and was still turning well after
2921
- * the wheels had settled.
2922
- *
2923
- * A token rather than a literal, so a consumer's motion scale carries: the
2924
- * Storybook theme resolves `--duration-fast` to 125ms, not the default
2925
- * 175ms, and the swap follows it without knowing.
2926
- */
2927
- it('runs the whole swap on one duration', () => {
2928
- const source = read('TouchDateField.tsx');
2929
- const styles = source.slice(
2930
- source.indexOf('const styles = stylex.create('),
2931
- );
2932
- // The arrows, Reset, the weekday row, the layer beneath, the layer
2933
- // above, and the chevron.
2934
- expect(styles.match(/transitionDuration: SWAP_DURATION/g)).toHaveLength(6);
2935
- // And no leftover hand-rolled timing beside them.
2936
- expect(styles).not.toContain('PANEL_FADE_MS');
2937
- expect(source).toContain(
2938
- "const SWAP_DURATION = durationVars['--duration-fast']",
2939
- );
2940
- });
2941
-
2942
- it('keeps the virtual keyboard down on the touch field', () => {
2943
- const source = read('TouchDateField.tsx');
2944
- // readOnly alone still opens the keyboard on some Android browsers.
2945
- expect(source).toContain('readOnly');
2946
- expect(source).toContain('inputMode="none"');
2947
- });
2948
- });