@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,713 @@
1
+ import { useState, useRef, useCallback, useEffect } from 'react'
2
+ import { AnimatePresence } from '@hanzogui/animate-presence'
3
+ import { Button, Paragraph, Square, XStack, YStack, View } from '@hanzo/gui'
4
+
5
+ /**
6
+ * EXIT COMPLETION TEST SUITE
7
+ *
8
+ * Tests exit animation completion invariants:
9
+ * 1. sendExitComplete is called exactly once per exit cycle
10
+ * 2. Completion fires only after animations actually finish
11
+ * 3. Interrupted/canceled animations don't cause premature completion
12
+ * 4. Zero-duration animations complete correctly
13
+ * 5. Rapid re-renders during exit don't cause duplicate completions
14
+ *
15
+ * Each scenario logs completion events for verification:
16
+ * [EXIT_COMPLETE] scenario:<id> count:<n> time:<ms>
17
+ */
18
+
19
+ // helper to expose completion tracking to window for test access
20
+ declare global {
21
+ interface Window {
22
+ __exitCompletionCounts: Record<string, number>
23
+ __exitCompletionTimes: Record<string, number[]>
24
+ __resetExitTracking: () => void
25
+ }
26
+ }
27
+
28
+ if (typeof window !== 'undefined') {
29
+ window.__exitCompletionCounts = {}
30
+ window.__exitCompletionTimes = {}
31
+ window.__resetExitTracking = () => {
32
+ window.__exitCompletionCounts = {}
33
+ window.__exitCompletionTimes = {}
34
+ }
35
+ }
36
+
37
+ function useExitTracker(scenarioId: string) {
38
+ const countRef = useRef(0)
39
+ const startTimeRef = useRef(0)
40
+
41
+ const startExit = useCallback(() => {
42
+ countRef.current = 0
43
+ startTimeRef.current = Date.now()
44
+ if (typeof window !== 'undefined') {
45
+ window.__exitCompletionCounts[scenarioId] = 0
46
+ window.__exitCompletionTimes[scenarioId] = []
47
+ }
48
+ console.log(`[EXIT_START] scenario:${scenarioId} time:${startTimeRef.current}`)
49
+ }, [scenarioId])
50
+
51
+ const onExitComplete = useCallback(() => {
52
+ countRef.current++
53
+ const elapsed = Date.now() - startTimeRef.current
54
+ if (typeof window !== 'undefined') {
55
+ window.__exitCompletionCounts[scenarioId] = countRef.current
56
+ window.__exitCompletionTimes[scenarioId] =
57
+ window.__exitCompletionTimes[scenarioId] || []
58
+ window.__exitCompletionTimes[scenarioId].push(elapsed)
59
+ }
60
+ console.log(
61
+ `[EXIT_COMPLETE] scenario:${scenarioId} count:${countRef.current} time:${elapsed}ms`
62
+ )
63
+ }, [scenarioId])
64
+
65
+ return { startExit, onExitComplete, getCount: () => countRef.current }
66
+ }
67
+
68
+ export function ExitCompletionCase() {
69
+ return (
70
+ <YStack gap="$2" padding="$2" flex={1} overflow="scroll">
71
+ <Paragraph fontWeight="bold" fontSize="$5">
72
+ Exit Completion Test Suite
73
+ </Paragraph>
74
+ <Paragraph size="$2" color="$color10">
75
+ Tests sendExitComplete invariants. Check console for [EXIT_COMPLETE] logs.
76
+ </Paragraph>
77
+
78
+ <SectionHeader>1. Basic Exit Completion</SectionHeader>
79
+ <Scenario01_BasicExit />
80
+ <Scenario02_ZeroDuration />
81
+ <Scenario03_VeryShortDuration />
82
+
83
+ <SectionHeader>2. Duplicate Completion Guards</SectionHeader>
84
+ <Scenario04_RapidToggle />
85
+ <Scenario05_ReRenderDuringExit />
86
+ <Scenario06_MultipleChildren />
87
+
88
+ <SectionHeader>3. Timing Validation</SectionHeader>
89
+ <Scenario07_LongAnimation />
90
+ <Scenario08_InterruptedExit />
91
+ <Scenario09_CanceledAndRestarted />
92
+
93
+ <SectionHeader>4. Per-Property Exit</SectionHeader>
94
+ <Scenario10_PerPropertyExit />
95
+ <Scenario11_MixedDurationExit />
96
+
97
+ <SectionHeader>5. AnimateOnly & Transform Sub-Keys</SectionHeader>
98
+ <Scenario51_AnimateOnlyExclusion />
99
+ <Scenario53_TransformSubKeySplitDurations />
100
+ <Scenario55_ZeroAnimatableExitProps />
101
+ </YStack>
102
+ )
103
+ }
104
+
105
+ const SectionHeader = ({ children }: { children: string }) => (
106
+ <Paragraph fontWeight="bold" fontSize="$3" marginTop="$3" color="$blue10">
107
+ {children}
108
+ </Paragraph>
109
+ )
110
+
111
+ // ============================================================================
112
+ // SCENARIO 01: Basic Exit - should complete exactly once
113
+ // ============================================================================
114
+ function Scenario01_BasicExit() {
115
+ const [visible, setVisible] = useState(true)
116
+ const { startExit, onExitComplete } = useExitTracker('01-basic-exit')
117
+
118
+ return (
119
+ <XStack gap="$2" alignItems="center" minHeight={50}>
120
+ <Button
121
+ size="$2"
122
+ onPress={() => {
123
+ if (visible) startExit()
124
+ setVisible(!visible)
125
+ }}
126
+ testID="exit-01-trigger"
127
+ data-testid="exit-01-trigger"
128
+ >
129
+ 01: Basic Exit
130
+ </Button>
131
+ <AnimatePresence onExitComplete={onExitComplete}>
132
+ {visible && (
133
+ <Square
134
+ key="basic-exit"
135
+ transition="300ms"
136
+ size={40}
137
+ bg="$blue10"
138
+ exitStyle={{ opacity: 0, scale: 0.5 }}
139
+ testID="exit-01-target"
140
+ data-testid="exit-01-target"
141
+ />
142
+ )}
143
+ </AnimatePresence>
144
+ <Paragraph size="$1" testID="exit-01-status" data-testid="exit-01-status">
145
+ {visible ? 'visible' : 'hidden'}
146
+ </Paragraph>
147
+ </XStack>
148
+ )
149
+ }
150
+
151
+ // ============================================================================
152
+ // SCENARIO 02: Zero Duration - edge case that can race
153
+ // Tests finding #3: race where callbacks fire before keys registered
154
+ // ============================================================================
155
+ function Scenario02_ZeroDuration() {
156
+ const [visible, setVisible] = useState(true)
157
+ const { startExit, onExitComplete } = useExitTracker('02-zero-duration')
158
+
159
+ return (
160
+ <XStack gap="$2" alignItems="center" minHeight={50}>
161
+ <Button
162
+ size="$2"
163
+ onPress={() => {
164
+ if (visible) startExit()
165
+ setVisible(!visible)
166
+ }}
167
+ testID="exit-02-trigger"
168
+ data-testid="exit-02-trigger"
169
+ >
170
+ 02: Zero Duration
171
+ </Button>
172
+ <AnimatePresence onExitComplete={onExitComplete}>
173
+ {visible && (
174
+ <Square
175
+ key="zero-duration"
176
+ transition="0ms"
177
+ size={40}
178
+ bg="$green10"
179
+ exitStyle={{ opacity: 0 }}
180
+ testID="exit-02-target"
181
+ data-testid="exit-02-target"
182
+ />
183
+ )}
184
+ </AnimatePresence>
185
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
186
+ </XStack>
187
+ )
188
+ }
189
+
190
+ // ============================================================================
191
+ // SCENARIO 03: Very Short Duration (30ms) - another race edge case
192
+ // ============================================================================
193
+ function Scenario03_VeryShortDuration() {
194
+ const [visible, setVisible] = useState(true)
195
+ const { startExit, onExitComplete } = useExitTracker('03-short-duration')
196
+
197
+ return (
198
+ <XStack gap="$2" alignItems="center" minHeight={50}>
199
+ <Button
200
+ size="$2"
201
+ onPress={() => {
202
+ if (visible) startExit()
203
+ setVisible(!visible)
204
+ }}
205
+ testID="exit-03-trigger"
206
+ data-testid="exit-03-trigger"
207
+ >
208
+ 03: 30ms Duration
209
+ </Button>
210
+ <AnimatePresence onExitComplete={onExitComplete}>
211
+ {visible && (
212
+ <Square
213
+ key="short-duration"
214
+ transition="30ms"
215
+ size={40}
216
+ bg="$yellow10"
217
+ exitStyle={{ opacity: 0, scale: 0.8 }}
218
+ testID="exit-03-target"
219
+ data-testid="exit-03-target"
220
+ />
221
+ )}
222
+ </AnimatePresence>
223
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
224
+ </XStack>
225
+ )
226
+ }
227
+
228
+ // ============================================================================
229
+ // SCENARIO 04: Rapid Toggle - tests finding #4/#5: duplicate completions
230
+ // Rapidly toggle visibility to stress test the exit cycle guards
231
+ // ============================================================================
232
+ function Scenario04_RapidToggle() {
233
+ const [visible, setVisible] = useState(true)
234
+ const { startExit, onExitComplete } = useExitTracker('04-rapid-toggle')
235
+
236
+ const handleRapidToggle = () => {
237
+ startExit()
238
+ // toggle off
239
+ setVisible(false)
240
+ // toggle back on quickly (before exit completes)
241
+ setTimeout(() => setVisible(true), 50)
242
+ // toggle off again
243
+ setTimeout(() => setVisible(false), 100)
244
+ }
245
+
246
+ return (
247
+ <XStack gap="$2" alignItems="center" minHeight={50}>
248
+ <Button
249
+ size="$2"
250
+ onPress={handleRapidToggle}
251
+ testID="exit-04-trigger"
252
+ data-testid="exit-04-trigger"
253
+ >
254
+ 04: Rapid Toggle
255
+ </Button>
256
+ <AnimatePresence onExitComplete={onExitComplete}>
257
+ {visible && (
258
+ <Square
259
+ key="rapid-toggle"
260
+ transition="200ms"
261
+ size={40}
262
+ bg="$red10"
263
+ exitStyle={{ opacity: 0, scale: 0.5 }}
264
+ testID="exit-04-target"
265
+ data-testid="exit-04-target"
266
+ />
267
+ )}
268
+ </AnimatePresence>
269
+ <Paragraph size="$1" testID="exit-04-status" data-testid="exit-04-status">
270
+ {visible ? 'visible' : 'hidden'}
271
+ </Paragraph>
272
+ </XStack>
273
+ )
274
+ }
275
+
276
+ // ============================================================================
277
+ // SCENARIO 05: Re-render During Exit - tests finding #4: flush restart
278
+ // Force re-renders during exit animation via unrelated state
279
+ // ============================================================================
280
+ function Scenario05_ReRenderDuringExit() {
281
+ const [visible, setVisible] = useState(true)
282
+ const [counter, setCounter] = useState(0)
283
+ const { startExit, onExitComplete } = useExitTracker('05-rerender-during-exit')
284
+
285
+ const handleExitWithRerenders = () => {
286
+ startExit()
287
+ setVisible(false)
288
+ // force multiple re-renders during exit
289
+ setTimeout(() => setCounter((c) => c + 1), 50)
290
+ setTimeout(() => setCounter((c) => c + 1), 100)
291
+ setTimeout(() => setCounter((c) => c + 1), 150)
292
+ }
293
+
294
+ return (
295
+ <XStack gap="$2" alignItems="center" minHeight={50}>
296
+ <Button
297
+ size="$2"
298
+ onPress={handleExitWithRerenders}
299
+ testID="exit-05-trigger"
300
+ data-testid="exit-05-trigger"
301
+ >
302
+ 05: Rerender During
303
+ </Button>
304
+ <AnimatePresence onExitComplete={onExitComplete}>
305
+ {visible && (
306
+ <Square
307
+ key="rerender-exit"
308
+ transition="300ms"
309
+ size={40}
310
+ bg="$blue10"
311
+ exitStyle={{ opacity: 0, y: 20 }}
312
+ testID="exit-05-target"
313
+ data-testid="exit-05-target"
314
+ />
315
+ )}
316
+ </AnimatePresence>
317
+ <Paragraph size="$1">
318
+ counter: {counter} | {visible ? 'visible' : 'hidden'}
319
+ </Paragraph>
320
+ </XStack>
321
+ )
322
+ }
323
+
324
+ // ============================================================================
325
+ // SCENARIO 06: Multiple Children Exiting - each should complete once
326
+ // ============================================================================
327
+ function Scenario06_MultipleChildren() {
328
+ const [items, setItems] = useState([1, 2, 3])
329
+ const { startExit, onExitComplete } = useExitTracker('06-multiple-children')
330
+
331
+ const handleRemoveAll = () => {
332
+ startExit()
333
+ setItems([])
334
+ }
335
+
336
+ const handleReset = () => {
337
+ setItems([1, 2, 3])
338
+ }
339
+
340
+ return (
341
+ <XStack gap="$2" alignItems="center" minHeight={50}>
342
+ <Button
343
+ size="$2"
344
+ onPress={handleRemoveAll}
345
+ testID="exit-06-trigger"
346
+ data-testid="exit-06-trigger"
347
+ >
348
+ 06: Remove All
349
+ </Button>
350
+ <Button size="$2" onPress={handleReset}>
351
+ Reset
352
+ </Button>
353
+ <AnimatePresence onExitComplete={onExitComplete}>
354
+ {items.map((id) => (
355
+ <Square
356
+ key={`multi-${id}`}
357
+ transition="200ms"
358
+ size={30}
359
+ bg="$green10"
360
+ exitStyle={{ opacity: 0, scale: 0.5 }}
361
+ testID={`exit-06-target-${id}`}
362
+ data-testid={`exit-06-target-${id}`}
363
+ />
364
+ ))}
365
+ </AnimatePresence>
366
+ <Paragraph size="$1">count: {items.length}</Paragraph>
367
+ </XStack>
368
+ )
369
+ }
370
+
371
+ // ============================================================================
372
+ // SCENARIO 07: Long Animation - verify completion timing
373
+ // 500ms animation should not complete before ~500ms
374
+ // ============================================================================
375
+ function Scenario07_LongAnimation() {
376
+ const [visible, setVisible] = useState(true)
377
+ const { startExit, onExitComplete } = useExitTracker('07-long-animation')
378
+
379
+ return (
380
+ <XStack gap="$2" alignItems="center" minHeight={50}>
381
+ <Button
382
+ size="$2"
383
+ onPress={() => {
384
+ if (visible) startExit()
385
+ setVisible(!visible)
386
+ }}
387
+ testID="exit-07-trigger"
388
+ data-testid="exit-07-trigger"
389
+ >
390
+ 07: 500ms Exit
391
+ </Button>
392
+ <AnimatePresence onExitComplete={onExitComplete}>
393
+ {visible && (
394
+ <Square
395
+ key="long-anim"
396
+ transition="500ms"
397
+ size={40}
398
+ bg="$yellow10"
399
+ exitStyle={{ opacity: 0, scale: 0.5, y: 30 }}
400
+ testID="exit-07-target"
401
+ data-testid="exit-07-target"
402
+ />
403
+ )}
404
+ </AnimatePresence>
405
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
406
+ </XStack>
407
+ )
408
+ }
409
+
410
+ // ============================================================================
411
+ // SCENARIO 08: Interrupted Exit - tests finding #2: canceled = completed
412
+ // Start exit, then cancel by showing again, then exit again
413
+ // Should only get ONE completion for the final successful exit
414
+ // ============================================================================
415
+ function Scenario08_InterruptedExit() {
416
+ const [visible, setVisible] = useState(true)
417
+ const { startExit, onExitComplete } = useExitTracker('08-interrupted')
418
+
419
+ const handleInterruptedExit = () => {
420
+ startExit()
421
+ // start exiting
422
+ setVisible(false)
423
+ // interrupt by showing again after 100ms (during 300ms animation)
424
+ setTimeout(() => setVisible(true), 100)
425
+ // then exit for real after 200ms
426
+ setTimeout(() => setVisible(false), 200)
427
+ }
428
+
429
+ return (
430
+ <XStack gap="$2" alignItems="center" minHeight={50}>
431
+ <Button
432
+ size="$2"
433
+ onPress={handleInterruptedExit}
434
+ testID="exit-08-trigger"
435
+ data-testid="exit-08-trigger"
436
+ >
437
+ 08: Interrupted
438
+ </Button>
439
+ <AnimatePresence onExitComplete={onExitComplete}>
440
+ {visible && (
441
+ <Square
442
+ key="interrupted"
443
+ transition="300ms"
444
+ size={40}
445
+ bg="$red10"
446
+ exitStyle={{ opacity: 0, scale: 0.5 }}
447
+ testID="exit-08-target"
448
+ data-testid="exit-08-target"
449
+ />
450
+ )}
451
+ </AnimatePresence>
452
+ <Paragraph size="$1" testID="exit-08-status" data-testid="exit-08-status">
453
+ {visible ? 'visible' : 'hidden'}
454
+ </Paragraph>
455
+ </XStack>
456
+ )
457
+ }
458
+
459
+ // ============================================================================
460
+ // SCENARIO 09: Canceled and Restarted Multiple Times
461
+ // Extreme stress test: cancel/restart exit 5 times
462
+ // ============================================================================
463
+ function Scenario09_CanceledAndRestarted() {
464
+ const [visible, setVisible] = useState(true)
465
+ const { startExit, onExitComplete } = useExitTracker('09-cancel-restart')
466
+
467
+ const handleStressTest = () => {
468
+ startExit()
469
+ // rapid cancel/restart cycle
470
+ setVisible(false)
471
+ setTimeout(() => setVisible(true), 30)
472
+ setTimeout(() => setVisible(false), 60)
473
+ setTimeout(() => setVisible(true), 90)
474
+ setTimeout(() => setVisible(false), 120)
475
+ setTimeout(() => setVisible(true), 150)
476
+ // final exit
477
+ setTimeout(() => setVisible(false), 180)
478
+ }
479
+
480
+ return (
481
+ <XStack gap="$2" alignItems="center" minHeight={50}>
482
+ <Button
483
+ size="$2"
484
+ onPress={handleStressTest}
485
+ testID="exit-09-trigger"
486
+ data-testid="exit-09-trigger"
487
+ >
488
+ 09: Stress Test
489
+ </Button>
490
+ <AnimatePresence onExitComplete={onExitComplete}>
491
+ {visible && (
492
+ <Square
493
+ key="stress"
494
+ transition="200ms"
495
+ size={40}
496
+ bg="$blue10"
497
+ exitStyle={{ opacity: 0, x: -20 }}
498
+ testID="exit-09-target"
499
+ data-testid="exit-09-target"
500
+ />
501
+ )}
502
+ </AnimatePresence>
503
+ <Paragraph size="$1" testID="exit-09-status" data-testid="exit-09-status">
504
+ {visible ? 'visible' : 'hidden'}
505
+ </Paragraph>
506
+ </XStack>
507
+ )
508
+ }
509
+
510
+ // ============================================================================
511
+ // SCENARIO 10: Per-Property Exit - tests finding #1: emitter-driven keys
512
+ // Different properties have different durations during exit
513
+ // Completion should wait for the LONGEST one
514
+ // ============================================================================
515
+ function Scenario10_PerPropertyExit() {
516
+ const [visible, setVisible] = useState(true)
517
+ const { startExit, onExitComplete } = useExitTracker('10-per-property')
518
+
519
+ return (
520
+ <XStack gap="$2" alignItems="center" minHeight={50}>
521
+ <Button
522
+ size="$2"
523
+ onPress={() => {
524
+ if (visible) startExit()
525
+ setVisible(!visible)
526
+ }}
527
+ testID="exit-10-trigger"
528
+ data-testid="exit-10-trigger"
529
+ >
530
+ 10: Per-Property
531
+ </Button>
532
+ <AnimatePresence onExitComplete={onExitComplete}>
533
+ {visible && (
534
+ <Square
535
+ key="per-prop"
536
+ // opacity=100ms (fast), scale=500ms (slow)
537
+ transition={['100ms', { scale: '500ms' }] as any}
538
+ size={40}
539
+ bg="$green10"
540
+ exitStyle={{ opacity: 0, scale: 0.5 }}
541
+ testID="exit-10-target"
542
+ data-testid="exit-10-target"
543
+ />
544
+ )}
545
+ </AnimatePresence>
546
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
547
+ </XStack>
548
+ )
549
+ }
550
+
551
+ // ============================================================================
552
+ // SCENARIO 11: Mixed Duration with Transform Exit
553
+ // opacity=100ms, transform properties=400ms
554
+ // Should complete after ~400ms, not 100ms
555
+ // ============================================================================
556
+ function Scenario11_MixedDurationExit() {
557
+ const [visible, setVisible] = useState(true)
558
+ const { startExit, onExitComplete } = useExitTracker('11-mixed-duration')
559
+
560
+ return (
561
+ <XStack gap="$2" alignItems="center" minHeight={50}>
562
+ <Button
563
+ size="$2"
564
+ onPress={() => {
565
+ if (visible) startExit()
566
+ setVisible(!visible)
567
+ }}
568
+ testID="exit-11-trigger"
569
+ data-testid="exit-11-trigger"
570
+ >
571
+ 11: Mixed Duration
572
+ </Button>
573
+ <AnimatePresence onExitComplete={onExitComplete}>
574
+ {visible && (
575
+ <Square
576
+ key="mixed"
577
+ transition={{ opacity: '100ms', scale: '400ms', default: '400ms' } as any}
578
+ size={40}
579
+ bg="$yellow10"
580
+ exitStyle={{ opacity: 0, scale: 0.5, y: 20 }}
581
+ testID="exit-11-target"
582
+ data-testid="exit-11-target"
583
+ />
584
+ )}
585
+ </AnimatePresence>
586
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
587
+ </XStack>
588
+ )
589
+ }
590
+
591
+ // ============================================================================
592
+ // SCENARIO 51: AnimateOnly Exclusion
593
+ // animateOnly=['opacity'] should exclude scale from pending set
594
+ // Even if scale has a long exit config (500ms), should complete based on opacity (100ms)
595
+ // Tests: pending-set only includes keys in animateOnly filter
596
+ // ============================================================================
597
+ function Scenario51_AnimateOnlyExclusion() {
598
+ const [visible, setVisible] = useState(true)
599
+ const { startExit, onExitComplete } = useExitTracker('51-animateonly-exclusion')
600
+
601
+ return (
602
+ <XStack gap="$2" alignItems="center" minHeight={50}>
603
+ <Button
604
+ size="$2"
605
+ onPress={() => {
606
+ if (visible) startExit()
607
+ setVisible(!visible)
608
+ }}
609
+ testID="exit-51-trigger"
610
+ data-testid="exit-51-trigger"
611
+ >
612
+ 51: AnimateOnly
613
+ </Button>
614
+ <AnimatePresence onExitComplete={onExitComplete}>
615
+ {visible && (
616
+ <Square
617
+ key="animateonly-exclusion"
618
+ transition={{ opacity: '100ms', scale: '500ms', default: '500ms' } as any}
619
+ animateOnly={['opacity']}
620
+ size={40}
621
+ bg="$purple10"
622
+ exitStyle={{ opacity: 0, scale: 0.5 }}
623
+ testID="exit-51-target"
624
+ data-testid="exit-51-target"
625
+ />
626
+ )}
627
+ </AnimatePresence>
628
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
629
+ </XStack>
630
+ )
631
+ }
632
+
633
+ // ============================================================================
634
+ // SCENARIO 53: Transform Sub-Key Split Durations
635
+ // scale=100ms, y=500ms - completion should wait for y (500ms)
636
+ // Tests: transform sub-keys (scale, y) tracked individually
637
+ // ============================================================================
638
+ function Scenario53_TransformSubKeySplitDurations() {
639
+ const [visible, setVisible] = useState(true)
640
+ const { startExit, onExitComplete } = useExitTracker('53-transform-subkeys')
641
+
642
+ return (
643
+ <XStack gap="$2" alignItems="center" minHeight={50}>
644
+ <Button
645
+ size="$2"
646
+ onPress={() => {
647
+ if (visible) startExit()
648
+ setVisible(!visible)
649
+ }}
650
+ testID="exit-53-trigger"
651
+ data-testid="exit-53-trigger"
652
+ >
653
+ 53: Transform Sub-Keys
654
+ </Button>
655
+ <AnimatePresence onExitComplete={onExitComplete}>
656
+ {visible && (
657
+ <Square
658
+ key="transform-subkeys"
659
+ transition={{ scale: '100ms', y: '500ms', default: '100ms' } as any}
660
+ size={40}
661
+ bg="$orange10"
662
+ exitStyle={{ scale: 0.5, y: 50 }}
663
+ testID="exit-53-target"
664
+ data-testid="exit-53-target"
665
+ />
666
+ )}
667
+ </AnimatePresence>
668
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
669
+ </XStack>
670
+ )
671
+ }
672
+
673
+ // ============================================================================
674
+ // SCENARIO 55: Zero Animatable Exit Props
675
+ // exitStyle exists but no animatable properties (only static props)
676
+ // Should complete immediately (no animations to wait for)
677
+ // Tests: immediate completion code path
678
+ // ============================================================================
679
+ function Scenario55_ZeroAnimatableExitProps() {
680
+ const [visible, setVisible] = useState(true)
681
+ const { startExit, onExitComplete } = useExitTracker('55-zero-animatable')
682
+
683
+ return (
684
+ <XStack gap="$2" alignItems="center" minHeight={50}>
685
+ <Button
686
+ size="$2"
687
+ onPress={() => {
688
+ if (visible) startExit()
689
+ setVisible(!visible)
690
+ }}
691
+ testID="exit-55-trigger"
692
+ data-testid="exit-55-trigger"
693
+ >
694
+ 55: Zero Animatable
695
+ </Button>
696
+ <AnimatePresence onExitComplete={onExitComplete}>
697
+ {visible && (
698
+ <Square
699
+ key="zero-animatable"
700
+ transition="300ms"
701
+ animateOnly={[]}
702
+ size={40}
703
+ bg="$gray10"
704
+ exitStyle={{ opacity: 0, scale: 0.5 }}
705
+ testID="exit-55-target"
706
+ data-testid="exit-55-target"
707
+ />
708
+ )}
709
+ </AnimatePresence>
710
+ <Paragraph size="$1">{visible ? 'visible' : 'hidden'}</Paragraph>
711
+ </XStack>
712
+ )
713
+ }