@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,207 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ /**
4
+ * Tests for multi-driver animation config: { default: motion, css: cssDriver }
5
+ *
6
+ * This test ONLY runs with ?animationDriver=multi which sets up:
7
+ * animations: { default: animationsMotion, css: animationsCSS }
8
+ *
9
+ * Verifies:
10
+ * 1. animatedBy="default" uses motion driver (JS-based animation)
11
+ * 2. animatedBy="css" uses CSS driver (CSS transitions)
12
+ * 3. no animatedBy defaults to motion driver
13
+ */
14
+
15
+ test.describe('Multi-driver animation config', () => {
16
+ test.beforeEach(async ({ page }) => {
17
+ // specifically load with multi-driver config
18
+ await page.goto('/?test=MultiDriverAnimation&animationDriver=multi')
19
+ await page.waitForLoadState('networkidle')
20
+ await page.waitForTimeout(300)
21
+ })
22
+
23
+ test('animatedBy="default" uses motion driver (animates smoothly)', async ({
24
+ page,
25
+ }) => {
26
+ const element = page.getByTestId('driver-default')
27
+
28
+ const initialOpacity = await element.evaluate((el) =>
29
+ Number(getComputedStyle(el).opacity)
30
+ )
31
+ expect(initialOpacity).toBeCloseTo(0.3, 1)
32
+
33
+ // use in-browser rAF polling to reliably capture mid-animation frames
34
+ // (Playwright waitForTimeout is too slow on CI to catch intermediate values)
35
+ const samples: number[] = await page.evaluate(() => {
36
+ return new Promise<number[]>((resolve) => {
37
+ const el = document.querySelector('[data-testid="driver-default"]')!
38
+ const vals: number[] = []
39
+ const start = performance.now()
40
+ function tick() {
41
+ vals.push(Number(getComputedStyle(el).opacity))
42
+ if (performance.now() - start < 500) requestAnimationFrame(tick)
43
+ else resolve(vals)
44
+ }
45
+ ;(document.querySelector('[data-testid="toggle-multi"]') as HTMLElement).click()
46
+ requestAnimationFrame(tick)
47
+ })
48
+ })
49
+
50
+ const finalOpacity = samples[samples.length - 1]
51
+ expect(finalOpacity).toBeGreaterThan(0.9)
52
+
53
+ // motion driver should show intermediate values (not jump instantly)
54
+ const intermediates = samples.filter((v) => v > 0.35 && v < 0.95)
55
+ expect(
56
+ intermediates.length,
57
+ `Motion driver should animate with interpolation (got ${intermediates.length} intermediate frames). ` +
58
+ `Samples: [${samples
59
+ .slice(0, 8)
60
+ .map((s) => s.toFixed(2))
61
+ .join(', ')}...]`
62
+ ).toBeGreaterThanOrEqual(2)
63
+ })
64
+
65
+ test('animatedBy="css" uses CSS driver (CSS transitions)', async ({ page }) => {
66
+ const element = page.getByTestId('driver-css')
67
+
68
+ const initialOpacity = await element.evaluate((el) =>
69
+ Number(getComputedStyle(el).opacity)
70
+ )
71
+ expect(initialOpacity).toBeCloseTo(0.3, 1)
72
+
73
+ // trigger animation
74
+ await page.getByTestId('toggle-multi').click()
75
+
76
+ // css driver uses CSS transitions - 200ms
77
+ await page.waitForTimeout(350)
78
+ const finalOpacity = await element.evaluate((el) =>
79
+ Number(getComputedStyle(el).opacity)
80
+ )
81
+
82
+ expect(finalOpacity).toBeGreaterThan(0.9)
83
+
84
+ // verify it's using CSS transition
85
+ const transition = await element.evaluate((el) => getComputedStyle(el).transition)
86
+ expect(transition.length).toBeGreaterThan(0)
87
+ })
88
+
89
+ test('no animatedBy defaults to motion driver', async ({ page }) => {
90
+ const initialOpacity = await page
91
+ .getByTestId('driver-none')
92
+ .evaluate((el) => Number(getComputedStyle(el).opacity))
93
+ expect(initialOpacity).toBeCloseTo(0.3, 1)
94
+
95
+ // poll via rAF to capture intermediate values reliably in slow CI
96
+ const samples: number[] = await page.evaluate(() => {
97
+ return new Promise<number[]>((resolve) => {
98
+ const el = document.querySelector('[data-testid="driver-none"]')!
99
+ const vals: number[] = []
100
+ const start = performance.now()
101
+ function tick() {
102
+ vals.push(Number(getComputedStyle(el).opacity))
103
+ if (performance.now() - start < 500) requestAnimationFrame(tick)
104
+ else resolve(vals)
105
+ }
106
+ ;(document.querySelector('[data-testid="toggle-multi"]') as HTMLElement).click()
107
+ requestAnimationFrame(tick)
108
+ })
109
+ })
110
+
111
+ const finalOpacity = samples[samples.length - 1]
112
+ expect(finalOpacity).toBeGreaterThan(0.9)
113
+
114
+ // verify motion driver: multiple frames of interpolation between 0.3 and ~1.0
115
+ const intermediates = samples.filter((v) => v > 0.35 && v < 0.95)
116
+ expect(
117
+ intermediates.length,
118
+ `Default should use motion with real interpolation (got ${intermediates.length} intermediate frames). ` +
119
+ `Samples: [${samples
120
+ .slice(0, 8)
121
+ .map((s) => s.toFixed(2))
122
+ .join(', ')}...]`
123
+ ).toBeGreaterThanOrEqual(2)
124
+
125
+ // first frames should still be near start (animation has real duration)
126
+ const earlyEnd = samples.slice(0, 3).every((v) => v > 0.95)
127
+ expect(earlyEnd, 'Animation should not complete in first 3 frames').toBe(false)
128
+ })
129
+
130
+ test('different drivers produce different animation behavior', async ({ page }) => {
131
+ // trigger animation
132
+ await page.getByTestId('toggle-multi').click()
133
+
134
+ // capture at same moment
135
+ await page.waitForTimeout(100)
136
+
137
+ const [defaultOpacity, cssOpacity] = await Promise.all([
138
+ page
139
+ .getByTestId('driver-default')
140
+ .evaluate((el) => Number(getComputedStyle(el).opacity)),
141
+ page
142
+ .getByTestId('driver-css')
143
+ .evaluate((el) => Number(getComputedStyle(el).opacity)),
144
+ ])
145
+
146
+ // both should be animating but may have different values due to different drivers
147
+ // this just verifies both are in valid range
148
+ expect(defaultOpacity).toBeGreaterThanOrEqual(0.3)
149
+ expect(defaultOpacity).toBeLessThanOrEqual(1)
150
+ expect(cssOpacity).toBeGreaterThanOrEqual(0.3)
151
+ expect(cssOpacity).toBeLessThanOrEqual(1)
152
+ })
153
+ })
154
+
155
+ test.describe('Multi-driver group hover transitions', () => {
156
+ test.beforeEach(async ({ page }) => {
157
+ await page.goto('/?test=MultiDriverAnimation&animationDriver=multi')
158
+ await page.waitForLoadState('networkidle')
159
+ await page.waitForTimeout(300)
160
+ })
161
+
162
+ test('group hover with motion driver animates child', async ({ page }) => {
163
+ const group = page.getByTestId('group-motion')
164
+ const child = page.getByTestId('group-motion-child')
165
+
166
+ const initialOpacity = await child.evaluate((el) =>
167
+ Number(getComputedStyle(el).opacity)
168
+ )
169
+ expect(initialOpacity).toBeCloseTo(0.5, 1)
170
+
171
+ // hover over group - 100ms enter transition
172
+ await group.hover()
173
+ await page.waitForTimeout(200)
174
+
175
+ const hoverOpacity = await child.evaluate((el) =>
176
+ Number(getComputedStyle(el).opacity)
177
+ )
178
+ expect(hoverOpacity).toBeGreaterThan(0.9)
179
+
180
+ // exit hover - 500ms exit transition
181
+ await page.mouse.move(0, 0)
182
+ await page.waitForTimeout(700)
183
+
184
+ const exitOpacity = await child.evaluate((el) => Number(getComputedStyle(el).opacity))
185
+ expect(exitOpacity).toBeCloseTo(0.5, 1)
186
+ })
187
+
188
+ test('group hover with css driver uses CSS transitions', async ({ page }) => {
189
+ const group = page.getByTestId('group-css')
190
+ const child = page.getByTestId('group-css-child')
191
+
192
+ const initialOpacity = await child.evaluate((el) =>
193
+ Number(getComputedStyle(el).opacity)
194
+ )
195
+ expect(initialOpacity).toBeCloseTo(0.5, 1)
196
+
197
+ await group.hover()
198
+ // css driver hover transition should complete
199
+ await page.waitForTimeout(400)
200
+
201
+ const hoverOpacity = await child.evaluate((el) =>
202
+ Number(getComputedStyle(el).opacity)
203
+ )
204
+ // CSS driver group hover should animate to full opacity
205
+ expect(hoverOpacity).toBeGreaterThan(0.8)
206
+ })
207
+ })
@@ -0,0 +1,50 @@
1
+ import { expect, test } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ test.beforeEach(async ({ page }) => {
5
+ await setupPage(page, { name: 'NewInputBasic', type: 'useCase' })
6
+ })
7
+
8
+ test('renders basic input', async ({ page }) => {
9
+ const input = page.locator('[data-testid="basic-input"]')
10
+ await expect(input).toBeVisible()
11
+ await expect(input).toHaveAttribute('placeholder', 'Basic input')
12
+ })
13
+
14
+ test('renders password input with correct type', async ({ page }) => {
15
+ const input = page.locator('[data-testid="password-input"]')
16
+ await expect(input).toBeVisible()
17
+ await expect(input).toHaveAttribute('type', 'password')
18
+ })
19
+
20
+ test('renders email input with correct type', async ({ page }) => {
21
+ const input = page.locator('[data-testid="email-input"]')
22
+ await expect(input).toBeVisible()
23
+ await expect(input).toHaveAttribute('type', 'email')
24
+ })
25
+
26
+ test('renders number input with correct type', async ({ page }) => {
27
+ const input = page.locator('[data-testid="number-input"]')
28
+ await expect(input).toBeVisible()
29
+ await expect(input).toHaveAttribute('type', 'number')
30
+ })
31
+
32
+ test('renders disabled input', async ({ page }) => {
33
+ const input = page.locator('[data-testid="disabled-input"]')
34
+ await expect(input).toBeVisible()
35
+ await expect(input).toBeDisabled()
36
+ })
37
+
38
+ test('renders readonly input', async ({ page }) => {
39
+ const input = page.locator('[data-testid="readonly-input"]')
40
+ await expect(input).toBeVisible()
41
+ await expect(input).toHaveAttribute('readonly', '')
42
+ await expect(input).toHaveValue('Read only value')
43
+ })
44
+
45
+ test('renders textarea', async ({ page }) => {
46
+ const textarea = page.locator('[data-testid="basic-textarea"]')
47
+ await expect(textarea).toBeVisible()
48
+ // Textarea should be rendered as textarea element
49
+ await expect(textarea).toHaveAttribute('placeholder', 'Basic textarea')
50
+ })
@@ -0,0 +1,55 @@
1
+ import { expect, test } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ test.beforeEach(async ({ page }) => {
5
+ await setupPage(page, { name: 'NewInputEvents', type: 'useCase' })
6
+ })
7
+
8
+ test('onChange updates value display', async ({ page }) => {
9
+ const input = page.locator('[data-testid="event-input"]')
10
+ const valueDisplay = page.locator('[data-testid="value-display"]')
11
+
12
+ await input.fill('hello world')
13
+
14
+ await expect(valueDisplay).toContainText('Value: hello world')
15
+ })
16
+
17
+ test('onChange increments change count', async ({ page }) => {
18
+ const input = page.locator('[data-testid="event-input"]')
19
+ const changeCount = page.locator('[data-testid="change-count"]')
20
+
21
+ await expect(changeCount).toContainText('Changes: 0')
22
+
23
+ await input.fill('a')
24
+ await expect(changeCount).toContainText('Changes: 1')
25
+
26
+ await input.fill('ab')
27
+ await expect(changeCount).toContainText('Changes: 2')
28
+ })
29
+
30
+ test('onSubmitEditing fires on Enter', async ({ page }) => {
31
+ const input = page.locator('[data-testid="event-input"]')
32
+ const submitCount = page.locator('[data-testid="submit-count"]')
33
+
34
+ await expect(submitCount).toContainText('Submits: 0')
35
+
36
+ await input.focus()
37
+ await input.press('Enter')
38
+
39
+ await expect(submitCount).toContainText('Submits: 1')
40
+ })
41
+
42
+ test('typing and submitting works together', async ({ page }) => {
43
+ const input = page.locator('[data-testid="event-input"]')
44
+ const valueDisplay = page.locator('[data-testid="value-display"]')
45
+ const submitCount = page.locator('[data-testid="submit-count"]')
46
+
47
+ await input.fill('test value')
48
+ await expect(valueDisplay).toContainText('Value: test value')
49
+
50
+ await input.press('Enter')
51
+ await expect(submitCount).toContainText('Submits: 1')
52
+
53
+ // Value should still be there after submit
54
+ await expect(valueDisplay).toContainText('Value: test value')
55
+ })
@@ -0,0 +1,163 @@
1
+ import { expect, test, type Page } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ // poll interval is ~233ms (14 frames), give generous margin
5
+ const POLL_WAIT = 800
6
+
7
+ function parseLayout(text: string) {
8
+ // format: "A:120x60@0,0#1"
9
+ const m = text.match(/^([AB]):(\d+)x(\d+)@(-?\d+),(-?\d+)#(\d+)$/)
10
+ if (!m) throw new Error(`bad layout string: ${text}`)
11
+ return {
12
+ label: m[1],
13
+ width: Number(m[2]),
14
+ height: Number(m[3]),
15
+ x: Number(m[4]),
16
+ y: Number(m[5]),
17
+ count: Number(m[6]),
18
+ }
19
+ }
20
+
21
+ async function getLayout(page: Page, testId: string) {
22
+ const text = await page.getByTestId(testId).innerText()
23
+ return parseLayout(text)
24
+ }
25
+
26
+ test.beforeEach(async ({ page }) => {
27
+ await setupPage(page, { name: 'OnLayoutCase', type: 'useCase' })
28
+ })
29
+
30
+ test('fires on mount with correct dimensions', async ({ page }) => {
31
+ // onLayout should fire at least once on mount with accurate size
32
+ await page.waitForTimeout(POLL_WAIT)
33
+ const a = await getLayout(page, 'layout-a')
34
+ expect(a.count).toBeGreaterThanOrEqual(1)
35
+ expect(a.width).toBe(120)
36
+ expect(a.height).toBe(60)
37
+ // x should be 0 (no margin) relative to parent
38
+ expect(a.x).toBe(0)
39
+
40
+ // box B should also have fired
41
+ const b = await getLayout(page, 'layout-b')
42
+ expect(b.count).toBeGreaterThanOrEqual(1)
43
+ expect(b.width).toBe(200)
44
+ expect(b.height).toBe(50)
45
+ })
46
+
47
+ test('detects position change', async ({ page }) => {
48
+ await page.waitForTimeout(POLL_WAIT)
49
+ const before = await getLayout(page, 'layout-a')
50
+ expect(before.x).toBe(0)
51
+
52
+ await page.getByTestId('btn-move').click()
53
+ await page.waitForTimeout(POLL_WAIT)
54
+
55
+ const after = await getLayout(page, 'layout-a')
56
+ expect(after.x).toBe(100)
57
+ expect(after.count).toBeGreaterThan(before.count)
58
+ })
59
+
60
+ test('detects size change', async ({ page }) => {
61
+ await page.waitForTimeout(POLL_WAIT)
62
+ const before = await getLayout(page, 'layout-a')
63
+ expect(before.width).toBe(120)
64
+ expect(before.height).toBe(60)
65
+
66
+ await page.getByTestId('btn-resize').click()
67
+ await page.waitForTimeout(POLL_WAIT)
68
+
69
+ const after = await getLayout(page, 'layout-a')
70
+ expect(after.width).toBe(180)
71
+ expect(after.height).toBe(80)
72
+ expect(after.count).toBeGreaterThan(before.count)
73
+ })
74
+
75
+ test('detects parent size change via child resize', async ({ page }) => {
76
+ // parent shrinking alone won't re-fire onLayout if the child rect is unchanged.
77
+ // combine parent resize + child resize to verify the parent rect is picked up.
78
+ await page.waitForTimeout(POLL_WAIT)
79
+ const before = await getLayout(page, 'layout-a')
80
+
81
+ // resize both parent and child
82
+ await page.getByTestId('btn-parent-resize').click()
83
+ await page.getByTestId('btn-resize').click()
84
+ await page.waitForTimeout(POLL_WAIT)
85
+
86
+ const after = await getLayout(page, 'layout-a')
87
+ expect(after.count).toBeGreaterThan(before.count)
88
+ // child resized to 180x80
89
+ expect(after.width).toBe(180)
90
+ expect(after.height).toBe(80)
91
+ })
92
+
93
+ test('unmount cleans up and remount re-fires', async ({ page }) => {
94
+ await page.waitForTimeout(POLL_WAIT)
95
+ const before = await getLayout(page, 'layout-a')
96
+ expect(before.count).toBeGreaterThanOrEqual(1)
97
+
98
+ // unmount
99
+ await page.getByTestId('btn-toggle-mount').click()
100
+ await page.waitForTimeout(200)
101
+ // layout text should reset to zeros since state resets
102
+ const unmounted = await getLayout(page, 'layout-a')
103
+ // count shouldn't have increased (component reset its state)
104
+ // but we can't assert count=0 because state persists in parent
105
+ // instead verify box-a is gone from DOM
106
+ await expect(page.getByTestId('box-a')).not.toBeVisible()
107
+
108
+ // remount
109
+ await page.getByTestId('btn-toggle-mount').click()
110
+ await page.waitForTimeout(POLL_WAIT)
111
+ const remounted = await getLayout(page, 'layout-a')
112
+ expect(remounted.count).toBeGreaterThan(unmounted.count)
113
+ expect(remounted.width).toBe(120)
114
+ expect(remounted.height).toBe(60)
115
+ })
116
+
117
+ test('node swap fires onLayout for new element', async ({ page }) => {
118
+ await page.waitForTimeout(POLL_WAIT)
119
+ const before = await getLayout(page, 'layout-a')
120
+ expect(before.count).toBeGreaterThanOrEqual(1)
121
+
122
+ // swap replaces the element via different key (box-a-default -> box-a-alt)
123
+ await page.getByTestId('btn-swap').click()
124
+ await page.waitForTimeout(POLL_WAIT)
125
+
126
+ const after = await getLayout(page, 'layout-a')
127
+ // should have fired for the new node
128
+ expect(after.count).toBeGreaterThan(before.count)
129
+ // dimensions should be the same since props match
130
+ expect(after.width).toBe(120)
131
+ expect(after.height).toBe(60)
132
+
133
+ // verify the background color changed (proves DOM node actually swapped)
134
+ const bg = await page.getByTestId('box-a').evaluate((el) => {
135
+ return getComputedStyle(el).backgroundColor
136
+ })
137
+ // blue8 instead of red8 — just check it's not the original red
138
+ expect(bg).not.toContain('rgb(229, 72, 77)')
139
+ })
140
+
141
+ test('multiple instances do not cross-contaminate', async ({ page }) => {
142
+ await page.waitForTimeout(POLL_WAIT)
143
+ const a = await getLayout(page, 'layout-a')
144
+ const b = await getLayout(page, 'layout-b')
145
+
146
+ // both should have independent correct values
147
+ expect(a.width).toBe(120)
148
+ expect(b.width).toBe(200)
149
+
150
+ // move box A — box B should not change
151
+ await page.getByTestId('btn-move').click()
152
+ await page.waitForTimeout(POLL_WAIT)
153
+
154
+ const a2 = await getLayout(page, 'layout-a')
155
+ const b2 = await getLayout(page, 'layout-b')
156
+
157
+ expect(a2.x).toBe(100)
158
+ // B's values should be unchanged
159
+ expect(b2.width).toBe(b.width)
160
+ expect(b2.height).toBe(b.height)
161
+ expect(b2.x).toBe(b.x)
162
+ expect(b2.y).toBe(b.y)
163
+ })
@@ -0,0 +1,100 @@
1
+ import { expect, test } from '@playwright/test'
2
+ import { setupPage } from './test-utils'
3
+
4
+ /**
5
+ * tests for onLayout with CSS scale transform.
6
+ *
7
+ * default behavior (no flag):
8
+ * - onLayout reports transformed dimensions (getBoundingClientRect behavior)
9
+ * - a 200x200 element with scale(0.5) reports 100x100
10
+ *
11
+ * with __HANZO_GUI_ONLAYOUT_PRETRANSFORM = true:
12
+ * - onLayout should report pre-transform dimensions (matching RN behavior)
13
+ * - a 200x200 element with scale(0.5) should report 200x200
14
+ *
15
+ * see: https://github.com/hanzoai/gui/pull/2329
16
+ */
17
+
18
+ test.describe('onLayout with CSS scale', () => {
19
+ test.beforeEach(async ({ page }) => {
20
+ await setupPage(page, {
21
+ name: 'OnLayoutScaleCase',
22
+ type: 'useCase',
23
+ waitExtra: true,
24
+ })
25
+ })
26
+
27
+ test('unscaled box always reports correct dimensions (200x200)', async ({ page }) => {
28
+ await page.waitForTimeout(500)
29
+
30
+ const layoutText = await page.getByTestId('layout-no-scale').textContent()
31
+
32
+ expect(layoutText).toContain('200')
33
+ expect(layoutText).toMatch(/200.*200/)
34
+ })
35
+
36
+ test('scaled box reports transformed dimensions by default (100x100)', async ({
37
+ page,
38
+ }) => {
39
+ // ensure flag is OFF
40
+ await page.evaluate(() => {
41
+ ;(globalThis as any).__HANZO_GUI_ONLAYOUT_PRETRANSFORM = false
42
+ })
43
+
44
+ // trigger relayout to pick up the flag change
45
+ await page.getByTestId('trigger-relayout').click()
46
+ await page.waitForTimeout(500)
47
+
48
+ const layoutText = await page.getByTestId('layout-with-scale').textContent()
49
+
50
+ // without flag: 200x200 with scale(0.5) should report 100x100 (transformed)
51
+ expect(layoutText).toContain('100')
52
+ expect(layoutText).toMatch(/100.*100/)
53
+ })
54
+
55
+ test('scaled box reports pre-transform dimensions with flag enabled (200x200)', async ({
56
+ page,
57
+ }) => {
58
+ // enable the flag
59
+ await page.evaluate(() => {
60
+ ;(globalThis as any).__HANZO_GUI_ONLAYOUT_PRETRANSFORM = true
61
+ })
62
+
63
+ // trigger relayout to pick up the flag change
64
+ await page.getByTestId('trigger-relayout').click()
65
+ await page.waitForTimeout(500)
66
+
67
+ const layoutText = await page.getByTestId('layout-with-scale').textContent()
68
+
69
+ // with flag: 200x200 with scale(0.5) should report 200x200 (pre-transform, RN behavior)
70
+ expect(layoutText).toContain('200')
71
+ expect(layoutText).toMatch(/200.*200/)
72
+ })
73
+
74
+ test('both boxes report same dimensions with flag enabled', async ({ page }) => {
75
+ // enable the flag
76
+ await page.evaluate(() => {
77
+ ;(globalThis as any).__HANZO_GUI_ONLAYOUT_PRETRANSFORM = true
78
+ })
79
+
80
+ // trigger relayout
81
+ await page.getByTestId('trigger-relayout').click()
82
+ await page.waitForTimeout(500)
83
+
84
+ const noScaleText = await page.getByTestId('layout-no-scale').textContent()
85
+ const withScaleText = await page.getByTestId('layout-with-scale').textContent()
86
+
87
+ // extract dimensions
88
+ const noScaleMatch = noScaleText?.match(/(\d+)x(\d+)/)
89
+ const withScaleMatch = withScaleText?.match(/(\d+)x(\d+)/)
90
+
91
+ expect(noScaleMatch).toBeTruthy()
92
+ expect(withScaleMatch).toBeTruthy()
93
+
94
+ if (noScaleMatch && withScaleMatch) {
95
+ // with flag enabled, both should report same pre-transform dimensions
96
+ expect(withScaleMatch[1]).toBe(noScaleMatch[1])
97
+ expect(withScaleMatch[2]).toBe(noScaleMatch[2])
98
+ }
99
+ })
100
+ })