@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.
- package/.detoxrc.js +130 -0
- package/.env.production +2 -0
- package/.maestro/config.yaml +4 -0
- package/.maestro/flows/shorthand-variables.yaml +23 -0
- package/.watchmanconfig +1 -0
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/app.json +43 -0
- package/assets/adaptive-icon.png +0 -0
- package/assets/favicon.png +0 -0
- package/assets/icon.png +0 -0
- package/assets/splash.png +0 -0
- package/babel.config.js +25 -0
- package/e2e/CompilerExtraction.test.ts +147 -0
- package/e2e/GroupPressNative.test.ts +167 -0
- package/e2e/MediaQueryGtMd.test.ts +71 -0
- package/e2e/NativePortal.test.ts +113 -0
- package/e2e/PointerEvents.test.ts +116 -0
- package/e2e/PressStyleNative.noRngh.test.ts +191 -0
- package/e2e/PressStyleNative.test.ts +231 -0
- package/e2e/SafeArea.test.ts +57 -0
- package/e2e/SelectAndroidOnPress.test.ts +181 -0
- package/e2e/SelectRemount.test.ts +137 -0
- package/e2e/SheetDragResist.test.ts +370 -0
- package/e2e/SheetKeyboardDrag.test.ts +249 -0
- package/e2e/SheetScrollableDrag.test.ts +560 -0
- package/e2e/ShorthandVariables.test.ts +53 -0
- package/e2e/ThemeChangeBasic.test.ts +123 -0
- package/e2e/ThemeMutation.test.ts +80 -0
- package/e2e/check-rngh-status.test.ts +31 -0
- package/e2e/jest.config.js +19 -0
- package/e2e/utils/colors.ts +75 -0
- package/e2e/utils/navigation.ts +53 -0
- package/eas.json +22 -0
- package/flows/AlertDialog.yaml +17 -0
- package/flows/OpenApp.yaml +25 -0
- package/flows/Select.yaml +13 -0
- package/flows/Sheet.yaml +12 -0
- package/flows/Tabs.yaml +13 -0
- package/flows/Toast.yaml +14 -0
- package/flows/WarmUp.yaml +24 -0
- package/index.html +21 -0
- package/index.js +17 -0
- package/metro.config.js +64 -0
- package/next-router-shim.ts +9 -0
- package/package.json +118 -0
- package/plans/toast-2.md +471 -0
- package/playwright.config.ts +71 -0
- package/plugins/expo-modules-core-swift6.js +76 -0
- package/pod-install.sh +7 -0
- package/public/favicon.svg +70 -0
- package/public/fonts/inter.css +15 -0
- package/public/fonts/noto-cn.otf +0 -0
- package/public/gui-icon.svg +68 -0
- package/run-detox.sh +230 -0
- package/run-native-tests.sh +4 -0
- package/run-tests-parallel.ts +195 -0
- package/screenshots/Screenshotter.test.tsx +48 -0
- package/src/AnimationDemos.tsx +131 -0
- package/src/App.native.tsx +121 -0
- package/src/App.tsx +121 -0
- package/src/Navigation.tsx +98 -0
- package/src/Sandbox.tsx +87 -0
- package/src/TestDynamicEval.tsx +33 -0
- package/src/TestNativeSheet.tsx +100 -0
- package/src/components/TimedRender.tsx +18 -0
- package/src/constants/test-ids.ts +52 -0
- package/src/features/demos/demo-screen.tsx +72 -0
- package/src/features/home/ColorSchemeListItem.tsx +41 -0
- package/src/features/home/TestBuildAButton.tsx +102 -0
- package/src/features/home/TestSeparator.tsx +0 -0
- package/src/features/home/screen.tsx +285 -0
- package/src/features/testcases/screen.tsx +59 -0
- package/src/features/testcases/test-screen.tsx +50 -0
- package/src/generatedV5Theme.ts +112 -0
- package/src/gui.config.ts +411 -0
- package/src/guy.png +0 -0
- package/src/index.tsx +6 -0
- package/src/provider/index.tsx +18 -0
- package/src/test-gui-stack.tsx +11 -0
- package/src/test.tsx +3 -0
- package/src/useKitchenSinkTheme.tsx +15 -0
- package/src/usecases/ActionsSheetComparison.tsx +194 -0
- package/src/usecases/AnimatePresenceEnterExitCase.tsx +255 -0
- package/src/usecases/AnimatePresenceExitTest.tsx +69 -0
- package/src/usecases/AnimatedByProp.tsx +39 -0
- package/src/usecases/AnimationComprehensiveCase.tsx +2515 -0
- package/src/usecases/AnimationValueLoggingCase.tsx +526 -0
- package/src/usecases/AnimationsWithMediaQueriesCase.tsx +110 -0
- package/src/usecases/Benchmark.tsx +148 -0
- package/src/usecases/BenchmarkSelect.tsx +34 -0
- package/src/usecases/ButtonCircular.tsx +3 -0
- package/src/usecases/ButtonCustom.tsx +33 -0
- package/src/usecases/ButtonIconColor.tsx +18 -0
- package/src/usecases/ButtonInverse.tsx +30 -0
- package/src/usecases/ButtonUnstyled.tsx +31 -0
- package/src/usecases/CheckboxDisabledOnPress.tsx +62 -0
- package/src/usecases/ClickDuringEnterCase.tsx +59 -0
- package/src/usecases/CodeExamplesInput.tsx +9 -0
- package/src/usecases/ColorTokenFallback.tsx +52 -0
- package/src/usecases/CompilerExtraction.tsx +380 -0
- package/src/usecases/ComplexVariants.tsx +164 -0
- package/src/usecases/CrashAdaptSheet.tsx +98 -0
- package/src/usecases/CustomStyledAnimatedPopover.tsx +42 -0
- package/src/usecases/CustomStyledAnimatedTooltip.tsx +72 -0
- package/src/usecases/DOMNodeAPIs.tsx +154 -0
- package/src/usecases/DialogFocusScopeCase.tsx +277 -0
- package/src/usecases/DialogFocusScopeDebug.tsx +85 -0
- package/src/usecases/DialogNestedCase.tsx +121 -0
- package/src/usecases/DialogOpenControlled.tsx +49 -0
- package/src/usecases/DialogPointerEventsCase.tsx +58 -0
- package/src/usecases/DialogScopedCase.tsx +106 -0
- package/src/usecases/DialogSheetAdaptCase.tsx +178 -0
- package/src/usecases/DialogSheetAdaptResizeCase.tsx +98 -0
- package/src/usecases/DismissLayerStackingCase.tsx +223 -0
- package/src/usecases/DriverDisableAnimationPropsCase.tsx +44 -0
- package/src/usecases/Example.tsx +10 -0
- package/src/usecases/ExitCompletionCase.tsx +713 -0
- package/src/usecases/FocusVisibleButton.tsx +14 -0
- package/src/usecases/FocusVisibleButtonPointer.tsx +13 -0
- package/src/usecases/FocusVisibleButtonWithFocusStyle.tsx +16 -0
- package/src/usecases/FocusWithinCase.tsx +55 -0
- package/src/usecases/FontTokensInVariants.tsx +14 -0
- package/src/usecases/FormButtonTypeCase.tsx +34 -0
- package/src/usecases/GlobalScopedTriggerIsolationCase.tsx +178 -0
- package/src/usecases/GroupHoverMobile.tsx +39 -0
- package/src/usecases/GroupPressInVariant.tsx +92 -0
- package/src/usecases/GroupPressNative.tsx +200 -0
- package/src/usecases/GroupProp.tsx +96 -0
- package/src/usecases/GroupPseudoVariantOverride.tsx +56 -0
- package/src/usecases/GroupUseCases.tsx +94 -0
- package/src/usecases/HeightMediaQueryOverrideCase.tsx +183 -0
- package/src/usecases/InputAutoFocusAfterMenuCase.tsx +105 -0
- package/src/usecases/InputAutoFocusStyledCase.tsx +39 -0
- package/src/usecases/KeyboardControllerTest.tsx +146 -0
- package/src/usecases/ListItem.tsx +123 -0
- package/src/usecases/MediaQueriesV5.tsx +137 -0
- package/src/usecases/MediaQueryGtMd.tsx +73 -0
- package/src/usecases/MenuAboveDialogCase.tsx +75 -0
- package/src/usecases/MenuAccessibilityCase.tsx +133 -0
- package/src/usecases/MenuAnimatePositionCase.tsx +41 -0
- package/src/usecases/MenuArrowAnimatePresenceCase.tsx +98 -0
- package/src/usecases/MenuAsChildPositionCase.tsx +24 -0
- package/src/usecases/MenuAutoResizeCase.tsx +57 -0
- package/src/usecases/MenuBottomCase.tsx +55 -0
- package/src/usecases/MenuFocusLeaveCase.tsx +135 -0
- package/src/usecases/MenuHighlightCase.tsx +44 -0
- package/src/usecases/MenuItemFocusCase.tsx +79 -0
- package/src/usecases/MenuItemPseudoOverrideCase.tsx +270 -0
- package/src/usecases/MenuMultiTriggerCase.tsx +47 -0
- package/src/usecases/MenuOverflowCase.tsx +60 -0
- package/src/usecases/MenuSubCase.tsx +223 -0
- package/src/usecases/MenuSubLeftCase.tsx +178 -0
- package/src/usecases/MenuSubNestedPositionCase.tsx +171 -0
- package/src/usecases/MenuSubStyledCase.tsx +145 -0
- package/src/usecases/MenuThemeCase.tsx +50 -0
- package/src/usecases/MenuUnstyledCase.tsx +52 -0
- package/src/usecases/MultiDriverAnimation.tsx +118 -0
- package/src/usecases/NativePortalTest.tsx +179 -0
- package/src/usecases/NewInputBasic.tsx +16 -0
- package/src/usecases/NewInputEvents.tsx +29 -0
- package/src/usecases/NonGuiTextStyledType.tsx +23 -0
- package/src/usecases/OnLayoutCase.tsx +134 -0
- package/src/usecases/OnLayoutScaleCase.tsx +88 -0
- package/src/usecases/OnLayoutStressCase.tsx +353 -0
- package/src/usecases/OpacityModifierCase.tsx +113 -0
- package/src/usecases/OverlayStyled.tsx +66 -0
- package/src/usecases/ParagraphSpanFontInheritance.tsx +53 -0
- package/src/usecases/PlaceholderTextColor.tsx +20 -0
- package/src/usecases/PointerEventsCase.tsx +100 -0
- package/src/usecases/PopoverAndMenuMultiTriggerCase.tsx +138 -0
- package/src/usecases/PopoverCase.tsx +222 -0
- package/src/usecases/PopoverContentStyledPlusAnimations.tsx +44 -0
- package/src/usecases/PopoverFocusScopeCase.tsx +171 -0
- package/src/usecases/PopoverHoverableCase.tsx +167 -0
- package/src/usecases/PopoverHoverableDisableClickCase.tsx +118 -0
- package/src/usecases/PopoverHoverableRapidCase.tsx +103 -0
- package/src/usecases/PopoverHoverableScopedCase.tsx +135 -0
- package/src/usecases/PopoverScopedCase.tsx +76 -0
- package/src/usecases/PopoverTriggerIsolationCase.tsx +80 -0
- package/src/usecases/PressStyleNative.tsx +143 -0
- package/src/usecases/PseudoStyleMerge.tsx +25 -0
- package/src/usecases/PseudoTransitionCase.tsx +174 -0
- package/src/usecases/RawAnimatedValueCase.tsx +231 -0
- package/src/usecases/RemoveScrollCase.tsx +66 -0
- package/src/usecases/RenderPropCase.tsx +263 -0
- package/src/usecases/SafeAreaCase.tsx +236 -0
- package/src/usecases/ScrollViewRefCase.tsx +88 -0
- package/src/usecases/SecondPage.tsx +5 -0
- package/src/usecases/SelectAndroidOnPress.tsx +129 -0
- package/src/usecases/SelectFocusScopeCase.tsx +270 -0
- package/src/usecases/SelectRemount.tsx +136 -0
- package/src/usecases/Shadows.tsx +5 -0
- package/src/usecases/SheetAnimationCase.tsx +155 -0
- package/src/usecases/SheetDragCase.tsx +183 -0
- package/src/usecases/SheetDragResistCase.tsx +433 -0
- package/src/usecases/SheetDragResistCase.web.tsx +359 -0
- package/src/usecases/SheetKeyboardDragCase.tsx +328 -0
- package/src/usecases/SheetKeyboardFitContentCase.tsx +165 -0
- package/src/usecases/SheetOnAnimationCompleteCase.tsx +54 -0
- package/src/usecases/SheetScrollLockCase.tsx +166 -0
- package/src/usecases/SheetScrollableDrag.tsx +249 -0
- package/src/usecases/SheetSnapPointsFitCase.tsx +393 -0
- package/src/usecases/ShorthandVariables.tsx +49 -0
- package/src/usecases/SlowThemeReRender.tsx +48 -0
- package/src/usecases/SpinnerCustomColors.tsx +34 -0
- package/src/usecases/StackZIndex.tsx +82 -0
- package/src/usecases/StressPage.tsx +301 -0
- package/src/usecases/StylePlatform.tsx +30 -0
- package/src/usecases/StyleProp.tsx +29 -0
- package/src/usecases/StyledAnchor.tsx +27 -0
- package/src/usecases/StyledButtonAnimationAuto.tsx +99 -0
- package/src/usecases/StyledButtonTheme.tsx +63 -0
- package/src/usecases/StyledButtonVariantPseudo.tsx +25 -0
- package/src/usecases/StyledButtonVariantPseudoMerge.tsx +77 -0
- package/src/usecases/StyledCheckboxTheme.tsx +23 -0
- package/src/usecases/StyledContextColor.tsx +246 -0
- package/src/usecases/StyledContextTokens.tsx +147 -0
- package/src/usecases/StyledHOCNamed.tsx +20 -0
- package/src/usecases/StyledHtmlCase.tsx +144 -0
- package/src/usecases/StyledIconColor.tsx +19 -0
- package/src/usecases/StyledInputFocusStyle.tsx +21 -0
- package/src/usecases/StyledInputOnFocus.tsx +30 -0
- package/src/usecases/StyledMediaQueryMerge.tsx +95 -0
- package/src/usecases/StyledOverridePsuedo.tsx +26 -0
- package/src/usecases/StyledRNW.tsx +61 -0
- package/src/usecases/StyledStyleableInputOnFocus.tsx +34 -0
- package/src/usecases/StyledStyleableInputVariant.tsx +48 -0
- package/src/usecases/StyledStyledStyleableInputOnFocus.tsx +36 -0
- package/src/usecases/StyledVariantTextColor.tsx +25 -0
- package/src/usecases/StyledViewOnFocus.tsx +32 -0
- package/src/usecases/TabHoverAnimationCase.tsx +212 -0
- package/src/usecases/TextNestedInheritance.tsx +80 -0
- package/src/usecases/ThemeChange.tsx +100 -0
- package/src/usecases/ThemeChangeBasic.tsx +52 -0
- package/src/usecases/ThemeComponentResolution.tsx +119 -0
- package/src/usecases/ThemeConditionalName.tsx +31 -0
- package/src/usecases/ThemeMediaAnimationCase.tsx +39 -0
- package/src/usecases/ThemeMutation.tsx +86 -0
- package/src/usecases/ThemeNested.tsx +103 -0
- package/src/usecases/ThemeReset.tsx +62 -0
- package/src/usecases/ThemeShallowCase.tsx +83 -0
- package/src/usecases/ToastCase.tsx +46 -0
- package/src/usecases/ToggleGroupActiveProps.tsx +40 -0
- package/src/usecases/ToggleGroupXGroupCase.tsx +104 -0
- package/src/usecases/TooltipAnimationCase.tsx +99 -0
- package/src/usecases/TooltipCase.tsx +32 -0
- package/src/usecases/TooltipGlobalPatternCase.tsx +83 -0
- package/src/usecases/TooltipGroupCase.tsx +102 -0
- package/src/usecases/TooltipMultiTriggerCase.tsx +88 -0
- package/src/usecases/TooltipPositionJumpCase.tsx +91 -0
- package/src/usecases/TooltipTriggerInlineCase.tsx +60 -0
- package/src/usecases/TransformMediaQueryMerge.tsx +98 -0
- package/src/usecases/UseCases.tsx +409 -0
- package/src/usecases/UseTheme.tsx +41 -0
- package/src/usecases/V5ThemeBuilderOutput.tsx +231 -0
- package/src/usecases/VariantFontFamily.tsx +25 -0
- package/src/usecases/VariantsOrder.tsx +117 -0
- package/src/usecases/ZIndex.tsx +155 -0
- package/src/usecases/helpers.tsx +44 -0
- package/src/usecases/index.native.ts +122 -0
- package/src/usecases/index.ts +3 -0
- package/src/usecases/index.web.ts +177 -0
- package/tests/AnimatePresenceEnterExit.animated.test.tsx +176 -0
- package/tests/AnimatedByProp.animated.test.tsx +138 -0
- package/tests/AnimationBehavior.animated.test.tsx +543 -0
- package/tests/AnimationTiming.animated.test.tsx +195 -0
- package/tests/AnimationsWithMediaQueries.animated.test.tsx +154 -0
- package/tests/BuildAButton.test.tsx +87 -0
- package/tests/ButtonCircular.test.tsx +17 -0
- package/tests/ButtonCustom.test.tsx +17 -0
- package/tests/ButtonIconColor.test.tsx +23 -0
- package/tests/ButtonUnstyled.test.tsx +56 -0
- package/tests/ClickDuringEnter.animated.test.tsx +174 -0
- package/tests/ColorTokenFallback.test.tsx +45 -0
- package/tests/DOMNodeAPIs.test.tsx +161 -0
- package/tests/DialogFocusScope.animated.test.tsx +309 -0
- package/tests/DialogNested.test.tsx +128 -0
- package/tests/DialogOpenControlled.test.tsx +42 -0
- package/tests/DialogPointerEvents.animated.test.tsx +108 -0
- package/tests/DialogScoped.test.tsx +137 -0
- package/tests/DialogSheetAdapt.test.tsx +68 -0
- package/tests/DialogSheetAdaptResize.test.tsx +161 -0
- package/tests/DismissLayerStacking.test.tsx +292 -0
- package/tests/DriverDisableAnimationProps.animated.test.tsx +157 -0
- package/tests/ExitCompletion.animated.test.tsx +425 -0
- package/tests/ExitTimingCheck.animated.test.ts +34 -0
- package/tests/FocusVisibleButton.test.tsx +41 -0
- package/tests/FocusVisibleButtonPointerFocus.test.tsx +23 -0
- package/tests/FocusVisibleButtonPointerFocusWithFocusStyle.test.tsx +40 -0
- package/tests/FocusWithinStyle.animated.test.tsx +66 -0
- package/tests/FocusWithinStyle.test.tsx +60 -0
- package/tests/FormButtonType.test.tsx +42 -0
- package/tests/GlobalScopedTriggerIsolation.test.tsx +89 -0
- package/tests/GroupHoverMobile.test.tsx +52 -0
- package/tests/GroupPressInVariant.test.tsx +82 -0
- package/tests/GroupProp.test.tsx +30 -0
- package/tests/GroupPseudoVariantOverride.test.tsx +57 -0
- package/tests/GroupUseCases.test.tsx +111 -0
- package/tests/GuiSiteMotion.test.ts +481 -0
- package/tests/HeightMediaQueryOverride.test.tsx +112 -0
- package/tests/InputAutoFocusAfterMenu.test.tsx +55 -0
- package/tests/InputAutoFocusStyled.test.tsx +22 -0
- package/tests/ListItem.test.tsx +129 -0
- package/tests/MediaQueriesV5.test.tsx +113 -0
- package/tests/MediaQueryGtMd.test.tsx +84 -0
- package/tests/MenuAboveDialog.test.tsx +108 -0
- package/tests/MenuAccessibility.test.tsx +346 -0
- package/tests/MenuAnimatePosition.animated.test.tsx +57 -0
- package/tests/MenuArrowAnimatePresence.animated.test.tsx +71 -0
- package/tests/MenuAsChildPosition.test.tsx +16 -0
- package/tests/MenuAutoResize.test.tsx +54 -0
- package/tests/MenuFocusLeave.test.tsx +181 -0
- package/tests/MenuHighlight.test.tsx +165 -0
- package/tests/MenuHoverKeyboardBugs.test.tsx +252 -0
- package/tests/MenuItemFocus.test.tsx +59 -0
- package/tests/MenuItemPseudoOverride.test.tsx +231 -0
- package/tests/MenuMultiTrigger.test.tsx +101 -0
- package/tests/MenuOverflow.test.tsx +93 -0
- package/tests/MenuStayInFrame.test.tsx +102 -0
- package/tests/MenuSubKeyboardFocus.test.tsx +220 -0
- package/tests/MenuSubLeftSafePolygon.test.tsx +88 -0
- package/tests/MenuSubNestedPosition.test.tsx +48 -0
- package/tests/MenuSubSafePolygon.test.tsx +97 -0
- package/tests/MenuSubStyled.test.tsx +40 -0
- package/tests/MenuTheme.test.tsx +34 -0
- package/tests/MenuUnstyled.test.tsx +56 -0
- package/tests/MultiDriverAnimation.test.tsx +207 -0
- package/tests/NewInputBasic.test.tsx +50 -0
- package/tests/NewInputEvents.test.tsx +55 -0
- package/tests/OnLayout.test.tsx +163 -0
- package/tests/OnLayoutScale.test.tsx +100 -0
- package/tests/OnLayoutStress.test.tsx +304 -0
- package/tests/ParagraphSpanFontInheritance.test.tsx +73 -0
- package/tests/PointerEvents.test.tsx +123 -0
- package/tests/Popover.animated.test.tsx +234 -0
- package/tests/PopoverAndMenuMultiTrigger.test.tsx +184 -0
- package/tests/PopoverAnimatePosition.animated.test.tsx +51 -0
- package/tests/PopoverClickDuringEnter.animated.test.tsx +197 -0
- package/tests/PopoverFocusScope.test.tsx +242 -0
- package/tests/PopoverHoverable.test.tsx +383 -0
- package/tests/PopoverHoverableDisableClick.test.tsx +106 -0
- package/tests/PopoverHoverableRapid.test.tsx +129 -0
- package/tests/PopoverHoverableReposition.test.tsx +111 -0
- package/tests/PopoverHoverableScoped.animated.test.tsx +103 -0
- package/tests/PopoverHoverableStress.test.tsx +169 -0
- package/tests/PopoverInitialPosition.animated.test.tsx +82 -0
- package/tests/PopoverMiddlewareSkipRegression.animated.test.tsx +221 -0
- package/tests/PopoverScoped.test.tsx +128 -0
- package/tests/PopoverScopedPositionGlitch.animated.test.tsx +184 -0
- package/tests/PopoverTriggerIsolation.test.tsx +62 -0
- package/tests/PseudoTransition.animated.test.tsx +319 -0
- package/tests/RawAnimatedValue.test.tsx +147 -0
- package/tests/RemoveScroll.test.tsx +223 -0
- package/tests/RenderProp.test.tsx +293 -0
- package/tests/ScrollViewRef.test.tsx +39 -0
- package/tests/SelectClickHold.test.tsx +147 -0
- package/tests/SelectFocusScope.test.tsx +176 -0
- package/tests/SelectInnerPositioning.test.tsx +82 -0
- package/tests/SelectKeyboardNav.test.tsx +173 -0
- package/tests/SelectPositioning.test.tsx +56 -0
- package/tests/SelectTypeahead.test.tsx +63 -0
- package/tests/Shadows.test.tsx +14 -0
- package/tests/SheetAnimation.animated.test.tsx +413 -0
- package/tests/SheetDrag.animated.test.tsx +223 -0
- package/tests/SheetDragResist.animated.test.tsx +393 -0
- package/tests/SheetOnAnimationComplete.animated.test.tsx +62 -0
- package/tests/SheetScrollLock.animated.test.tsx +287 -0
- package/tests/SheetScrollableDrag.animated.test.tsx +1264 -0
- package/tests/SheetSnapPointsFit.animated.test.tsx +259 -0
- package/tests/ShorthandVariables.test.tsx +44 -0
- package/tests/SpinnerCustomColors.test.tsx +67 -0
- package/tests/StackZIndex.test.tsx +51 -0
- package/tests/StressPagePerf.test.tsx +76 -0
- package/tests/StylePlatform.test.tsx +38 -0
- package/tests/StyleProp.test.tsx +20 -0
- package/tests/StyledAnchor.test.tsx +17 -0
- package/tests/StyledButtonTheme.test.tsx +22 -0
- package/tests/StyledButtonVariantPseudo.test.tsx +20 -0
- package/tests/StyledButtonVariantPseudoMerge.animated.test.tsx +33 -0
- package/tests/StyledCheckboxTheme.test.tsx +16 -0
- package/tests/StyledContextColor.test.tsx +119 -0
- package/tests/StyledContextTokens.test.tsx +56 -0
- package/tests/StyledHOCNamed.test.tsx +16 -0
- package/tests/StyledHtml.test.tsx +161 -0
- package/tests/StyledIconColor.test.tsx +32 -0
- package/tests/StyledInputFocusStyle.test.tsx +19 -0
- package/tests/StyledInputOnFocus.test.tsx +27 -0
- package/tests/StyledMediaQueryMerge.test.tsx +66 -0
- package/tests/StyledRNW.test.tsx +17 -0
- package/tests/StyledStyleableInputOnFocus.test.tsx +27 -0
- package/tests/StyledStyleableInputVariant.test.tsx +22 -0
- package/tests/StyledStyledStyleableInputOnFocus.test.tsx +27 -0
- package/tests/StyledVariantTextColor.test.tsx +24 -0
- package/tests/StyledViewOnFocus.test.tsx +27 -0
- package/tests/TabHoverAnimation.animated.test.tsx +468 -0
- package/tests/TabHoverPositionSmooth.animated.test.tsx +129 -0
- package/tests/TextNestedInheritance.test.tsx +93 -0
- package/tests/ThemeChange.test.tsx +70 -0
- package/tests/ThemeComponentResolution.test.tsx +82 -0
- package/tests/ThemeConditionalName.test.tsx +34 -0
- package/tests/ThemeMediaAnimation.test.tsx +65 -0
- package/tests/ThemeNested.test.tsx +141 -0
- package/tests/ThemeReset.test.tsx +63 -0
- package/tests/ThemeShallow.test.tsx +95 -0
- package/tests/Toast.test.tsx +106 -0
- package/tests/ToggleGroup.test.tsx +61 -0
- package/tests/ToggleGroupActiveProps.test.tsx +38 -0
- package/tests/ToggleGroupXGroup.test.tsx +172 -0
- package/tests/TooltipAnimation.animated.test.tsx +260 -0
- package/tests/TooltipEnterInterrupt.animated.test.tsx +76 -0
- package/tests/TooltipGlobalPattern.animated.test.tsx +208 -0
- package/tests/TooltipGroup.animated.test.tsx +79 -0
- package/tests/TooltipMultiTrigger.test.tsx +116 -0
- package/tests/TooltipPositionJump.animated.test.tsx +229 -0
- package/tests/TooltipPositionJumpNotes.md +219 -0
- package/tests/TooltipRapidSwitch.animated.test.tsx +399 -0
- package/tests/TooltipTriggerInline.test.tsx +65 -0
- package/tests/TransformMediaQueryMerge.test.tsx +104 -0
- package/tests/TransitionEnterExit.animated.test.tsx +311 -0
- package/tests/UseTheme.test.tsx +16 -0
- package/tests/V5ThemeBuilderOutput.test.tsx +164 -0
- package/tests/VariantFontFamily.test.tsx +11 -0
- package/tests/VariantsOrder.test.tsx +53 -0
- package/tests/_debug_position.mjs +52 -0
- package/tests/test-utils.ts +106 -0
- package/tests/utils.tsx +54 -0
- package/tsconfig.json +45 -0
- package/vite-env.d.ts +1 -0
- package/vite.config.ts +14 -0
- package/webpack.config.js +139 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for motion driver issues on gui.hanzo.ai
|
|
5
|
+
*
|
|
6
|
+
* These tests run against the actual gui.hanzo.ai site (localhost:4444)
|
|
7
|
+
* to test motion driver behavior on the real site components.
|
|
8
|
+
*
|
|
9
|
+
* SKIPPED: requires gui.hanzo.ai running on localhost:4444
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const SITE_URL = 'http://localhost:4444'
|
|
13
|
+
|
|
14
|
+
test.use({
|
|
15
|
+
video: 'on',
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test.describe.skip('Gui.dev Motion Issues', () => {
|
|
19
|
+
test.beforeAll(async () => {
|
|
20
|
+
// verify site is running
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test.describe('Logo Jitter Bug', () => {
|
|
24
|
+
test('dot indicator should not jitter when moving mouse rapidly across HANZO_GUI text', async ({
|
|
25
|
+
page,
|
|
26
|
+
}) => {
|
|
27
|
+
await page.goto(SITE_URL, { waitUntil: 'networkidle' })
|
|
28
|
+
await page.waitForTimeout(1000)
|
|
29
|
+
|
|
30
|
+
// find the logo-words element (in header, not footer)
|
|
31
|
+
const logoWords = page.locator('header .logo-words').first()
|
|
32
|
+
await expect(logoWords).toBeVisible()
|
|
33
|
+
|
|
34
|
+
const logoBox = await logoWords.boundingBox()
|
|
35
|
+
if (!logoBox) {
|
|
36
|
+
throw new Error('Could not find logo-words bounding box')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// inject position tracking for the Circle (dot indicator)
|
|
40
|
+
// the dot is a Circle with position absolute inside logo-words
|
|
41
|
+
await page.evaluate(() => {
|
|
42
|
+
;(window as any).__dotPositions = []
|
|
43
|
+
;(window as any).__dotDebug = { found: false, selector: null }
|
|
44
|
+
const track = () => {
|
|
45
|
+
// find the circle element inside logo-words
|
|
46
|
+
// it's in the header, which has render="header" so becomes a <header> tag
|
|
47
|
+
const header = document.querySelector('header')
|
|
48
|
+
if (!header) {
|
|
49
|
+
;(window as any).__dotDebug.selector = 'no header found'
|
|
50
|
+
requestAnimationFrame(track)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const logoWords = header.querySelector('.logo-words')
|
|
54
|
+
if (!logoWords) {
|
|
55
|
+
;(window as any).__dotDebug.selector = 'no logo-words in header'
|
|
56
|
+
requestAnimationFrame(track)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
// the circle is the first child element with position absolute (the SVG is not absolute)
|
|
60
|
+
let found = false
|
|
61
|
+
for (const child of Array.from(logoWords.children)) {
|
|
62
|
+
const el = child as HTMLElement
|
|
63
|
+
const computed = getComputedStyle(el)
|
|
64
|
+
if (computed.position === 'absolute') {
|
|
65
|
+
found = true
|
|
66
|
+
;(window as any).__dotDebug.found = true
|
|
67
|
+
;(window as any).__dotDebug.selector = 'found absolute positioned child'
|
|
68
|
+
const transform = computed.transform
|
|
69
|
+
if (transform && transform !== 'none') {
|
|
70
|
+
const match = transform.match(
|
|
71
|
+
/matrix\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*([^,]+),\s*([^)]+)\)/
|
|
72
|
+
)
|
|
73
|
+
if (match) {
|
|
74
|
+
;(window as any).__dotPositions.push({
|
|
75
|
+
x: parseFloat(match[1]),
|
|
76
|
+
y: parseFloat(match[2]),
|
|
77
|
+
time: Date.now(),
|
|
78
|
+
transform,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
// transform may be in translate form or not set yet
|
|
83
|
+
const left = parseFloat(computed.left) || 0
|
|
84
|
+
const top = parseFloat(computed.top) || 0
|
|
85
|
+
;(window as any).__dotPositions.push({
|
|
86
|
+
x: left,
|
|
87
|
+
y: top,
|
|
88
|
+
time: Date.now(),
|
|
89
|
+
noTransform: true,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
break
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!found) {
|
|
96
|
+
;(window as any).__dotDebug.selector =
|
|
97
|
+
'no absolute child - children: ' + logoWords.children.length
|
|
98
|
+
}
|
|
99
|
+
requestAnimationFrame(track)
|
|
100
|
+
}
|
|
101
|
+
requestAnimationFrame(track)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// hover over logo first to activate animation
|
|
105
|
+
const centerY = logoBox.y + logoBox.height / 2
|
|
106
|
+
await page.mouse.move(logoBox.x + 10, centerY)
|
|
107
|
+
await page.waitForTimeout(300)
|
|
108
|
+
|
|
109
|
+
// do very rapid back-and-forth to trigger jitter
|
|
110
|
+
// mimic user quickly flicking mouse back and forth every few frames
|
|
111
|
+
const screenshots: string[] = []
|
|
112
|
+
for (let sweep = 0; sweep < 20; sweep++) {
|
|
113
|
+
// quick flick right then left - this mimics the user pattern that causes jitter
|
|
114
|
+
// every 2-3 mouse moves we change direction
|
|
115
|
+
const rightX = logoBox.x + logoBox.width * 0.8
|
|
116
|
+
const leftX = logoBox.x + logoBox.width * 0.2
|
|
117
|
+
const midX = logoBox.x + logoBox.width * 0.5
|
|
118
|
+
|
|
119
|
+
// rapid: left->right->left in quick succession
|
|
120
|
+
await page.mouse.move(leftX, centerY)
|
|
121
|
+
await page.waitForTimeout(16) // ~60fps
|
|
122
|
+
await page.mouse.move(rightX, centerY)
|
|
123
|
+
await page.waitForTimeout(16)
|
|
124
|
+
await page.mouse.move(midX, centerY)
|
|
125
|
+
await page.waitForTimeout(16)
|
|
126
|
+
|
|
127
|
+
// take screenshot every few sweeps to see visual state
|
|
128
|
+
if (sweep % 5 === 0) {
|
|
129
|
+
const screenshot = await page.screenshot()
|
|
130
|
+
screenshots.push(screenshot.toString('base64').slice(0, 50) + '...')
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// also do the continuous sweep pattern
|
|
135
|
+
for (let sweep = 0; sweep < 3; sweep++) {
|
|
136
|
+
for (let i = 0; i <= 10; i++) {
|
|
137
|
+
const t = i / 10
|
|
138
|
+
const x = logoBox.x + logoBox.width * t
|
|
139
|
+
await page.mouse.move(x, centerY)
|
|
140
|
+
await page.waitForTimeout(8)
|
|
141
|
+
}
|
|
142
|
+
for (let i = 0; i <= 10; i++) {
|
|
143
|
+
const t = i / 10
|
|
144
|
+
const x = logoBox.x + logoBox.width - logoBox.width * t
|
|
145
|
+
await page.mouse.move(x, centerY)
|
|
146
|
+
await page.waitForTimeout(8)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log(`Took ${screenshots.length} screenshots during test`)
|
|
151
|
+
|
|
152
|
+
await page.waitForTimeout(500)
|
|
153
|
+
|
|
154
|
+
// analyze positions for unexpected jumps
|
|
155
|
+
const positions = await page.evaluate(() => (window as any).__dotPositions || [])
|
|
156
|
+
const debug = await page.evaluate(() => (window as any).__dotDebug || {})
|
|
157
|
+
|
|
158
|
+
console.log(`Collected ${positions.length} dot positions`)
|
|
159
|
+
console.log(`Debug:`, debug)
|
|
160
|
+
if (positions.length > 0) {
|
|
161
|
+
console.log('First position:', positions[0])
|
|
162
|
+
console.log('Last position:', positions[positions.length - 1])
|
|
163
|
+
// show all x values to see the pattern
|
|
164
|
+
const xValues = positions.map((p: any) => Math.round(p.x))
|
|
165
|
+
console.log('X values sample (first 50):', xValues.slice(0, 50).join(', '))
|
|
166
|
+
console.log('X values sample (last 50):', xValues.slice(-50).join(', '))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// analyze for jitter patterns:
|
|
170
|
+
// 1. Large sudden jumps (>20px in one frame)
|
|
171
|
+
// 2. Rapid back-and-forth oscillations
|
|
172
|
+
const jumps: any[] = []
|
|
173
|
+
const oscillations: any[] = []
|
|
174
|
+
|
|
175
|
+
for (let i = 1; i < positions.length; i++) {
|
|
176
|
+
const prev = positions[i - 1]
|
|
177
|
+
const curr = positions[i]
|
|
178
|
+
const dx = curr.x - prev.x
|
|
179
|
+
const timeDelta = curr.time - prev.time
|
|
180
|
+
|
|
181
|
+
// detect large jumps: >20px change in a single frame
|
|
182
|
+
if (Math.abs(dx) > 20 && timeDelta < 30) {
|
|
183
|
+
jumps.push({
|
|
184
|
+
idx: i,
|
|
185
|
+
from: Math.round(prev.x),
|
|
186
|
+
to: Math.round(curr.x),
|
|
187
|
+
dx: Math.round(dx),
|
|
188
|
+
timeDelta,
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// detect oscillations: A->B->A pattern in <100ms
|
|
194
|
+
for (let i = 2; i < positions.length; i++) {
|
|
195
|
+
const a = positions[i - 2]
|
|
196
|
+
const b = positions[i - 1]
|
|
197
|
+
const c = positions[i]
|
|
198
|
+
const totalTime = c.time - a.time
|
|
199
|
+
|
|
200
|
+
if (totalTime < 100) {
|
|
201
|
+
// check if c is close to a (returned to original position)
|
|
202
|
+
const returnDistance = Math.abs(c.x - a.x)
|
|
203
|
+
const deviationDistance = Math.abs(b.x - a.x)
|
|
204
|
+
if (returnDistance < 3 && deviationDistance > 8) {
|
|
205
|
+
oscillations.push({
|
|
206
|
+
idx: i,
|
|
207
|
+
pattern: `${Math.round(a.x)} -> ${Math.round(b.x)} -> ${Math.round(c.x)}`,
|
|
208
|
+
totalTime,
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log(`Large jumps (>20px): ${jumps.length}`)
|
|
215
|
+
if (jumps.length > 0) {
|
|
216
|
+
console.log('Jump samples:', jumps.slice(0, 5))
|
|
217
|
+
}
|
|
218
|
+
console.log(`Oscillations: ${oscillations.length}`)
|
|
219
|
+
if (oscillations.length > 0) {
|
|
220
|
+
console.log('Oscillation samples:', oscillations.slice(0, 5))
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`Detected ${jumps.length} jitters`)
|
|
224
|
+
if (jumps.length > 0) {
|
|
225
|
+
console.log('First few jitters:', JSON.stringify(jumps.slice(0, 5), null, 2))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// allow some jitter but not excessive
|
|
229
|
+
// in a well-behaved animation, we might get 0-3 jitters at direction changes
|
|
230
|
+
// the bug causes 10+ jitters
|
|
231
|
+
expect(jumps.length, `Too many jitters detected: ${jumps.length}`).toBeLessThan(10)
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
test.describe('PromoLinksRow Tooltip Position Jump', () => {
|
|
236
|
+
test('tooltip should not jump when moving between promo buttons', async ({
|
|
237
|
+
page,
|
|
238
|
+
}) => {
|
|
239
|
+
await page.goto(SITE_URL, { waitUntil: 'networkidle' })
|
|
240
|
+
await page.waitForTimeout(1000)
|
|
241
|
+
|
|
242
|
+
// resize to ensure promo buttons are visible
|
|
243
|
+
await page.setViewportSize({ width: 1400, height: 900 })
|
|
244
|
+
await page.waitForTimeout(500)
|
|
245
|
+
|
|
246
|
+
// find the PromoLinksRow buttons (Starter Kit/Takeout, Copy-Paste UI/Bento, Add Even)
|
|
247
|
+
// these are the buttons that use the scoped tooltip with animatePosition
|
|
248
|
+
// the buttons show "Starter Kit", "Copy-Paste UI", "Add Even"
|
|
249
|
+
const takeoutBtn = page.locator('button:has-text("Starter Kit")').first()
|
|
250
|
+
const bentoBtn = page.locator('button:has-text("Copy-Paste UI")').first()
|
|
251
|
+
const consultingBtn = page.locator('button:has-text("Hire Us!")').first()
|
|
252
|
+
|
|
253
|
+
const takeoutVisible = await takeoutBtn.isVisible().catch(() => false)
|
|
254
|
+
const bentoVisible = await bentoBtn.isVisible().catch(() => false)
|
|
255
|
+
const consultingVisible = await consultingBtn.isVisible().catch(() => false)
|
|
256
|
+
|
|
257
|
+
if (!takeoutVisible || !bentoVisible || !consultingVisible) {
|
|
258
|
+
console.log('PromoLinksRow buttons not visible, skipping')
|
|
259
|
+
console.log('Takeout visible:', takeoutVisible)
|
|
260
|
+
console.log('Bento visible:', bentoVisible)
|
|
261
|
+
console.log('Consulting visible:', consultingVisible)
|
|
262
|
+
test.skip()
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const takeoutBox = await takeoutBtn.boundingBox()
|
|
267
|
+
const bentoBox = await bentoBtn.boundingBox()
|
|
268
|
+
const consultingBox = await consultingBtn.boundingBox()
|
|
269
|
+
|
|
270
|
+
if (!takeoutBox || !bentoBox || !consultingBox) {
|
|
271
|
+
throw new Error('Could not get bounding boxes for promo buttons')
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
console.log('Button positions:')
|
|
275
|
+
console.log(' Takeout (left):', takeoutBox.x)
|
|
276
|
+
console.log(' Bento (middle):', bentoBox.x)
|
|
277
|
+
console.log(' Consulting (right):', consultingBox.x)
|
|
278
|
+
|
|
279
|
+
// inject position tracking for tooltip
|
|
280
|
+
// the tooltip content has animatePosition and scope="promo-tooltip"
|
|
281
|
+
await page.evaluate(() => {
|
|
282
|
+
;(window as any).__tooltipPositions = []
|
|
283
|
+
;(window as any).__jumpLog = []
|
|
284
|
+
let lastPos: any = null
|
|
285
|
+
|
|
286
|
+
const track = () => {
|
|
287
|
+
// find tooltip by data-placement attribute (set by Popper)
|
|
288
|
+
const tooltip = document.querySelector('[data-placement]') as HTMLElement
|
|
289
|
+
if (tooltip) {
|
|
290
|
+
// get actual rendered position
|
|
291
|
+
const rect = tooltip.getBoundingClientRect()
|
|
292
|
+
const transform = getComputedStyle(tooltip).transform
|
|
293
|
+
let x = rect.x
|
|
294
|
+
let y = rect.y
|
|
295
|
+
|
|
296
|
+
// also extract matrix values if present
|
|
297
|
+
if (transform && transform !== 'none') {
|
|
298
|
+
const match = transform.match(
|
|
299
|
+
/matrix\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*([^,]+),\s*([^)]+)\)/
|
|
300
|
+
)
|
|
301
|
+
if (match) {
|
|
302
|
+
// use rect for tracking since that's where it visually appears
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const pos = { x: rect.x, y: rect.y, time: Date.now() }
|
|
307
|
+
;(window as any).__tooltipPositions.push(pos)
|
|
308
|
+
|
|
309
|
+
// detect jumps in real-time
|
|
310
|
+
if (lastPos) {
|
|
311
|
+
const dx = Math.abs(x - lastPos.x)
|
|
312
|
+
if (dx > 100) {
|
|
313
|
+
const msg = `JUMP: ${lastPos.x.toFixed(0)} -> ${x.toFixed(0)} (dx: ${dx.toFixed(0)})`
|
|
314
|
+
;(window as any).__jumpLog.push(msg)
|
|
315
|
+
console.log('%c' + msg, 'color: red; font-weight: bold')
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
lastPos = pos
|
|
319
|
+
} else {
|
|
320
|
+
lastPos = null
|
|
321
|
+
}
|
|
322
|
+
requestAnimationFrame(track)
|
|
323
|
+
}
|
|
324
|
+
requestAnimationFrame(track)
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
// step 1: hover on rightmost button (Consulting) to open tooltip
|
|
328
|
+
const consultingCenter = {
|
|
329
|
+
x: consultingBox.x + consultingBox.width / 2,
|
|
330
|
+
y: consultingBox.y + consultingBox.height / 2,
|
|
331
|
+
}
|
|
332
|
+
await page.mouse.move(consultingCenter.x, consultingCenter.y)
|
|
333
|
+
await page.waitForTimeout(800) // wait for tooltip to fully appear
|
|
334
|
+
|
|
335
|
+
// step 2: FAST move to leftmost button (Takeout)
|
|
336
|
+
// this is the pattern that triggers the jump bug
|
|
337
|
+
const takeoutCenter = {
|
|
338
|
+
x: takeoutBox.x + takeoutBox.width / 2,
|
|
339
|
+
y: takeoutBox.y + takeoutBox.height / 2,
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// fast sweep across all 3 buttons
|
|
343
|
+
const steps = 10
|
|
344
|
+
for (let i = 1; i <= steps; i++) {
|
|
345
|
+
const t = i / steps
|
|
346
|
+
const x = consultingCenter.x + (takeoutCenter.x - consultingCenter.x) * t
|
|
347
|
+
const y = consultingCenter.y + (takeoutCenter.y - consultingCenter.y) * t
|
|
348
|
+
await page.mouse.move(x, y)
|
|
349
|
+
await page.waitForTimeout(10) // fast movement
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
await page.waitForTimeout(400)
|
|
353
|
+
|
|
354
|
+
// analyze for position jumps
|
|
355
|
+
const positions = await page.evaluate(
|
|
356
|
+
() => (window as any).__tooltipPositions || []
|
|
357
|
+
)
|
|
358
|
+
const jumpLog = await page.evaluate(() => (window as any).__jumpLog || [])
|
|
359
|
+
|
|
360
|
+
const jumps: any[] = []
|
|
361
|
+
for (let i = 1; i < positions.length; i++) {
|
|
362
|
+
const prev = positions[i - 1]
|
|
363
|
+
const curr = positions[i]
|
|
364
|
+
const dx = curr.x - prev.x
|
|
365
|
+
const timeDelta = curr.time - prev.time
|
|
366
|
+
|
|
367
|
+
// detect large sudden jumps (>100px in <50ms)
|
|
368
|
+
if (Math.abs(dx) > 100 && timeDelta < 50) {
|
|
369
|
+
jumps.push({
|
|
370
|
+
idx: i,
|
|
371
|
+
from: Math.round(prev.x),
|
|
372
|
+
to: Math.round(curr.x),
|
|
373
|
+
dx: Math.round(dx),
|
|
374
|
+
timeDelta,
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.log(`Collected ${positions.length} tooltip positions`)
|
|
380
|
+
if (positions.length > 0) {
|
|
381
|
+
const xValues = positions.map((p: any) => Math.round(p.x))
|
|
382
|
+
console.log('X positions (first 30):', xValues.slice(0, 30).join(', '))
|
|
383
|
+
console.log('X positions (last 30):', xValues.slice(-30).join(', '))
|
|
384
|
+
}
|
|
385
|
+
console.log(`Jump log from browser:`, jumpLog)
|
|
386
|
+
console.log(`Detected ${jumps.length} position jumps`)
|
|
387
|
+
if (jumps.length > 0) {
|
|
388
|
+
console.log('Jumps:', JSON.stringify(jumps.slice(0, 5), null, 2))
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
expect(jumps.length, `Tooltip jumped ${jumps.length} times!`).toBe(0)
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
test('inner content animations should not get stuck at endpoints', async ({
|
|
395
|
+
page,
|
|
396
|
+
}) => {
|
|
397
|
+
await page.goto(SITE_URL, { waitUntil: 'networkidle' })
|
|
398
|
+
await page.waitForTimeout(1000)
|
|
399
|
+
|
|
400
|
+
await page.setViewportSize({ width: 1400, height: 900 })
|
|
401
|
+
await page.waitForTimeout(500)
|
|
402
|
+
|
|
403
|
+
// find header links
|
|
404
|
+
const coreLink = page.locator('a:has-text("Core")').first()
|
|
405
|
+
const menuButton = page.locator('button[aria-label="Open the main menu"]').first()
|
|
406
|
+
|
|
407
|
+
const coreVisible = await coreLink.isVisible().catch(() => false)
|
|
408
|
+
if (!coreVisible) {
|
|
409
|
+
console.log('Core link not visible, skipping')
|
|
410
|
+
test.skip()
|
|
411
|
+
return
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// inject tracking for Frame (inner content) animations
|
|
415
|
+
await page.evaluate(() => {
|
|
416
|
+
;(window as any).__framePositions = []
|
|
417
|
+
const track = () => {
|
|
418
|
+
const frames = document.querySelectorAll('.header-popover-frame')
|
|
419
|
+
frames.forEach((frame, idx) => {
|
|
420
|
+
const el = frame as HTMLElement
|
|
421
|
+
const transform = getComputedStyle(el).transform
|
|
422
|
+
const opacity = getComputedStyle(el).opacity
|
|
423
|
+
if (transform && transform !== 'none') {
|
|
424
|
+
const match = transform.match(
|
|
425
|
+
/matrix\([^,]+,\s*[^,]+,\s*[^,]+,\s*[^,]+,\s*([^,]+),\s*([^)]+)\)/
|
|
426
|
+
)
|
|
427
|
+
if (match) {
|
|
428
|
+
;(window as any).__framePositions.push({
|
|
429
|
+
idx,
|
|
430
|
+
x: parseFloat(match[1]),
|
|
431
|
+
y: parseFloat(match[2]),
|
|
432
|
+
opacity: parseFloat(opacity),
|
|
433
|
+
time: Date.now(),
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
requestAnimationFrame(track)
|
|
439
|
+
}
|
|
440
|
+
requestAnimationFrame(track)
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
// hover Core
|
|
444
|
+
await coreLink.hover()
|
|
445
|
+
await page.waitForTimeout(400)
|
|
446
|
+
|
|
447
|
+
// rapidly switch to menu button
|
|
448
|
+
await menuButton.hover()
|
|
449
|
+
await page.waitForTimeout(200)
|
|
450
|
+
|
|
451
|
+
// back to Core
|
|
452
|
+
await coreLink.hover()
|
|
453
|
+
await page.waitForTimeout(200)
|
|
454
|
+
|
|
455
|
+
// to menu
|
|
456
|
+
await menuButton.hover()
|
|
457
|
+
await page.waitForTimeout(200)
|
|
458
|
+
|
|
459
|
+
// to Core
|
|
460
|
+
await coreLink.hover()
|
|
461
|
+
await page.waitForTimeout(400)
|
|
462
|
+
|
|
463
|
+
const positions = await page.evaluate(() => (window as any).__framePositions || [])
|
|
464
|
+
console.log(`Collected ${positions.length} frame positions`)
|
|
465
|
+
|
|
466
|
+
// analyze for stuck animations (opacity stuck at 0 or x stuck at wrong value)
|
|
467
|
+
let stuckAtExit = 0
|
|
468
|
+
for (let i = 0; i < positions.length; i++) {
|
|
469
|
+
const pos = positions[i]
|
|
470
|
+
// check for opacity stuck near 0 when it should be 1
|
|
471
|
+
if (pos.opacity < 0.1 && Math.abs(pos.x) > 30) {
|
|
472
|
+
stuckAtExit++
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
console.log(`Frames stuck at exit state: ${stuckAtExit}`)
|
|
477
|
+
// some exit states during transition are fine, but not excessive
|
|
478
|
+
expect(stuckAtExit).toBeLessThan(50)
|
|
479
|
+
})
|
|
480
|
+
})
|
|
481
|
+
})
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { expect, test, type Page } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests for height media query overriding transform props (scale)
|
|
6
|
+
* Uses v5 config where height-lg breakpoint: minHeight 1024px
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
async function getScale(page: Page, testId: string): Promise<number> {
|
|
10
|
+
return page.evaluate((id) => {
|
|
11
|
+
const el = document.querySelector(`[data-testid="${id}"]`)
|
|
12
|
+
if (!el) return -1
|
|
13
|
+
const transform = getComputedStyle(el).transform
|
|
14
|
+
if (transform === 'none') return 1
|
|
15
|
+
const match = transform.match(/matrix\(([^,]+),/)
|
|
16
|
+
return match ? Number.parseFloat(match[1]) : 1
|
|
17
|
+
}, testId)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function getBgColor(page: Page, testId: string): Promise<string> {
|
|
21
|
+
return page.evaluate((id) => {
|
|
22
|
+
const el = document.querySelector(`[data-testid="${id}"]`)
|
|
23
|
+
return el ? getComputedStyle(el).backgroundColor : ''
|
|
24
|
+
}, testId)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test.describe('Height Media Query Override', () => {
|
|
28
|
+
test.beforeEach(async ({ page }) => {
|
|
29
|
+
await setupPage(page, {
|
|
30
|
+
name: 'HeightMediaQueryOverrideCase',
|
|
31
|
+
type: 'useCase',
|
|
32
|
+
searchParams: { v5config: 'true' },
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('$height-sm should override base scale=1 when height >= 640', async ({ page }) => {
|
|
37
|
+
await page.setViewportSize({ width: 1200, height: 700 })
|
|
38
|
+
await page.waitForTimeout(300)
|
|
39
|
+
|
|
40
|
+
const scale = await getScale(page, 'test-height-scale')
|
|
41
|
+
// should be 2 from $height-sm, not 1 from base
|
|
42
|
+
expect(scale, 'scale should be 2 at $height-sm, not 1 from base').toBeCloseTo(2, 1)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('$height-lg works when NO base scale is set', async ({ page }) => {
|
|
46
|
+
await page.setViewportSize({ width: 1200, height: 1100 })
|
|
47
|
+
await page.waitForTimeout(300)
|
|
48
|
+
|
|
49
|
+
const scale = await getScale(page, 'test-height-scale-no-base')
|
|
50
|
+
expect(scale, 'scale should be 2 at $height-lg').toBeCloseTo(2, 1)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('base scale=1 should apply when height < 640 (below $height-sm)', async ({
|
|
54
|
+
page,
|
|
55
|
+
}) => {
|
|
56
|
+
await page.setViewportSize({ width: 1200, height: 500 })
|
|
57
|
+
await page.waitForTimeout(300)
|
|
58
|
+
|
|
59
|
+
const scale = await getScale(page, 'test-height-scale')
|
|
60
|
+
expect(scale, 'scale should be 1 at base (below $height-sm)').toBeCloseTo(1, 1)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('$sm width query works for comparison', async ({ page }) => {
|
|
64
|
+
await page.setViewportSize({ width: 800, height: 700 })
|
|
65
|
+
await page.waitForTimeout(300)
|
|
66
|
+
|
|
67
|
+
const scale = await getScale(page, 'test-width-scale')
|
|
68
|
+
expect(scale, 'scale should be 1.5 at $sm').toBeCloseTo(1.5, 1)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('styled component with scale=1 in definition + $height-sm override', async ({
|
|
72
|
+
page,
|
|
73
|
+
}) => {
|
|
74
|
+
await page.setViewportSize({ width: 1200, height: 700 })
|
|
75
|
+
await page.waitForTimeout(300)
|
|
76
|
+
|
|
77
|
+
const scale = await getScale(page, 'test-styled-scale')
|
|
78
|
+
// styled component has scale=1 in definition, $height-sm should override to 2
|
|
79
|
+
expect(scale, 'styled component scale should be 2 at $height-sm').toBeCloseTo(2, 1)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('styled component with $height-sm in definition + runtime $height-sm override', async ({
|
|
83
|
+
page,
|
|
84
|
+
}) => {
|
|
85
|
+
await page.setViewportSize({ width: 1200, height: 700 })
|
|
86
|
+
await page.waitForTimeout(300)
|
|
87
|
+
|
|
88
|
+
const scale = await getScale(page, 'test-styled-media-override')
|
|
89
|
+
// styled has $height-sm: scale=0.5 in definition
|
|
90
|
+
// runtime $height-sm: scale=2 should override
|
|
91
|
+
expect(
|
|
92
|
+
scale,
|
|
93
|
+
'runtime $height-sm should override styled definition $height-sm'
|
|
94
|
+
).toBeCloseTo(2, 1)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('ContainerLarge with WIDTH queries in definition + runtime $height-sm scale', async ({
|
|
98
|
+
page,
|
|
99
|
+
}) => {
|
|
100
|
+
// exact scenario from chat app: ContainerLarge has $md/$lg width queries
|
|
101
|
+
// used with scale=1 and $height-sm={{ scale: 2 }}
|
|
102
|
+
await page.setViewportSize({ width: 1200, height: 700 })
|
|
103
|
+
await page.waitForTimeout(300)
|
|
104
|
+
|
|
105
|
+
const scale = await getScale(page, 'test-container-large')
|
|
106
|
+
// should be 2 from $height-sm, not 1 from base
|
|
107
|
+
expect(
|
|
108
|
+
scale,
|
|
109
|
+
'ContainerLarge with width queries should still allow $height-sm to override scale'
|
|
110
|
+
).toBeCloseTo(2, 1)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -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: 'InputAutoFocusAfterMenuCase', type: 'useCase' })
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
test('standalone Input with autoFocus should be focused on mount', async ({ page }) => {
|
|
9
|
+
const input = page.locator('[data-testid="autofocus-input"]')
|
|
10
|
+
await expect(input).toBeVisible()
|
|
11
|
+
await expect(input).toBeFocused()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test('Input with autoFocus should receive focus after menu close', async ({ page }) => {
|
|
15
|
+
const trigger = page.locator('[data-testid="menu-trigger"]')
|
|
16
|
+
await trigger.click()
|
|
17
|
+
|
|
18
|
+
const menuContent = page.locator('[data-testid="menu-content"]')
|
|
19
|
+
await expect(menuContent).toBeVisible()
|
|
20
|
+
|
|
21
|
+
const menuItem = page.locator('[data-testid="menu-item-show-input"]')
|
|
22
|
+
await menuItem.click()
|
|
23
|
+
|
|
24
|
+
const afterMenuInput = page.locator('[data-testid="after-menu-input"]')
|
|
25
|
+
await expect(afterMenuInput).toBeVisible()
|
|
26
|
+
|
|
27
|
+
await page.waitForTimeout(300)
|
|
28
|
+
await expect(afterMenuInput).toBeFocused()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test('onCloseAutoFocus preventDefault should prevent default focus restore and allow custom focus', async ({
|
|
32
|
+
page,
|
|
33
|
+
}) => {
|
|
34
|
+
const trigger = page.locator('[data-testid="prevent-default-trigger"]')
|
|
35
|
+
await trigger.click()
|
|
36
|
+
|
|
37
|
+
const menuContent = page.locator('[data-testid="prevent-default-menu-content"]')
|
|
38
|
+
await expect(menuContent).toBeVisible()
|
|
39
|
+
|
|
40
|
+
const menuItem = page.locator('[data-testid="prevent-default-menu-item"]')
|
|
41
|
+
await menuItem.click()
|
|
42
|
+
|
|
43
|
+
// menu should close
|
|
44
|
+
await expect(menuContent).not.toBeVisible()
|
|
45
|
+
|
|
46
|
+
await page.waitForTimeout(300)
|
|
47
|
+
|
|
48
|
+
// custom focus target should be focused (not the trigger)
|
|
49
|
+
const customTarget = page.locator('[data-testid="custom-focus-target"]')
|
|
50
|
+
await expect(customTarget).toBeFocused()
|
|
51
|
+
|
|
52
|
+
// trigger should NOT be focused
|
|
53
|
+
const triggerFocused = await trigger.evaluate((el) => el === document.activeElement)
|
|
54
|
+
expect(triggerFocused).toBe(false)
|
|
55
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.beforeEach(async ({ page }) => {
|
|
5
|
+
await setupPage(page, { name: 'InputAutoFocusStyledCase', type: 'useCase' })
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
test('plain Input with autoFocus gets focused on mount', async ({ page }) => {
|
|
9
|
+
await page.locator('[data-testid="show-plain"]').click()
|
|
10
|
+
const input = page.locator('[data-testid="plain-autofocus"]')
|
|
11
|
+
await expect(input).toBeVisible()
|
|
12
|
+
await page.waitForTimeout(200)
|
|
13
|
+
await expect(input).toBeFocused()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
test('styled(Input) with autoFocus gets focused on mount', async ({ page }) => {
|
|
17
|
+
await page.locator('[data-testid="show-styled"]').click()
|
|
18
|
+
const input = page.locator('[data-testid="styled-autofocus"]')
|
|
19
|
+
await expect(input).toBeVisible()
|
|
20
|
+
await page.waitForTimeout(200)
|
|
21
|
+
await expect(input).toBeFocused()
|
|
22
|
+
})
|