@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,61 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ test.describe('ToggleGroup', () => {
4
+ test.beforeEach(async ({ page }) => {
5
+ await page.goto('/?demo=ToggleGroup')
6
+ await page.waitForLoadState('networkidle')
7
+ })
8
+
9
+ test('single mode: clicking toggle item should toggle active state', async ({
10
+ page,
11
+ }) => {
12
+ await page.waitForSelector('[aria-label="Left aligned"]')
13
+
14
+ const leftButton = page.locator('[aria-label="Left aligned"]').first()
15
+
16
+ const initialState = await leftButton.getAttribute('data-state')
17
+ expect(initialState).toBe('off')
18
+
19
+ await leftButton.click()
20
+ await page.waitForTimeout(100)
21
+
22
+ const stateAfterClick = await leftButton.getAttribute('data-state')
23
+ expect(stateAfterClick).toBe('on')
24
+ })
25
+
26
+ test('single mode with disableDeactivation: cannot turn off active item', async ({
27
+ page,
28
+ }) => {
29
+ await page.waitForSelector('[aria-label="Left aligned"]')
30
+
31
+ const leftButton = page.locator('[aria-label="Left aligned"]').first()
32
+
33
+ await leftButton.click()
34
+ await page.waitForTimeout(100)
35
+ expect(await leftButton.getAttribute('data-state')).toBe('on')
36
+
37
+ // clicking again should not turn it off due to disableDeactivation
38
+ await leftButton.click()
39
+ await page.waitForTimeout(100)
40
+ expect(await leftButton.getAttribute('data-state')).toBe('on')
41
+ })
42
+
43
+ test('single mode: clicking different item should change selection', async ({
44
+ page,
45
+ }) => {
46
+ await page.waitForSelector('[aria-label="Left aligned"]')
47
+
48
+ const leftButton = page.locator('[aria-label="Left aligned"]').first()
49
+ const centerButton = page.locator('[aria-label="Center aligned"]').first()
50
+
51
+ await leftButton.click()
52
+ await page.waitForTimeout(100)
53
+ expect(await leftButton.getAttribute('data-state')).toBe('on')
54
+ expect(await centerButton.getAttribute('data-state')).toBe('off')
55
+
56
+ await centerButton.click()
57
+ await page.waitForTimeout(100)
58
+ expect(await leftButton.getAttribute('data-state')).toBe('off')
59
+ expect(await centerButton.getAttribute('data-state')).toBe('on')
60
+ })
61
+ })
@@ -0,0 +1,38 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ import { setupPage } from './test-utils'
4
+
5
+ test.beforeEach(async ({ page }) => {
6
+ await setupPage(page, { name: 'ToggleGroupActiveProps', type: 'useCase' })
7
+ })
8
+
9
+ test(`ToggleGroup.Item passes active prop to children - initially selected item`, async ({
10
+ page,
11
+ }) => {
12
+ // option1 is selected by default
13
+ const activeItem = page.locator('#custom-item-option1')
14
+ await expect(activeItem).toHaveAttribute('data-active', 'true')
15
+ })
16
+
17
+ test(`ToggleGroup.Item passes active prop to children - non-selected items`, async ({
18
+ page,
19
+ }) => {
20
+ // option2 and option3 are not selected
21
+ const item2 = page.locator('#custom-item-option2')
22
+ const item3 = page.locator('#custom-item-option3')
23
+
24
+ await expect(item2).toHaveAttribute('data-active', 'false')
25
+ await expect(item3).toHaveAttribute('data-active', 'false')
26
+ })
27
+
28
+ test(`ToggleGroup.Item updates active prop when selection changes`, async ({ page }) => {
29
+ // Click on option2
30
+ await page.click('#item-2')
31
+
32
+ // option2 should now be active
33
+ const item1 = page.locator('#custom-item-option1')
34
+ const item2 = page.locator('#custom-item-option2')
35
+
36
+ await expect(item1).toHaveAttribute('data-active', 'false')
37
+ await expect(item2).toHaveAttribute('data-active', 'true')
38
+ })
@@ -0,0 +1,172 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ test.describe('ToggleGroup + XGroup integration', () => {
4
+ test.beforeEach(async ({ page }) => {
5
+ await page.goto('/?test=ToggleGroupXGroupCase')
6
+ await page.waitForLoadState('networkidle')
7
+ })
8
+
9
+ test.describe('Pattern A: XGroup.Item wraps ToggleGroup.Item', () => {
10
+ test('clicking item changes toggle value', async ({ page }) => {
11
+ const leftButton = page.getByTestId('pattern-a-left')
12
+ await leftButton.waitFor({ state: 'visible' })
13
+
14
+ // initial state should be off
15
+ const initialState = await leftButton.getAttribute('data-state')
16
+ expect(initialState).toBe('off')
17
+
18
+ // click to toggle on
19
+ await leftButton.click()
20
+ await page.waitForTimeout(100)
21
+
22
+ const stateAfterClick = await leftButton.getAttribute('data-state')
23
+ expect(stateAfterClick).toBe('on')
24
+ })
25
+
26
+ test('clicking different items changes selection', async ({ page }) => {
27
+ const leftButton = page.getByTestId('pattern-a-left')
28
+ const centerButton = page.getByTestId('pattern-a-center')
29
+ await leftButton.waitFor({ state: 'visible' })
30
+
31
+ await leftButton.click()
32
+ await page.waitForTimeout(100)
33
+ expect(await leftButton.getAttribute('data-state')).toBe('on')
34
+ expect(await centerButton.getAttribute('data-state')).toBe('off')
35
+
36
+ await centerButton.click()
37
+ await page.waitForTimeout(100)
38
+ expect(await leftButton.getAttribute('data-state')).toBe('off')
39
+ expect(await centerButton.getAttribute('data-state')).toBe('on')
40
+ })
41
+
42
+ test('XGroup properly applies radius styles to items', async ({ page }) => {
43
+ const leftButton = page.getByTestId('pattern-a-left')
44
+ const centerButton = page.getByTestId('pattern-a-center')
45
+ const rightButton = page.getByTestId('pattern-a-right')
46
+ await leftButton.waitFor({ state: 'visible' })
47
+
48
+ // first item: should have left border radius, zeroed right
49
+ const leftStyles = await leftButton.evaluate((el) => {
50
+ const style = window.getComputedStyle(el)
51
+ return {
52
+ borderTopLeftRadius: style.borderTopLeftRadius,
53
+ borderBottomLeftRadius: style.borderBottomLeftRadius,
54
+ borderTopRightRadius: style.borderTopRightRadius,
55
+ borderBottomRightRadius: style.borderBottomRightRadius,
56
+ }
57
+ })
58
+ // first item should have zero right radii
59
+ expect(parseFloat(leftStyles.borderTopRightRadius)).toBe(0)
60
+ expect(parseFloat(leftStyles.borderBottomRightRadius)).toBe(0)
61
+
62
+ // center item: should have all corners zeroed
63
+ const centerStyles = await centerButton.evaluate((el) => {
64
+ const style = window.getComputedStyle(el)
65
+ return {
66
+ borderTopLeftRadius: style.borderTopLeftRadius,
67
+ borderBottomLeftRadius: style.borderBottomLeftRadius,
68
+ borderTopRightRadius: style.borderTopRightRadius,
69
+ borderBottomRightRadius: style.borderBottomRightRadius,
70
+ }
71
+ })
72
+ expect(parseFloat(centerStyles.borderTopLeftRadius)).toBe(0)
73
+ expect(parseFloat(centerStyles.borderBottomLeftRadius)).toBe(0)
74
+ expect(parseFloat(centerStyles.borderTopRightRadius)).toBe(0)
75
+ expect(parseFloat(centerStyles.borderBottomRightRadius)).toBe(0)
76
+
77
+ // last item: should have zero left radii
78
+ const rightStyles = await rightButton.evaluate((el) => {
79
+ const style = window.getComputedStyle(el)
80
+ return {
81
+ borderTopLeftRadius: style.borderTopLeftRadius,
82
+ borderBottomLeftRadius: style.borderBottomLeftRadius,
83
+ borderTopRightRadius: style.borderTopRightRadius,
84
+ borderBottomRightRadius: style.borderBottomRightRadius,
85
+ }
86
+ })
87
+ expect(parseFloat(rightStyles.borderTopLeftRadius)).toBe(0)
88
+ expect(parseFloat(rightStyles.borderBottomLeftRadius)).toBe(0)
89
+ })
90
+ })
91
+
92
+ test.describe('Pattern B: ToggleGroup.Item asChild wraps XGroup.Item', () => {
93
+ test('clicking item changes toggle value', async ({ page }) => {
94
+ const leftButton = page.getByTestId('pattern-b-left')
95
+ await leftButton.waitFor({ state: 'visible' })
96
+
97
+ // click to toggle on
98
+ await leftButton.click()
99
+ await page.waitForTimeout(100)
100
+
101
+ // verify toggle group value changed via data-state or aria-pressed
102
+ const stateAfterClick = await leftButton.getAttribute('data-state')
103
+ expect(stateAfterClick).toBe('on')
104
+ })
105
+
106
+ test('clicking different items changes selection', async ({ page }) => {
107
+ const leftButton = page.getByTestId('pattern-b-left')
108
+ const centerButton = page.getByTestId('pattern-b-center')
109
+ await leftButton.waitFor({ state: 'visible' })
110
+
111
+ await leftButton.click()
112
+ await page.waitForTimeout(100)
113
+ expect(await leftButton.getAttribute('data-state')).toBe('on')
114
+ expect(await centerButton.getAttribute('data-state')).toBe('off')
115
+
116
+ await centerButton.click()
117
+ await page.waitForTimeout(100)
118
+ expect(await leftButton.getAttribute('data-state')).toBe('off')
119
+ expect(await centerButton.getAttribute('data-state')).toBe('on')
120
+ })
121
+
122
+ test('XGroup properly applies radius styles through asChild chain', async ({
123
+ page,
124
+ }) => {
125
+ const leftButton = page.getByTestId('pattern-b-left')
126
+ const centerButton = page.getByTestId('pattern-b-center')
127
+ const rightButton = page.getByTestId('pattern-b-right')
128
+ await leftButton.waitFor({ state: 'visible' })
129
+
130
+ // first item: should have left border radius preserved, zeroed right
131
+ const leftStyles = await leftButton.evaluate((el) => {
132
+ const style = window.getComputedStyle(el)
133
+ return {
134
+ borderTopLeftRadius: style.borderTopLeftRadius,
135
+ borderBottomLeftRadius: style.borderBottomLeftRadius,
136
+ borderTopRightRadius: style.borderTopRightRadius,
137
+ borderBottomRightRadius: style.borderBottomRightRadius,
138
+ }
139
+ })
140
+ expect(parseFloat(leftStyles.borderTopRightRadius)).toBe(0)
141
+ expect(parseFloat(leftStyles.borderBottomRightRadius)).toBe(0)
142
+
143
+ // center item: should have all corners zeroed
144
+ const centerStyles = await centerButton.evaluate((el) => {
145
+ const style = window.getComputedStyle(el)
146
+ return {
147
+ borderTopLeftRadius: style.borderTopLeftRadius,
148
+ borderBottomLeftRadius: style.borderBottomLeftRadius,
149
+ borderTopRightRadius: style.borderTopRightRadius,
150
+ borderBottomRightRadius: style.borderBottomRightRadius,
151
+ }
152
+ })
153
+ expect(parseFloat(centerStyles.borderTopLeftRadius)).toBe(0)
154
+ expect(parseFloat(centerStyles.borderBottomLeftRadius)).toBe(0)
155
+ expect(parseFloat(centerStyles.borderTopRightRadius)).toBe(0)
156
+ expect(parseFloat(centerStyles.borderBottomRightRadius)).toBe(0)
157
+
158
+ // last item: should have zero left radii
159
+ const rightStyles = await rightButton.evaluate((el) => {
160
+ const style = window.getComputedStyle(el)
161
+ return {
162
+ borderTopLeftRadius: style.borderTopLeftRadius,
163
+ borderBottomLeftRadius: style.borderBottomLeftRadius,
164
+ borderTopRightRadius: style.borderTopRightRadius,
165
+ borderBottomRightRadius: style.borderBottomRightRadius,
166
+ }
167
+ })
168
+ expect(parseFloat(rightStyles.borderTopLeftRadius)).toBe(0)
169
+ expect(parseFloat(rightStyles.borderBottomLeftRadius)).toBe(0)
170
+ })
171
+ })
172
+ })
@@ -0,0 +1,260 @@
1
+ import { expect, test, type Page } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ /**
5
+ * TOOLTIP CSS ANIMATION TESTS
6
+ *
7
+ * Validates that CSS tooltip animations have proper intermediate states:
8
+ * 1. Enter animation should animate y translation AND opacity smoothly
9
+ * 2. Exit animation should animate y translation AND opacity smoothly
10
+ * 3. First show should behave identically to subsequent shows
11
+ * 4. Arrow should have proper size
12
+ *
13
+ * Uses "lazy" animation (1000ms) for reliable intermediate capture.
14
+ */
15
+
16
+ const TOLERANCE = 0.1
17
+
18
+ async function getOpacity(page: Page, testId: string): Promise<number> {
19
+ return page.evaluate((id) => {
20
+ const el = document.querySelector(`[data-testid="${id}"]`)
21
+ if (!el) return -1
22
+ return Number.parseFloat(getComputedStyle(el).opacity)
23
+ }, testId)
24
+ }
25
+
26
+ async function getTranslateY(page: Page, testId: string): Promise<number> {
27
+ return page.evaluate((id) => {
28
+ const el = document.querySelector(`[data-testid="${id}"]`)
29
+ if (!el) return -9999
30
+ const transform = getComputedStyle(el).transform
31
+ if (transform === 'none') return 0
32
+ // matrix(a, b, c, d, tx, ty) - translateY is in the 'ty' position (6th value)
33
+ const match = transform.match(/matrix\([^,]+,[^,]+,[^,]+,[^,]+,[^,]+,\s*([^)]+)\)/)
34
+ return match ? Number.parseFloat(match[1]) : 0
35
+ }, testId)
36
+ }
37
+
38
+ async function elementExists(page: Page, testId: string): Promise<boolean> {
39
+ return page.evaluate((id) => !!document.querySelector(`[data-testid="${id}"]`), testId)
40
+ }
41
+
42
+ function isIntermediate(
43
+ value: number,
44
+ start: number,
45
+ end: number,
46
+ tolerance = TOLERANCE
47
+ ): boolean {
48
+ const notAtStart = Math.abs(value - start) > tolerance
49
+ const notAtEnd = Math.abs(value - end) > tolerance
50
+ const min = Math.min(start, end)
51
+ const max = Math.max(start, end)
52
+ const inRange = value >= min - tolerance && value <= max + tolerance
53
+ return notAtStart && notAtEnd && inRange
54
+ }
55
+
56
+ test.describe('Tooltip animation', () => {
57
+ test.beforeEach(async ({ page }, testInfo) => {
58
+ // These tests validate CSS transition intermediate states using transition="lazy"
59
+ // which is CSS-specific. Skip for non-CSS animation drivers.
60
+ const animationDriver = testInfo.project.metadata?.animationDriver
61
+ if (animationDriver && animationDriver !== 'css') {
62
+ test.skip()
63
+ }
64
+
65
+ await setupPage(page, {
66
+ name: 'TooltipAnimationCase',
67
+ type: 'useCase',
68
+ })
69
+ await page.waitForTimeout(500)
70
+ })
71
+
72
+ // TEST 1: Enter animation opacity intermediate values
73
+ test('enter animation has intermediate opacity values', async ({ page }) => {
74
+ const START_OPACITY = 0
75
+ const END_OPACITY = 1
76
+
77
+ // Tooltip should not be visible initially
78
+ expect(await elementExists(page, 'tooltip-content'), 'Initially hidden').toBe(false)
79
+
80
+ // Hover to trigger tooltip
81
+ await page.getByTestId('tooltip-trigger').hover()
82
+
83
+ // Wait for tooltip to appear
84
+ await page.waitForTimeout(50)
85
+ expect(
86
+ await elementExists(page, 'tooltip-content'),
87
+ 'Element exists after hover'
88
+ ).toBe(true)
89
+
90
+ // Capture mid-animation value (after ~400ms of 1000ms animation)
91
+ await page.waitForTimeout(350)
92
+ const midOpacity = await getOpacity(page, 'tooltip-content')
93
+
94
+ // Wait for animation to complete
95
+ await page.waitForTimeout(1200)
96
+ const endOpacity = await getOpacity(page, 'tooltip-content')
97
+
98
+ expect(endOpacity, 'End opacity').toBeCloseTo(END_OPACITY, 1)
99
+ expect(
100
+ isIntermediate(midOpacity, START_OPACITY, END_OPACITY),
101
+ `Mid opacity (${midOpacity.toFixed(3)}) should be intermediate between ${START_OPACITY} and ${END_OPACITY}`
102
+ ).toBe(true)
103
+ })
104
+
105
+ // TEST 2: Enter animation translateY intermediate values
106
+ // timing sensitive - on fast machines animation may complete before sample
107
+ test('enter animation has intermediate translateY values', async ({ page }) => {
108
+ const START_Y = -20 // enterStyle y value
109
+ const END_Y = 0
110
+
111
+ // Hover to trigger tooltip
112
+ await page.getByTestId('tooltip-trigger').hover()
113
+
114
+ // Wait for tooltip to appear
115
+ await page.waitForTimeout(50)
116
+ expect(
117
+ await elementExists(page, 'tooltip-content'),
118
+ 'Element exists after hover'
119
+ ).toBe(true)
120
+
121
+ // Capture mid-animation value (sample earlier on fast machines)
122
+ await page.waitForTimeout(200)
123
+ const midY = await getTranslateY(page, 'tooltip-content')
124
+
125
+ // Wait for animation to complete
126
+ await page.waitForTimeout(1200)
127
+ const endY = await getTranslateY(page, 'tooltip-content')
128
+
129
+ expect(endY, 'End translateY').toBeCloseTo(END_Y, 0)
130
+ // Allow either intermediate value OR at end (animation completed fast)
131
+ // The key thing is that the animation didn't snap to START_Y
132
+ const isAnimating = isIntermediate(midY, START_Y, END_Y) || Math.abs(midY - END_Y) < 1
133
+ expect(
134
+ isAnimating,
135
+ `Mid translateY (${midY.toFixed(2)}) should be animating (not stuck at start ${START_Y})`
136
+ ).toBe(true)
137
+ })
138
+
139
+ // TEST 3: First show should animate same as subsequent shows (not snap on first)
140
+ // timing sensitive - on fast machines animation may complete before sample
141
+ test('first show animates same as subsequent shows', async ({ page }) => {
142
+ // First show
143
+ await page.getByTestId('tooltip-trigger').hover()
144
+ await page.waitForTimeout(50)
145
+ expect(
146
+ await elementExists(page, 'tooltip-content'),
147
+ 'First show: element exists'
148
+ ).toBe(true)
149
+
150
+ // Capture first show mid-animation values (sample earlier on fast machines)
151
+ await page.waitForTimeout(200)
152
+ const firstMidOpacity = await getOpacity(page, 'tooltip-content')
153
+ const firstMidY = await getTranslateY(page, 'tooltip-content')
154
+
155
+ // Wait for first animation to complete
156
+ await page.waitForTimeout(1200)
157
+
158
+ // Move away to hide tooltip
159
+ await page.mouse.move(0, 0)
160
+ await page.waitForTimeout(1500) // Wait for exit animation
161
+
162
+ // Second show
163
+ await page.getByTestId('tooltip-trigger').hover()
164
+ await page.waitForTimeout(50)
165
+ expect(
166
+ await elementExists(page, 'tooltip-content'),
167
+ 'Second show: element exists'
168
+ ).toBe(true)
169
+
170
+ // Capture second show mid-animation values
171
+ await page.waitForTimeout(200)
172
+ const secondMidOpacity = await getOpacity(page, 'tooltip-content')
173
+ const secondMidY = await getTranslateY(page, 'tooltip-content')
174
+
175
+ // Both should be animating (intermediate or at end for fast machines)
176
+ // The key is they shouldn't be stuck at start (0 opacity, -20 Y)
177
+ const firstAnimating = isIntermediate(firstMidOpacity, 0, 1) || firstMidOpacity > 0.5
178
+ const secondAnimating =
179
+ isIntermediate(secondMidOpacity, 0, 1) || secondMidOpacity > 0.5
180
+
181
+ expect(
182
+ firstAnimating,
183
+ `First mid opacity (${firstMidOpacity.toFixed(3)}) should be animating`
184
+ ).toBe(true)
185
+
186
+ expect(
187
+ secondAnimating,
188
+ `Second mid opacity (${secondMidOpacity.toFixed(3)}) should be animating`
189
+ ).toBe(true)
190
+
191
+ // Y should also be animating (not stuck at -20)
192
+ const firstYAnimating = isIntermediate(firstMidY, -20, 0) || Math.abs(firstMidY) < 5
193
+ const secondYAnimating =
194
+ isIntermediate(secondMidY, -20, 0) || Math.abs(secondMidY) < 5
195
+
196
+ expect(
197
+ firstYAnimating,
198
+ `First mid Y (${firstMidY.toFixed(2)}) should be animating`
199
+ ).toBe(true)
200
+
201
+ expect(
202
+ secondYAnimating,
203
+ `Second mid Y (${secondMidY.toFixed(2)}) should be animating`
204
+ ).toBe(true)
205
+ })
206
+
207
+ // TEST 4: Exit animation intermediate values
208
+ test('exit animation has intermediate opacity values', async ({ page }) => {
209
+ const START_OPACITY = 1
210
+ const END_OPACITY = 0
211
+
212
+ // Show tooltip first
213
+ await page.getByTestId('tooltip-trigger').hover()
214
+ await page.waitForTimeout(1500) // Wait for enter animation to complete
215
+
216
+ const startOpacity = await getOpacity(page, 'tooltip-content')
217
+ expect(startOpacity, 'Start opacity before exit').toBeCloseTo(START_OPACITY, 1)
218
+
219
+ // Move away to trigger exit
220
+ await page.mouse.move(0, 0)
221
+
222
+ // Capture mid-exit animation
223
+ await page.waitForTimeout(400)
224
+
225
+ // Element should still exist during exit animation
226
+ const stillExists = await elementExists(page, 'tooltip-content')
227
+
228
+ if (stillExists) {
229
+ const midOpacity = await getOpacity(page, 'tooltip-content')
230
+ expect(
231
+ isIntermediate(midOpacity, START_OPACITY, END_OPACITY) ||
232
+ midOpacity < START_OPACITY,
233
+ `Mid exit opacity (${midOpacity.toFixed(3)}) should be animating`
234
+ ).toBe(true)
235
+ }
236
+
237
+ // Wait for exit to complete
238
+ await page.waitForTimeout(1500)
239
+ expect(await elementExists(page, 'tooltip-content'), 'Hidden after exit').toBe(false)
240
+ })
241
+
242
+ // TEST 5: Arrow size validation - SKIPPED pending Tooltip arrow sizing fix
243
+ test.skip('arrow has proper size (not tiny)', async ({ page }) => {
244
+ // Show tooltip
245
+ await page.getByTestId('tooltip-trigger').hover()
246
+ await page.waitForTimeout(1500)
247
+
248
+ const arrowSize = await page.evaluate(() => {
249
+ const arrow = document.querySelector('[data-testid="tooltip-arrow"]')
250
+ if (!arrow) return { width: 0, height: 0, exists: false }
251
+ const rect = arrow.getBoundingClientRect()
252
+ return { width: rect.width, height: rect.height, exists: true }
253
+ })
254
+
255
+ expect(arrowSize.exists, 'Arrow element exists').toBe(true)
256
+ // Arrow should have reasonable size (size="$2" should be ~16px, not tiny)
257
+ expect(arrowSize.width, 'Arrow width should not be tiny').toBeGreaterThan(8)
258
+ expect(arrowSize.height, 'Arrow height should not be tiny').toBeGreaterThan(8)
259
+ })
260
+ })
@@ -0,0 +1,76 @@
1
+ import { expect, test } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ test.describe('Tooltip enter animation interrupted by trigger switch', () => {
5
+ test.beforeEach(async ({ page }) => {
6
+ await setupPage(page, {
7
+ name: 'TooltipMultiTriggerCase',
8
+ type: 'useCase',
9
+ })
10
+ await page.waitForLoadState('networkidle')
11
+ })
12
+
13
+ test('enter animation completes after quick trigger switch', async ({ page }) => {
14
+ const content = page.locator('#tip-content')
15
+
16
+ // hover trigger A to start opening
17
+ await page.locator('#tip-trigger-a').hover()
18
+ // wait just long enough for tooltip to mount but enter animation to still be in-flight
19
+ await page.waitForTimeout(80)
20
+
21
+ // quickly switch to trigger C (interrupts the enter animation mid-flight)
22
+ const triggerC = page.locator('#tip-trigger-c')
23
+ const cBox = await triggerC.boundingBox()
24
+ await page.mouse.move(cBox!.x + cBox!.width / 2, cBox!.y + cBox!.height / 2, {
25
+ steps: 2,
26
+ })
27
+
28
+ // wait for animations to settle
29
+ await page.waitForTimeout(600)
30
+
31
+ await expect(content).toBeVisible({ timeout: 2000 })
32
+
33
+ // the tooltip should be fully opaque — not stuck at a mid-flight value
34
+ const opacity = await content.evaluate((el) => {
35
+ return parseFloat(getComputedStyle(el).opacity)
36
+ })
37
+ expect(
38
+ opacity,
39
+ 'tooltip should be fully opaque after enter completes'
40
+ ).toBeGreaterThan(0.9)
41
+ })
42
+
43
+ test('enter animation completes after rapid multi-trigger sweep', async ({ page }) => {
44
+ const content = page.locator('#tip-content')
45
+
46
+ // hover trigger A
47
+ await page.locator('#tip-trigger-a').hover()
48
+ await page.waitForTimeout(60)
49
+
50
+ // rapidly sweep A → B → C
51
+ const triggerB = page.locator('#tip-trigger-b')
52
+ const triggerC = page.locator('#tip-trigger-c')
53
+ const bBox = await triggerB.boundingBox()
54
+ const cBox = await triggerC.boundingBox()
55
+
56
+ await page.mouse.move(bBox!.x + bBox!.width / 2, bBox!.y + bBox!.height / 2, {
57
+ steps: 2,
58
+ })
59
+ await page.waitForTimeout(30)
60
+ await page.mouse.move(cBox!.x + cBox!.width / 2, cBox!.y + cBox!.height / 2, {
61
+ steps: 2,
62
+ })
63
+
64
+ // wait for animations to settle
65
+ await page.waitForTimeout(600)
66
+
67
+ await expect(content).toBeVisible({ timeout: 2000 })
68
+
69
+ const opacity = await content.evaluate((el) => {
70
+ return parseFloat(getComputedStyle(el).opacity)
71
+ })
72
+ expect(opacity, 'tooltip should be fully opaque after rapid sweep').toBeGreaterThan(
73
+ 0.9
74
+ )
75
+ })
76
+ })