@hanzogui/kitchen-sink 3.0.5

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 (431) hide show
  1. package/.detoxrc.js +130 -0
  2. package/.env.production +2 -0
  3. package/.maestro/config.yaml +4 -0
  4. package/.maestro/flows/shorthand-variables.yaml +23 -0
  5. package/.watchmanconfig +1 -0
  6. package/LICENSE +21 -0
  7. package/README.md +11 -0
  8. package/app.json +43 -0
  9. package/assets/adaptive-icon.png +0 -0
  10. package/assets/favicon.png +0 -0
  11. package/assets/icon.png +0 -0
  12. package/assets/splash.png +0 -0
  13. package/babel.config.js +25 -0
  14. package/e2e/CompilerExtraction.test.ts +147 -0
  15. package/e2e/GroupPressNative.test.ts +167 -0
  16. package/e2e/MediaQueryGtMd.test.ts +71 -0
  17. package/e2e/NativePortal.test.ts +113 -0
  18. package/e2e/PointerEvents.test.ts +116 -0
  19. package/e2e/PressStyleNative.noRngh.test.ts +191 -0
  20. package/e2e/PressStyleNative.test.ts +231 -0
  21. package/e2e/SafeArea.test.ts +57 -0
  22. package/e2e/SelectAndroidOnPress.test.ts +181 -0
  23. package/e2e/SelectRemount.test.ts +137 -0
  24. package/e2e/SheetDragResist.test.ts +370 -0
  25. package/e2e/SheetKeyboardDrag.test.ts +249 -0
  26. package/e2e/SheetScrollableDrag.test.ts +560 -0
  27. package/e2e/ShorthandVariables.test.ts +53 -0
  28. package/e2e/ThemeChangeBasic.test.ts +123 -0
  29. package/e2e/ThemeMutation.test.ts +80 -0
  30. package/e2e/check-rngh-status.test.ts +31 -0
  31. package/e2e/jest.config.js +19 -0
  32. package/e2e/utils/colors.ts +75 -0
  33. package/e2e/utils/navigation.ts +53 -0
  34. package/eas.json +22 -0
  35. package/flows/AlertDialog.yaml +17 -0
  36. package/flows/OpenApp.yaml +25 -0
  37. package/flows/Select.yaml +13 -0
  38. package/flows/Sheet.yaml +12 -0
  39. package/flows/Tabs.yaml +13 -0
  40. package/flows/Toast.yaml +14 -0
  41. package/flows/WarmUp.yaml +24 -0
  42. package/index.html +21 -0
  43. package/index.js +17 -0
  44. package/metro.config.js +64 -0
  45. package/next-router-shim.ts +9 -0
  46. package/package.json +118 -0
  47. package/plans/toast-2.md +471 -0
  48. package/playwright.config.ts +71 -0
  49. package/plugins/expo-modules-core-swift6.js +76 -0
  50. package/pod-install.sh +7 -0
  51. package/public/favicon.svg +70 -0
  52. package/public/fonts/inter.css +15 -0
  53. package/public/fonts/noto-cn.otf +0 -0
  54. package/public/gui-icon.svg +68 -0
  55. package/run-detox.sh +230 -0
  56. package/run-native-tests.sh +4 -0
  57. package/run-tests-parallel.ts +195 -0
  58. package/screenshots/Screenshotter.test.tsx +48 -0
  59. package/src/AnimationDemos.tsx +131 -0
  60. package/src/App.native.tsx +121 -0
  61. package/src/App.tsx +121 -0
  62. package/src/Navigation.tsx +98 -0
  63. package/src/Sandbox.tsx +87 -0
  64. package/src/TestDynamicEval.tsx +33 -0
  65. package/src/TestNativeSheet.tsx +100 -0
  66. package/src/components/TimedRender.tsx +18 -0
  67. package/src/constants/test-ids.ts +52 -0
  68. package/src/features/demos/demo-screen.tsx +72 -0
  69. package/src/features/home/ColorSchemeListItem.tsx +41 -0
  70. package/src/features/home/TestBuildAButton.tsx +102 -0
  71. package/src/features/home/TestSeparator.tsx +0 -0
  72. package/src/features/home/screen.tsx +285 -0
  73. package/src/features/testcases/screen.tsx +59 -0
  74. package/src/features/testcases/test-screen.tsx +50 -0
  75. package/src/generatedV5Theme.ts +112 -0
  76. package/src/gui.config.ts +411 -0
  77. package/src/guy.png +0 -0
  78. package/src/index.tsx +6 -0
  79. package/src/provider/index.tsx +18 -0
  80. package/src/test-gui-stack.tsx +11 -0
  81. package/src/test.tsx +3 -0
  82. package/src/useKitchenSinkTheme.tsx +15 -0
  83. package/src/usecases/ActionsSheetComparison.tsx +194 -0
  84. package/src/usecases/AnimatePresenceEnterExitCase.tsx +255 -0
  85. package/src/usecases/AnimatePresenceExitTest.tsx +69 -0
  86. package/src/usecases/AnimatedByProp.tsx +39 -0
  87. package/src/usecases/AnimationComprehensiveCase.tsx +2515 -0
  88. package/src/usecases/AnimationValueLoggingCase.tsx +526 -0
  89. package/src/usecases/AnimationsWithMediaQueriesCase.tsx +110 -0
  90. package/src/usecases/Benchmark.tsx +148 -0
  91. package/src/usecases/BenchmarkSelect.tsx +34 -0
  92. package/src/usecases/ButtonCircular.tsx +3 -0
  93. package/src/usecases/ButtonCustom.tsx +33 -0
  94. package/src/usecases/ButtonIconColor.tsx +18 -0
  95. package/src/usecases/ButtonInverse.tsx +30 -0
  96. package/src/usecases/ButtonUnstyled.tsx +31 -0
  97. package/src/usecases/CheckboxDisabledOnPress.tsx +62 -0
  98. package/src/usecases/ClickDuringEnterCase.tsx +59 -0
  99. package/src/usecases/CodeExamplesInput.tsx +9 -0
  100. package/src/usecases/ColorTokenFallback.tsx +52 -0
  101. package/src/usecases/CompilerExtraction.tsx +380 -0
  102. package/src/usecases/ComplexVariants.tsx +164 -0
  103. package/src/usecases/CrashAdaptSheet.tsx +98 -0
  104. package/src/usecases/CustomStyledAnimatedPopover.tsx +42 -0
  105. package/src/usecases/CustomStyledAnimatedTooltip.tsx +72 -0
  106. package/src/usecases/DOMNodeAPIs.tsx +154 -0
  107. package/src/usecases/DialogFocusScopeCase.tsx +277 -0
  108. package/src/usecases/DialogFocusScopeDebug.tsx +85 -0
  109. package/src/usecases/DialogNestedCase.tsx +121 -0
  110. package/src/usecases/DialogOpenControlled.tsx +49 -0
  111. package/src/usecases/DialogPointerEventsCase.tsx +58 -0
  112. package/src/usecases/DialogScopedCase.tsx +106 -0
  113. package/src/usecases/DialogSheetAdaptCase.tsx +178 -0
  114. package/src/usecases/DialogSheetAdaptResizeCase.tsx +98 -0
  115. package/src/usecases/DismissLayerStackingCase.tsx +223 -0
  116. package/src/usecases/DriverDisableAnimationPropsCase.tsx +44 -0
  117. package/src/usecases/Example.tsx +10 -0
  118. package/src/usecases/ExitCompletionCase.tsx +713 -0
  119. package/src/usecases/FocusVisibleButton.tsx +14 -0
  120. package/src/usecases/FocusVisibleButtonPointer.tsx +13 -0
  121. package/src/usecases/FocusVisibleButtonWithFocusStyle.tsx +16 -0
  122. package/src/usecases/FocusWithinCase.tsx +55 -0
  123. package/src/usecases/FontTokensInVariants.tsx +14 -0
  124. package/src/usecases/FormButtonTypeCase.tsx +34 -0
  125. package/src/usecases/GlobalScopedTriggerIsolationCase.tsx +178 -0
  126. package/src/usecases/GroupHoverMobile.tsx +39 -0
  127. package/src/usecases/GroupPressInVariant.tsx +92 -0
  128. package/src/usecases/GroupPressNative.tsx +200 -0
  129. package/src/usecases/GroupProp.tsx +96 -0
  130. package/src/usecases/GroupPseudoVariantOverride.tsx +56 -0
  131. package/src/usecases/GroupUseCases.tsx +94 -0
  132. package/src/usecases/HeightMediaQueryOverrideCase.tsx +183 -0
  133. package/src/usecases/InputAutoFocusAfterMenuCase.tsx +105 -0
  134. package/src/usecases/InputAutoFocusStyledCase.tsx +39 -0
  135. package/src/usecases/KeyboardControllerTest.tsx +146 -0
  136. package/src/usecases/ListItem.tsx +123 -0
  137. package/src/usecases/MediaQueriesV5.tsx +137 -0
  138. package/src/usecases/MediaQueryGtMd.tsx +73 -0
  139. package/src/usecases/MenuAboveDialogCase.tsx +75 -0
  140. package/src/usecases/MenuAccessibilityCase.tsx +133 -0
  141. package/src/usecases/MenuAnimatePositionCase.tsx +41 -0
  142. package/src/usecases/MenuArrowAnimatePresenceCase.tsx +98 -0
  143. package/src/usecases/MenuAsChildPositionCase.tsx +24 -0
  144. package/src/usecases/MenuAutoResizeCase.tsx +57 -0
  145. package/src/usecases/MenuBottomCase.tsx +55 -0
  146. package/src/usecases/MenuFocusLeaveCase.tsx +135 -0
  147. package/src/usecases/MenuHighlightCase.tsx +44 -0
  148. package/src/usecases/MenuItemFocusCase.tsx +79 -0
  149. package/src/usecases/MenuItemPseudoOverrideCase.tsx +270 -0
  150. package/src/usecases/MenuMultiTriggerCase.tsx +47 -0
  151. package/src/usecases/MenuOverflowCase.tsx +60 -0
  152. package/src/usecases/MenuSubCase.tsx +223 -0
  153. package/src/usecases/MenuSubLeftCase.tsx +178 -0
  154. package/src/usecases/MenuSubNestedPositionCase.tsx +171 -0
  155. package/src/usecases/MenuSubStyledCase.tsx +145 -0
  156. package/src/usecases/MenuThemeCase.tsx +50 -0
  157. package/src/usecases/MenuUnstyledCase.tsx +52 -0
  158. package/src/usecases/MultiDriverAnimation.tsx +118 -0
  159. package/src/usecases/NativePortalTest.tsx +179 -0
  160. package/src/usecases/NewInputBasic.tsx +16 -0
  161. package/src/usecases/NewInputEvents.tsx +29 -0
  162. package/src/usecases/NonGuiTextStyledType.tsx +23 -0
  163. package/src/usecases/OnLayoutCase.tsx +134 -0
  164. package/src/usecases/OnLayoutScaleCase.tsx +88 -0
  165. package/src/usecases/OnLayoutStressCase.tsx +353 -0
  166. package/src/usecases/OpacityModifierCase.tsx +113 -0
  167. package/src/usecases/OverlayStyled.tsx +66 -0
  168. package/src/usecases/ParagraphSpanFontInheritance.tsx +53 -0
  169. package/src/usecases/PlaceholderTextColor.tsx +20 -0
  170. package/src/usecases/PointerEventsCase.tsx +100 -0
  171. package/src/usecases/PopoverAndMenuMultiTriggerCase.tsx +138 -0
  172. package/src/usecases/PopoverCase.tsx +222 -0
  173. package/src/usecases/PopoverContentStyledPlusAnimations.tsx +44 -0
  174. package/src/usecases/PopoverFocusScopeCase.tsx +171 -0
  175. package/src/usecases/PopoverHoverableCase.tsx +167 -0
  176. package/src/usecases/PopoverHoverableDisableClickCase.tsx +118 -0
  177. package/src/usecases/PopoverHoverableRapidCase.tsx +103 -0
  178. package/src/usecases/PopoverHoverableScopedCase.tsx +135 -0
  179. package/src/usecases/PopoverScopedCase.tsx +76 -0
  180. package/src/usecases/PopoverTriggerIsolationCase.tsx +80 -0
  181. package/src/usecases/PressStyleNative.tsx +143 -0
  182. package/src/usecases/PseudoStyleMerge.tsx +25 -0
  183. package/src/usecases/PseudoTransitionCase.tsx +174 -0
  184. package/src/usecases/RawAnimatedValueCase.tsx +231 -0
  185. package/src/usecases/RemoveScrollCase.tsx +66 -0
  186. package/src/usecases/RenderPropCase.tsx +263 -0
  187. package/src/usecases/SafeAreaCase.tsx +236 -0
  188. package/src/usecases/ScrollViewRefCase.tsx +88 -0
  189. package/src/usecases/SecondPage.tsx +5 -0
  190. package/src/usecases/SelectAndroidOnPress.tsx +129 -0
  191. package/src/usecases/SelectFocusScopeCase.tsx +270 -0
  192. package/src/usecases/SelectRemount.tsx +136 -0
  193. package/src/usecases/Shadows.tsx +5 -0
  194. package/src/usecases/SheetAnimationCase.tsx +155 -0
  195. package/src/usecases/SheetDragCase.tsx +183 -0
  196. package/src/usecases/SheetDragResistCase.tsx +433 -0
  197. package/src/usecases/SheetDragResistCase.web.tsx +359 -0
  198. package/src/usecases/SheetKeyboardDragCase.tsx +328 -0
  199. package/src/usecases/SheetKeyboardFitContentCase.tsx +165 -0
  200. package/src/usecases/SheetOnAnimationCompleteCase.tsx +54 -0
  201. package/src/usecases/SheetScrollLockCase.tsx +166 -0
  202. package/src/usecases/SheetScrollableDrag.tsx +249 -0
  203. package/src/usecases/SheetSnapPointsFitCase.tsx +393 -0
  204. package/src/usecases/ShorthandVariables.tsx +49 -0
  205. package/src/usecases/SlowThemeReRender.tsx +48 -0
  206. package/src/usecases/SpinnerCustomColors.tsx +34 -0
  207. package/src/usecases/StackZIndex.tsx +82 -0
  208. package/src/usecases/StressPage.tsx +301 -0
  209. package/src/usecases/StylePlatform.tsx +30 -0
  210. package/src/usecases/StyleProp.tsx +29 -0
  211. package/src/usecases/StyledAnchor.tsx +27 -0
  212. package/src/usecases/StyledButtonAnimationAuto.tsx +99 -0
  213. package/src/usecases/StyledButtonTheme.tsx +63 -0
  214. package/src/usecases/StyledButtonVariantPseudo.tsx +25 -0
  215. package/src/usecases/StyledButtonVariantPseudoMerge.tsx +77 -0
  216. package/src/usecases/StyledCheckboxTheme.tsx +23 -0
  217. package/src/usecases/StyledContextColor.tsx +246 -0
  218. package/src/usecases/StyledContextTokens.tsx +147 -0
  219. package/src/usecases/StyledHOCNamed.tsx +20 -0
  220. package/src/usecases/StyledHtmlCase.tsx +144 -0
  221. package/src/usecases/StyledIconColor.tsx +19 -0
  222. package/src/usecases/StyledInputFocusStyle.tsx +21 -0
  223. package/src/usecases/StyledInputOnFocus.tsx +30 -0
  224. package/src/usecases/StyledMediaQueryMerge.tsx +95 -0
  225. package/src/usecases/StyledOverridePsuedo.tsx +26 -0
  226. package/src/usecases/StyledRNW.tsx +61 -0
  227. package/src/usecases/StyledStyleableInputOnFocus.tsx +34 -0
  228. package/src/usecases/StyledStyleableInputVariant.tsx +48 -0
  229. package/src/usecases/StyledStyledStyleableInputOnFocus.tsx +36 -0
  230. package/src/usecases/StyledVariantTextColor.tsx +25 -0
  231. package/src/usecases/StyledViewOnFocus.tsx +32 -0
  232. package/src/usecases/TabHoverAnimationCase.tsx +212 -0
  233. package/src/usecases/TextNestedInheritance.tsx +80 -0
  234. package/src/usecases/ThemeChange.tsx +100 -0
  235. package/src/usecases/ThemeChangeBasic.tsx +52 -0
  236. package/src/usecases/ThemeComponentResolution.tsx +119 -0
  237. package/src/usecases/ThemeConditionalName.tsx +31 -0
  238. package/src/usecases/ThemeMediaAnimationCase.tsx +39 -0
  239. package/src/usecases/ThemeMutation.tsx +86 -0
  240. package/src/usecases/ThemeNested.tsx +103 -0
  241. package/src/usecases/ThemeReset.tsx +62 -0
  242. package/src/usecases/ThemeShallowCase.tsx +83 -0
  243. package/src/usecases/ToastCase.tsx +46 -0
  244. package/src/usecases/ToggleGroupActiveProps.tsx +40 -0
  245. package/src/usecases/ToggleGroupXGroupCase.tsx +104 -0
  246. package/src/usecases/TooltipAnimationCase.tsx +99 -0
  247. package/src/usecases/TooltipCase.tsx +32 -0
  248. package/src/usecases/TooltipGlobalPatternCase.tsx +83 -0
  249. package/src/usecases/TooltipGroupCase.tsx +102 -0
  250. package/src/usecases/TooltipMultiTriggerCase.tsx +88 -0
  251. package/src/usecases/TooltipPositionJumpCase.tsx +91 -0
  252. package/src/usecases/TooltipTriggerInlineCase.tsx +60 -0
  253. package/src/usecases/TransformMediaQueryMerge.tsx +98 -0
  254. package/src/usecases/UseCases.tsx +409 -0
  255. package/src/usecases/UseTheme.tsx +41 -0
  256. package/src/usecases/V5ThemeBuilderOutput.tsx +231 -0
  257. package/src/usecases/VariantFontFamily.tsx +25 -0
  258. package/src/usecases/VariantsOrder.tsx +117 -0
  259. package/src/usecases/ZIndex.tsx +155 -0
  260. package/src/usecases/helpers.tsx +44 -0
  261. package/src/usecases/index.native.ts +122 -0
  262. package/src/usecases/index.ts +3 -0
  263. package/src/usecases/index.web.ts +177 -0
  264. package/tests/AnimatePresenceEnterExit.animated.test.tsx +176 -0
  265. package/tests/AnimatedByProp.animated.test.tsx +138 -0
  266. package/tests/AnimationBehavior.animated.test.tsx +543 -0
  267. package/tests/AnimationTiming.animated.test.tsx +195 -0
  268. package/tests/AnimationsWithMediaQueries.animated.test.tsx +154 -0
  269. package/tests/BuildAButton.test.tsx +87 -0
  270. package/tests/ButtonCircular.test.tsx +17 -0
  271. package/tests/ButtonCustom.test.tsx +17 -0
  272. package/tests/ButtonIconColor.test.tsx +23 -0
  273. package/tests/ButtonUnstyled.test.tsx +56 -0
  274. package/tests/ClickDuringEnter.animated.test.tsx +174 -0
  275. package/tests/ColorTokenFallback.test.tsx +45 -0
  276. package/tests/DOMNodeAPIs.test.tsx +161 -0
  277. package/tests/DialogFocusScope.animated.test.tsx +309 -0
  278. package/tests/DialogNested.test.tsx +128 -0
  279. package/tests/DialogOpenControlled.test.tsx +42 -0
  280. package/tests/DialogPointerEvents.animated.test.tsx +108 -0
  281. package/tests/DialogScoped.test.tsx +137 -0
  282. package/tests/DialogSheetAdapt.test.tsx +68 -0
  283. package/tests/DialogSheetAdaptResize.test.tsx +161 -0
  284. package/tests/DismissLayerStacking.test.tsx +292 -0
  285. package/tests/DriverDisableAnimationProps.animated.test.tsx +157 -0
  286. package/tests/ExitCompletion.animated.test.tsx +425 -0
  287. package/tests/ExitTimingCheck.animated.test.ts +34 -0
  288. package/tests/FocusVisibleButton.test.tsx +41 -0
  289. package/tests/FocusVisibleButtonPointerFocus.test.tsx +23 -0
  290. package/tests/FocusVisibleButtonPointerFocusWithFocusStyle.test.tsx +40 -0
  291. package/tests/FocusWithinStyle.animated.test.tsx +66 -0
  292. package/tests/FocusWithinStyle.test.tsx +60 -0
  293. package/tests/FormButtonType.test.tsx +42 -0
  294. package/tests/GlobalScopedTriggerIsolation.test.tsx +89 -0
  295. package/tests/GroupHoverMobile.test.tsx +52 -0
  296. package/tests/GroupPressInVariant.test.tsx +82 -0
  297. package/tests/GroupProp.test.tsx +30 -0
  298. package/tests/GroupPseudoVariantOverride.test.tsx +57 -0
  299. package/tests/GroupUseCases.test.tsx +111 -0
  300. package/tests/GuiSiteMotion.test.ts +481 -0
  301. package/tests/HeightMediaQueryOverride.test.tsx +112 -0
  302. package/tests/InputAutoFocusAfterMenu.test.tsx +55 -0
  303. package/tests/InputAutoFocusStyled.test.tsx +22 -0
  304. package/tests/ListItem.test.tsx +129 -0
  305. package/tests/MediaQueriesV5.test.tsx +113 -0
  306. package/tests/MediaQueryGtMd.test.tsx +84 -0
  307. package/tests/MenuAboveDialog.test.tsx +108 -0
  308. package/tests/MenuAccessibility.test.tsx +346 -0
  309. package/tests/MenuAnimatePosition.animated.test.tsx +57 -0
  310. package/tests/MenuArrowAnimatePresence.animated.test.tsx +71 -0
  311. package/tests/MenuAsChildPosition.test.tsx +16 -0
  312. package/tests/MenuAutoResize.test.tsx +54 -0
  313. package/tests/MenuFocusLeave.test.tsx +181 -0
  314. package/tests/MenuHighlight.test.tsx +165 -0
  315. package/tests/MenuHoverKeyboardBugs.test.tsx +252 -0
  316. package/tests/MenuItemFocus.test.tsx +59 -0
  317. package/tests/MenuItemPseudoOverride.test.tsx +231 -0
  318. package/tests/MenuMultiTrigger.test.tsx +101 -0
  319. package/tests/MenuOverflow.test.tsx +93 -0
  320. package/tests/MenuStayInFrame.test.tsx +102 -0
  321. package/tests/MenuSubKeyboardFocus.test.tsx +220 -0
  322. package/tests/MenuSubLeftSafePolygon.test.tsx +88 -0
  323. package/tests/MenuSubNestedPosition.test.tsx +48 -0
  324. package/tests/MenuSubSafePolygon.test.tsx +97 -0
  325. package/tests/MenuSubStyled.test.tsx +40 -0
  326. package/tests/MenuTheme.test.tsx +34 -0
  327. package/tests/MenuUnstyled.test.tsx +56 -0
  328. package/tests/MultiDriverAnimation.test.tsx +207 -0
  329. package/tests/NewInputBasic.test.tsx +50 -0
  330. package/tests/NewInputEvents.test.tsx +55 -0
  331. package/tests/OnLayout.test.tsx +163 -0
  332. package/tests/OnLayoutScale.test.tsx +100 -0
  333. package/tests/OnLayoutStress.test.tsx +304 -0
  334. package/tests/ParagraphSpanFontInheritance.test.tsx +73 -0
  335. package/tests/PointerEvents.test.tsx +123 -0
  336. package/tests/Popover.animated.test.tsx +234 -0
  337. package/tests/PopoverAndMenuMultiTrigger.test.tsx +184 -0
  338. package/tests/PopoverAnimatePosition.animated.test.tsx +51 -0
  339. package/tests/PopoverClickDuringEnter.animated.test.tsx +197 -0
  340. package/tests/PopoverFocusScope.test.tsx +242 -0
  341. package/tests/PopoverHoverable.test.tsx +383 -0
  342. package/tests/PopoverHoverableDisableClick.test.tsx +106 -0
  343. package/tests/PopoverHoverableRapid.test.tsx +129 -0
  344. package/tests/PopoverHoverableReposition.test.tsx +111 -0
  345. package/tests/PopoverHoverableScoped.animated.test.tsx +103 -0
  346. package/tests/PopoverHoverableStress.test.tsx +169 -0
  347. package/tests/PopoverInitialPosition.animated.test.tsx +82 -0
  348. package/tests/PopoverMiddlewareSkipRegression.animated.test.tsx +221 -0
  349. package/tests/PopoverScoped.test.tsx +128 -0
  350. package/tests/PopoverScopedPositionGlitch.animated.test.tsx +184 -0
  351. package/tests/PopoverTriggerIsolation.test.tsx +62 -0
  352. package/tests/PseudoTransition.animated.test.tsx +319 -0
  353. package/tests/RawAnimatedValue.test.tsx +147 -0
  354. package/tests/RemoveScroll.test.tsx +223 -0
  355. package/tests/RenderProp.test.tsx +293 -0
  356. package/tests/ScrollViewRef.test.tsx +39 -0
  357. package/tests/SelectClickHold.test.tsx +147 -0
  358. package/tests/SelectFocusScope.test.tsx +176 -0
  359. package/tests/SelectInnerPositioning.test.tsx +82 -0
  360. package/tests/SelectKeyboardNav.test.tsx +173 -0
  361. package/tests/SelectPositioning.test.tsx +56 -0
  362. package/tests/SelectTypeahead.test.tsx +63 -0
  363. package/tests/Shadows.test.tsx +14 -0
  364. package/tests/SheetAnimation.animated.test.tsx +413 -0
  365. package/tests/SheetDrag.animated.test.tsx +223 -0
  366. package/tests/SheetDragResist.animated.test.tsx +393 -0
  367. package/tests/SheetOnAnimationComplete.animated.test.tsx +62 -0
  368. package/tests/SheetScrollLock.animated.test.tsx +287 -0
  369. package/tests/SheetScrollableDrag.animated.test.tsx +1264 -0
  370. package/tests/SheetSnapPointsFit.animated.test.tsx +259 -0
  371. package/tests/ShorthandVariables.test.tsx +44 -0
  372. package/tests/SpinnerCustomColors.test.tsx +67 -0
  373. package/tests/StackZIndex.test.tsx +51 -0
  374. package/tests/StressPagePerf.test.tsx +76 -0
  375. package/tests/StylePlatform.test.tsx +38 -0
  376. package/tests/StyleProp.test.tsx +20 -0
  377. package/tests/StyledAnchor.test.tsx +17 -0
  378. package/tests/StyledButtonTheme.test.tsx +22 -0
  379. package/tests/StyledButtonVariantPseudo.test.tsx +20 -0
  380. package/tests/StyledButtonVariantPseudoMerge.animated.test.tsx +33 -0
  381. package/tests/StyledCheckboxTheme.test.tsx +16 -0
  382. package/tests/StyledContextColor.test.tsx +119 -0
  383. package/tests/StyledContextTokens.test.tsx +56 -0
  384. package/tests/StyledHOCNamed.test.tsx +16 -0
  385. package/tests/StyledHtml.test.tsx +161 -0
  386. package/tests/StyledIconColor.test.tsx +32 -0
  387. package/tests/StyledInputFocusStyle.test.tsx +19 -0
  388. package/tests/StyledInputOnFocus.test.tsx +27 -0
  389. package/tests/StyledMediaQueryMerge.test.tsx +66 -0
  390. package/tests/StyledRNW.test.tsx +17 -0
  391. package/tests/StyledStyleableInputOnFocus.test.tsx +27 -0
  392. package/tests/StyledStyleableInputVariant.test.tsx +22 -0
  393. package/tests/StyledStyledStyleableInputOnFocus.test.tsx +27 -0
  394. package/tests/StyledVariantTextColor.test.tsx +24 -0
  395. package/tests/StyledViewOnFocus.test.tsx +27 -0
  396. package/tests/TabHoverAnimation.animated.test.tsx +468 -0
  397. package/tests/TabHoverPositionSmooth.animated.test.tsx +129 -0
  398. package/tests/TextNestedInheritance.test.tsx +93 -0
  399. package/tests/ThemeChange.test.tsx +70 -0
  400. package/tests/ThemeComponentResolution.test.tsx +82 -0
  401. package/tests/ThemeConditionalName.test.tsx +34 -0
  402. package/tests/ThemeMediaAnimation.test.tsx +65 -0
  403. package/tests/ThemeNested.test.tsx +141 -0
  404. package/tests/ThemeReset.test.tsx +63 -0
  405. package/tests/ThemeShallow.test.tsx +95 -0
  406. package/tests/Toast.test.tsx +106 -0
  407. package/tests/ToggleGroup.test.tsx +61 -0
  408. package/tests/ToggleGroupActiveProps.test.tsx +38 -0
  409. package/tests/ToggleGroupXGroup.test.tsx +172 -0
  410. package/tests/TooltipAnimation.animated.test.tsx +260 -0
  411. package/tests/TooltipEnterInterrupt.animated.test.tsx +76 -0
  412. package/tests/TooltipGlobalPattern.animated.test.tsx +208 -0
  413. package/tests/TooltipGroup.animated.test.tsx +79 -0
  414. package/tests/TooltipMultiTrigger.test.tsx +116 -0
  415. package/tests/TooltipPositionJump.animated.test.tsx +229 -0
  416. package/tests/TooltipPositionJumpNotes.md +219 -0
  417. package/tests/TooltipRapidSwitch.animated.test.tsx +399 -0
  418. package/tests/TooltipTriggerInline.test.tsx +65 -0
  419. package/tests/TransformMediaQueryMerge.test.tsx +104 -0
  420. package/tests/TransitionEnterExit.animated.test.tsx +311 -0
  421. package/tests/UseTheme.test.tsx +16 -0
  422. package/tests/V5ThemeBuilderOutput.test.tsx +164 -0
  423. package/tests/VariantFontFamily.test.tsx +11 -0
  424. package/tests/VariantsOrder.test.tsx +53 -0
  425. package/tests/_debug_position.mjs +52 -0
  426. package/tests/test-utils.ts +106 -0
  427. package/tests/utils.tsx +54 -0
  428. package/tsconfig.json +45 -0
  429. package/vite-env.d.ts +1 -0
  430. package/vite.config.ts +14 -0
  431. package/webpack.config.js +139 -0
@@ -0,0 +1,413 @@
1
+ import { expect, test } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ /**
5
+ * Comprehensive tests for Sheet animation configurations
6
+ * Tests CSS driver, Motion driver, and various animation prop combinations
7
+ * Verifies that different animation configs produce measurably different animation speeds
8
+ */
9
+
10
+ // Helper to measure actual animation duration by tracking position changes
11
+ async function measureSheetAnimationDuration(
12
+ page: any,
13
+ triggerTestId: string,
14
+ frameTestId: string,
15
+ closeTestId: string
16
+ ): Promise<number> {
17
+ const closeButton = page.getByTestId(closeTestId)
18
+
19
+ // click trigger and measure how long until the frame's visual position stabilizes
20
+ const duration = await page.evaluate(
21
+ async ({ triggerId, frameId }: { triggerId: string; frameId: string }) => {
22
+ return new Promise<number>((resolve) => {
23
+ const trigger = document.querySelector(
24
+ `[data-testid="${triggerId}"]`
25
+ ) as HTMLElement
26
+ if (!trigger) {
27
+ resolve(-1)
28
+ return
29
+ }
30
+
31
+ const startTime = performance.now()
32
+ let initialTop = -1
33
+ let lastTop = -1
34
+ let stableCount = 0
35
+ let hasMovedFromInitial = false
36
+ let checkCount = 0
37
+ const maxChecks = 200 // ~10 seconds max
38
+
39
+ trigger.click()
40
+
41
+ const checkAnimation = () => {
42
+ checkCount++
43
+ const frame = document.querySelector(
44
+ `[data-testid="${frameId}"]`
45
+ ) as HTMLElement
46
+ if (!frame) {
47
+ if (checkCount < maxChecks) {
48
+ requestAnimationFrame(checkAnimation)
49
+ } else {
50
+ resolve(performance.now() - startTime)
51
+ }
52
+ return
53
+ }
54
+
55
+ // use getBoundingClientRect which reflects parent transforms
56
+ const currentTop = Math.round(frame.getBoundingClientRect().top)
57
+
58
+ // record the initial position on first check
59
+ if (initialTop === -1) {
60
+ initialTop = currentTop
61
+ }
62
+
63
+ // wait until the position has actually changed from the initial value
64
+ // before we start counting stability (animation might not start for a frame or two)
65
+ if (!hasMovedFromInitial && currentTop !== initialTop) {
66
+ hasMovedFromInitial = true
67
+ }
68
+
69
+ if (hasMovedFromInitial && currentTop === lastTop) {
70
+ stableCount++
71
+ if (stableCount >= 3) {
72
+ resolve(performance.now() - startTime)
73
+ return
74
+ }
75
+ } else {
76
+ stableCount = 0
77
+ lastTop = currentTop
78
+ }
79
+
80
+ if (checkCount < maxChecks) {
81
+ requestAnimationFrame(checkAnimation)
82
+ } else {
83
+ resolve(performance.now() - startTime)
84
+ }
85
+ }
86
+
87
+ requestAnimationFrame(checkAnimation)
88
+ })
89
+ },
90
+ { triggerId: triggerTestId, frameId: frameTestId }
91
+ )
92
+
93
+ // close the sheet for next measurement
94
+ await closeButton.scrollIntoViewIfNeeded()
95
+ await closeButton.click({ timeout: 5000 })
96
+ await page.waitForTimeout(1500)
97
+
98
+ return Math.round(duration)
99
+ }
100
+
101
+ // ============================================================================
102
+ // CSS DRIVER TESTS
103
+ // Note: CSS driver has a known limitation where animation names don't translate
104
+ // to Sheet animation timing (the Sheet uses useAnimatedNumber internally which
105
+ // CSS driver doesn't animate - it relies on CSS transitions for component styles)
106
+ // ============================================================================
107
+ test.describe('Sheet Animation - CSS Driver', () => {
108
+ test.beforeEach(async ({ page }) => {
109
+ await setupPage(page, {
110
+ name: 'SheetAnimationCase',
111
+ type: 'useCase',
112
+ searchParams: { animationDriver: 'css' },
113
+ })
114
+ })
115
+
116
+ test('sheet transform has CSS transition applied', async ({ page }) => {
117
+ // This test verifies that the CSS driver properly applies a CSS transition
118
+ // to the Sheet's transform property, enabling smooth open/close animations
119
+ const trigger = page.getByTestId('animation-quick-trigger')
120
+ const closeButton = page.getByTestId('animation-quick-close')
121
+
122
+ await trigger.click()
123
+ await page.waitForTimeout(100) // Small wait for sheet to start rendering
124
+
125
+ // The transform is on the AnimatedView wrapper, which is the parent of Sheet.Frame
126
+ // We need to find the element with position:absolute that wraps the frame
127
+ const debugInfo = await page.evaluate(() => {
128
+ const frame = document.querySelector('[data-testid="animation-quick-frame"]')
129
+ if (!frame) return { found: false, error: 'frame not found' }
130
+
131
+ // The AnimatedView is a parent with position:absolute and transform
132
+ let animatedView: Element | null = frame
133
+ for (let i = 0; i < 5; i++) {
134
+ animatedView = animatedView?.parentElement || null
135
+ if (!animatedView) break
136
+ const style = getComputedStyle(animatedView)
137
+ if (style.position === 'absolute' && style.transform !== 'none') {
138
+ break
139
+ }
140
+ }
141
+
142
+ if (!animatedView) {
143
+ return { found: false, error: 'animatedView not found' }
144
+ }
145
+
146
+ const style = getComputedStyle(animatedView)
147
+ // Get all style attributes for debugging
148
+ const allStyles: Record<string, string> = {}
149
+ for (let i = 0; i < style.length; i++) {
150
+ const prop = style[i]
151
+ if (prop.includes('transition') || prop.includes('transform')) {
152
+ allStyles[prop] = style.getPropertyValue(prop)
153
+ }
154
+ }
155
+ return {
156
+ found: true,
157
+ tagName: animatedView.tagName,
158
+ transition: style.transition,
159
+ transform: style.transform,
160
+ inlineStyle: (animatedView as HTMLElement).style.cssText,
161
+ allTransitionStyles: allStyles,
162
+ }
163
+ })
164
+
165
+ // Check that there's a transition that applies to transform
166
+ // CSS transition "0.1s ease-in" (without property name) means "all 0.1s ease-in"
167
+ // which applies to all properties including transform
168
+ const hasTransition =
169
+ debugInfo.found && debugInfo.transition && debugInfo.transition !== 'all'
170
+ expect(hasTransition).toBe(true)
171
+
172
+ await closeButton.click()
173
+ await page.waitForTimeout(600)
174
+ })
175
+
176
+ test('sheet actually animates (transform changes over time)', async ({ page }) => {
177
+ // This test verifies that the sheet's transform actually changes over time
178
+ // (i.e., it animates) rather than instantly snapping to position
179
+ const trigger = page.getByTestId('animation-lazy-trigger')
180
+ const closeButton = page.getByTestId('animation-lazy-close')
181
+
182
+ // Click to open the sheet
183
+ await trigger.click()
184
+
185
+ // Wait a small moment for sheet to start appearing
186
+ await page.waitForTimeout(50)
187
+
188
+ // Capture transform values over the animation duration
189
+ const transforms: string[] = []
190
+ for (let i = 0; i < 20; i++) {
191
+ const transform = await page.evaluate(() => {
192
+ // Find the AnimatedView (parent of frame with transform)
193
+ const frame = document.querySelector('[data-testid="animation-lazy-frame"]')
194
+ if (!frame) return 'frame-not-found'
195
+
196
+ let el: Element | null = frame
197
+ for (let j = 0; j < 5; j++) {
198
+ el = el?.parentElement
199
+ if (!el) break
200
+ const style = getComputedStyle(el)
201
+ if (style.position === 'absolute' && style.transform !== 'none') {
202
+ return style.transform
203
+ }
204
+ }
205
+ return 'parent-not-found'
206
+ })
207
+ transforms.push(transform)
208
+ await page.waitForTimeout(30) // ~30ms between samples
209
+ }
210
+
211
+ // Filter out errors and get unique values
212
+ const uniqueTransforms = [
213
+ ...new Set(transforms.filter((t) => !t.includes('not-found') && t !== 'none')),
214
+ ]
215
+
216
+ // If animation is working, we should see multiple distinct transform values
217
+ // as the sheet animates from bottom to its snap point
218
+ expect(uniqueTransforms.length).toBeGreaterThan(1)
219
+
220
+ await closeButton.click()
221
+ await page.waitForTimeout(600)
222
+ })
223
+
224
+ test('all sheet variants open and close correctly', async ({ page }) => {
225
+ // CSS driver doesn't support transitionConfig prop reliably - only test transition variants
226
+ const testIds = ['animation-quick', 'animation-lazy', 'animation-slow']
227
+
228
+ for (const testId of testIds) {
229
+ const trigger = page.getByTestId(`${testId}-trigger`)
230
+ // Use .first() because Sheet passes testId to both Sheet.Frame and SheetCover
231
+ const frame = page.getByTestId(`${testId}-frame`).first()
232
+ const closeButton = page.getByTestId(`${testId}-close`)
233
+
234
+ await trigger.click()
235
+ await expect(frame).toBeVisible({ timeout: 5000 })
236
+
237
+ await closeButton.click()
238
+ // Wait longer for slow animations to complete
239
+ await page.waitForTimeout(1500)
240
+ await expect(frame).not.toBeInViewport({ ratio: 0.5 })
241
+ }
242
+ })
243
+
244
+ // transitionConfig not working reliably with CSS driver
245
+ test.skip('transitionConfig prop works without animation prop', async ({ page }) => {
246
+ // Use .first() because Sheet passes testId to both Sheet.Frame and SheetCover
247
+ const frame = page.getByTestId('transitionConfig-only-frame').first()
248
+ const trigger = page.getByTestId('transitionConfig-only-trigger')
249
+ const closeButton = page.getByTestId('transitionConfig-only-close')
250
+
251
+ await trigger.click()
252
+ await expect(frame).toBeVisible({ timeout: 3000 })
253
+
254
+ // Sheet should open successfully with only transitionConfig
255
+ await closeButton.click()
256
+ await page.waitForTimeout(1500)
257
+ await expect(frame).not.toBeInViewport({ ratio: 0.5 })
258
+ })
259
+ })
260
+
261
+ // ============================================================================
262
+ // MOTION DRIVER TESTS
263
+ // ============================================================================
264
+ test.describe('Sheet Animation - Motion Driver', () => {
265
+ test.beforeEach(async ({ page }) => {
266
+ await setupPage(page, {
267
+ name: 'SheetAnimationCase',
268
+ type: 'useCase',
269
+ searchParams: { animationDriver: 'motion' },
270
+ })
271
+ })
272
+
273
+ test('all sheet variants open and close correctly', async ({ page }) => {
274
+ // Skip transitionConfig variants - not working reliably
275
+ const testIds = ['animation-quick', 'animation-lazy', 'animation-slow']
276
+
277
+ for (const testId of testIds) {
278
+ const trigger = page.getByTestId(`${testId}-trigger`)
279
+ // Use .first() because Sheet passes testId to both Sheet.Frame and SheetCover
280
+ const frame = page.getByTestId(`${testId}-frame`).first()
281
+ const closeButton = page.getByTestId(`${testId}-close`)
282
+
283
+ await trigger.click()
284
+ await expect(frame).toBeVisible({ timeout: 5000 })
285
+
286
+ await closeButton.click()
287
+ // Wait longer for slow animations to complete
288
+ await page.waitForTimeout(2000)
289
+ await expect(frame).not.toBeInViewport({ ratio: 0.5 })
290
+ }
291
+ })
292
+
293
+ test('transitionConfig prop works without animation prop', async ({ page }) => {
294
+ // Use .first() because Sheet passes testId to both Sheet.Frame and SheetCover
295
+ const frame = page.getByTestId('transitionConfig-only-frame').first()
296
+ const trigger = page.getByTestId('transitionConfig-only-trigger')
297
+ const closeButton = page.getByTestId('transitionConfig-only-close')
298
+
299
+ await trigger.click()
300
+ await expect(frame).toBeVisible({ timeout: 3000 })
301
+
302
+ await closeButton.click()
303
+ await page.waitForTimeout(2000)
304
+ await expect(frame).not.toBeInViewport({ ratio: 0.5 })
305
+ })
306
+
307
+ test('transitionConfig overrides transition prop', async ({ page }) => {
308
+ // transition="quick" (fast) + transitionConfig={type:'timing',duration:1000} (slow)
309
+ // if override works, the 1000ms config should make it measurably slower than the 100ms baseline
310
+ const baselineDuration = await measureSheetAnimationDuration(
311
+ page,
312
+ 'transitionConfig-only-trigger',
313
+ 'transitionConfig-only-frame',
314
+ 'transitionConfig-only-close'
315
+ )
316
+
317
+ const overrideDuration = await measureSheetAnimationDuration(
318
+ page,
319
+ 'animation-plus-config-trigger',
320
+ 'animation-plus-config-frame',
321
+ 'animation-plus-config-close'
322
+ )
323
+
324
+ console.info(
325
+ `Motion Driver - baseline (100ms): ${baselineDuration}ms, override (1000ms): ${overrideDuration}ms`
326
+ )
327
+
328
+ // the 1000ms override should be significantly slower than the 100ms baseline
329
+ // use absolute +250ms margin: CI inflates both measurements (spring settling + overhead)
330
+ // which compresses the ratio, but absolute difference stays well above 250ms
331
+ expect(overrideDuration).toBeGreaterThan(baselineDuration + 250)
332
+ })
333
+ })
334
+
335
+ // ============================================================================
336
+ // REANIMATED DRIVER TESTS (default)
337
+ // ============================================================================
338
+ test.describe('Sheet Animation - Reanimated Driver (default)', () => {
339
+ test.beforeEach(async ({ page }) => {
340
+ await setupPage(page, {
341
+ name: 'SheetAnimationCase',
342
+ type: 'useCase',
343
+ // No animationDriver param = default reanimated
344
+ })
345
+ })
346
+
347
+ test('all sheet variants open and close correctly', async ({ page }) => {
348
+ const testIds = ['animation-quick', 'animation-lazy', 'animation-slow']
349
+
350
+ for (const testId of testIds) {
351
+ const trigger = page.getByTestId(`${testId}-trigger`)
352
+ // Use .first() because Sheet passes testId to both Sheet.Frame and SheetCover
353
+ const frame = page.getByTestId(`${testId}-frame`).first()
354
+ const closeButton = page.getByTestId(`${testId}-close`)
355
+
356
+ await trigger.click()
357
+ await expect(frame).toBeVisible({ timeout: 5000 })
358
+
359
+ await closeButton.click()
360
+ // Wait longer for slow animations to complete
361
+ await page.waitForTimeout(2000)
362
+ await expect(frame).not.toBeInViewport({ ratio: 0.5 })
363
+ }
364
+ })
365
+
366
+ test('transitionConfig prop works', async ({ page }) => {
367
+ // transitionConfig only works with JS animation drivers, not CSS
368
+ const driver = (test.info().project?.metadata as any)?.animationDriver
369
+ test.skip(driver === 'css', 'transitionConfig not supported by CSS animation driver')
370
+
371
+ // Use .first() because Sheet passes testId to both Sheet.Frame and SheetCover
372
+ const frame = page.getByTestId('transitionConfig-only-frame').first()
373
+ const trigger = page.getByTestId('transitionConfig-only-trigger')
374
+ const closeButton = page.getByTestId('transitionConfig-only-close')
375
+
376
+ await trigger.click()
377
+ await expect(frame).toBeVisible({ timeout: 3000 })
378
+
379
+ await closeButton.click()
380
+ await page.waitForTimeout(2000)
381
+ await expect(frame).not.toBeInViewport({ ratio: 0.5 })
382
+ })
383
+
384
+ test('transitionConfig overrides transition prop', async ({ page }) => {
385
+ // transitionConfig only works with JS animation drivers, not CSS
386
+ const driver = (test.info().project?.metadata as any)?.animationDriver
387
+ test.skip(driver === 'css', 'transitionConfig not supported by CSS animation driver')
388
+ // transition="quick" (fast) + transitionConfig={type:'timing',duration:1000} (slow)
389
+ // if override works, the 1000ms config should make it measurably slower than the 100ms baseline
390
+ const baselineDuration = await measureSheetAnimationDuration(
391
+ page,
392
+ 'transitionConfig-only-trigger',
393
+ 'transitionConfig-only-frame',
394
+ 'transitionConfig-only-close'
395
+ )
396
+
397
+ const overrideDuration = await measureSheetAnimationDuration(
398
+ page,
399
+ 'animation-plus-config-trigger',
400
+ 'animation-plus-config-frame',
401
+ 'animation-plus-config-close'
402
+ )
403
+
404
+ console.info(
405
+ `Reanimated Driver - baseline (100ms): ${baselineDuration}ms, override (1000ms): ${overrideDuration}ms`
406
+ )
407
+
408
+ // the 1000ms override should be significantly slower than the 100ms baseline
409
+ // use absolute +250ms margin: CI inflates both measurements (spring settling + overhead)
410
+ // which compresses the ratio, but absolute difference stays well above 250ms
411
+ expect(overrideDuration).toBeGreaterThan(baselineDuration + 250)
412
+ })
413
+ })
@@ -0,0 +1,223 @@
1
+ import { expect, test, type Page } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ // mobile viewport with touch support for realistic sheet drag testing
5
+ test.use({
6
+ viewport: { width: 390, height: 844 },
7
+ hasTouch: true,
8
+ isMobile: true,
9
+ })
10
+
11
+ test.beforeEach(async ({ page }) => {
12
+ await setupPage(page, { name: 'SheetDragCase', type: 'useCase' })
13
+ })
14
+
15
+ /**
16
+ * perform a drag gesture using mouse events
17
+ * PanResponder responds to mouse events on web
18
+ */
19
+ async function dragSheet(
20
+ page: Page,
21
+ startX: number,
22
+ startY: number,
23
+ deltaY: number,
24
+ options: { steps?: number; stepDelay?: number } = {}
25
+ ) {
26
+ const { steps = 20, stepDelay = 16 } = options
27
+
28
+ await page.mouse.move(startX, startY)
29
+ await page.mouse.down()
30
+
31
+ for (let i = 1; i <= steps; i++) {
32
+ await page.mouse.move(startX, startY + (deltaY * i) / steps)
33
+ await page.waitForTimeout(stepDelay)
34
+ }
35
+
36
+ await page.mouse.up()
37
+ }
38
+
39
+ test.describe('Sheet drag interactions', () => {
40
+ test('dragging sheet changes snap point position state', async ({ page }) => {
41
+ const trigger = page.getByTestId('drag-percent-trigger')
42
+ const frame = page.getByTestId('drag-percent-frame')
43
+ const handle = page.getByTestId('drag-percent-handle')
44
+ const positionIndicator = page.getByTestId('drag-percent-snap-indicator')
45
+
46
+ await trigger.click()
47
+ await expect(frame).toBeVisible({ timeout: 5000 })
48
+ await page.waitForTimeout(400)
49
+
50
+ await expect(positionIndicator).toContainText('0')
51
+
52
+ const handleBox = await handle.boundingBox()
53
+ expect(handleBox).toBeTruthy()
54
+
55
+ const startX = handleBox!.x + handleBox!.width / 2
56
+ const startY = handleBox!.y + handleBox!.height / 2
57
+
58
+ // drag down to cross threshold between 80% and 40%
59
+ await dragSheet(page, startX, startY, 250)
60
+ await page.waitForTimeout(600)
61
+
62
+ // verify snap point state changed (this works)
63
+ await expect(positionIndicator).toContainText('1')
64
+ })
65
+
66
+ test('dragging sheet down changes snap point (constant mode)', async ({ page }) => {
67
+ const trigger = page.getByTestId('drag-constant-trigger')
68
+ const frame = page.getByTestId('drag-constant-frame')
69
+ const handle = page.getByTestId('drag-constant-handle')
70
+ const positionIndicator = page.getByTestId('drag-constant-snap-indicator')
71
+
72
+ await trigger.click()
73
+ await expect(frame).toBeVisible({ timeout: 5000 })
74
+ await page.waitForTimeout(400)
75
+
76
+ await expect(positionIndicator).toContainText('0')
77
+
78
+ const handleBox = await handle.boundingBox()
79
+ expect(handleBox).toBeTruthy()
80
+
81
+ const startX = handleBox!.x + handleBox!.width / 2
82
+ const startY = handleBox!.y + handleBox!.height / 2
83
+
84
+ // drag 200px to move from 500px to 250px snap point
85
+ await dragSheet(page, startX, startY, 200)
86
+ await page.waitForTimeout(600)
87
+
88
+ // verify snap point state changed
89
+ await expect(positionIndicator).toContainText('1')
90
+ })
91
+
92
+ test('dragging sheet past bottom dismisses it', async ({ page }) => {
93
+ const trigger = page.getByTestId('dismiss-drag-trigger')
94
+ const frame = page.getByTestId('dismiss-drag-frame')
95
+ const handle = page.getByTestId('dismiss-drag-handle')
96
+ const dismissCount = page.getByTestId('dismiss-drag-count')
97
+
98
+ await expect(dismissCount).toContainText('0')
99
+
100
+ await trigger.click()
101
+ await expect(frame).toBeVisible({ timeout: 5000 })
102
+ await page.waitForTimeout(400)
103
+
104
+ const handleBox = await handle.boundingBox()
105
+ expect(handleBox).toBeTruthy()
106
+
107
+ const startX = handleBox!.x + handleBox!.width / 2
108
+ const startY = handleBox!.y + handleBox!.height / 2
109
+
110
+ // drag far down to dismiss (sheet is at 50%)
111
+ await dragSheet(page, startX, startY, 500)
112
+ await page.waitForTimeout(800)
113
+
114
+ await expect(frame).not.toBeInViewport({ ratio: 0.5 })
115
+ await expect(dismissCount).toContainText('1')
116
+ })
117
+
118
+ test('partial drag snaps back to original position', async ({ page }) => {
119
+ const trigger = page.getByTestId('drag-percent-trigger')
120
+ const frame = page.getByTestId('drag-percent-frame')
121
+ const handle = page.getByTestId('drag-percent-handle')
122
+ const positionIndicator = page.getByTestId('drag-percent-snap-indicator')
123
+
124
+ await trigger.click()
125
+ await expect(frame).toBeVisible({ timeout: 5000 })
126
+ await page.waitForTimeout(400)
127
+
128
+ await expect(positionIndicator).toContainText('0')
129
+
130
+ const handleBox = await handle.boundingBox()
131
+ expect(handleBox).toBeTruthy()
132
+
133
+ const startX = handleBox!.x + handleBox!.width / 2
134
+ const startY = handleBox!.y + handleBox!.height / 2
135
+
136
+ // small drag that shouldn't cross threshold - use more steps for smoother movement
137
+ await dragSheet(page, startX, startY, 20, { steps: 10 })
138
+ await page.waitForTimeout(800)
139
+
140
+ await expect(positionIndicator).toContainText('0')
141
+ })
142
+
143
+ // flaky: Playwright mouse events don't trigger PanResponder consistently for subsequent drags
144
+ test.skip('dragging up from lower snap point returns to higher', async ({ page }) => {
145
+ const trigger = page.getByTestId('drag-percent-trigger')
146
+ const frame = page.getByTestId('drag-percent-frame')
147
+ const handle = page.getByTestId('drag-percent-handle')
148
+ const positionIndicator = page.getByTestId('drag-percent-snap-indicator')
149
+
150
+ await trigger.click()
151
+ await expect(frame).toBeVisible({ timeout: 5000 })
152
+ await page.waitForTimeout(400)
153
+
154
+ const handleBox = await handle.boundingBox()
155
+ expect(handleBox).toBeTruthy()
156
+
157
+ const startX = handleBox!.x + handleBox!.width / 2
158
+ const startY = handleBox!.y + handleBox!.height / 2
159
+
160
+ // first drag down to snap point 1
161
+ await dragSheet(page, startX, startY, 250)
162
+ await page.waitForTimeout(600)
163
+
164
+ await expect(positionIndicator).toContainText('1')
165
+
166
+ // wait for animation to fully settle
167
+ await page.waitForTimeout(800)
168
+
169
+ // get new handle position after snapping to lower position
170
+ const newHandleBox = await handle.boundingBox()
171
+ expect(newHandleBox).toBeTruthy()
172
+
173
+ const newStartX = newHandleBox!.x + newHandleBox!.width / 2
174
+ const newStartY = newHandleBox!.y + newHandleBox!.height / 2
175
+
176
+ // drag UP (negative) to return to snap point 0
177
+ await dragSheet(page, newStartX, newStartY, -200, { steps: 25, stepDelay: 16 })
178
+ await page.waitForTimeout(600)
179
+
180
+ // verify we returned to snap point 0
181
+ await expect(positionIndicator).toContainText('0')
182
+ })
183
+
184
+ test('sheet frame moves during drag', async ({ page }) => {
185
+ const trigger = page.getByTestId('drag-percent-trigger')
186
+ const frame = page.getByTestId('drag-percent-frame')
187
+ const handle = page.getByTestId('drag-percent-handle')
188
+
189
+ await trigger.click()
190
+ await expect(frame).toBeVisible({ timeout: 5000 })
191
+ await page.waitForTimeout(400)
192
+
193
+ const handleBox = await handle.boundingBox()
194
+ expect(handleBox).toBeTruthy()
195
+
196
+ // get initial frame position
197
+ const initialBox = await frame.boundingBox()
198
+ expect(initialBox).toBeTruthy()
199
+ const initialTop = initialBox!.y
200
+
201
+ const startX = handleBox!.x + handleBox!.width / 2
202
+ const startY = handleBox!.y + handleBox!.height / 2
203
+
204
+ // start drag and hold mid-drag
205
+ await page.mouse.move(startX, startY)
206
+ await page.mouse.down()
207
+ // move in steps like a real drag
208
+ for (let i = 1; i <= 10; i++) {
209
+ await page.mouse.move(startX, startY + 10 * i)
210
+ await page.waitForTimeout(40)
211
+ }
212
+
213
+ // check frame moved during drag
214
+ const midDragBox = await frame.boundingBox()
215
+ expect(midDragBox).toBeTruthy()
216
+
217
+ await page.mouse.up()
218
+
219
+ // frame should have moved down during drag
220
+ // this verifies the drag gesture is actually being detected by the sheet
221
+ expect(midDragBox!.y).toBeGreaterThan(initialTop)
222
+ })
223
+ })