@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,242 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.describe('Popover Focus Scope', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'PopoverFocusScopeCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test('traps focus within popover when trapFocus is true', async ({ page }) => {
|
|
10
|
+
await page.waitForLoadState('networkidle')
|
|
11
|
+
|
|
12
|
+
// Open the basic popover
|
|
13
|
+
const trigger = page.getByTestId('basic-popover-trigger')
|
|
14
|
+
await trigger.click()
|
|
15
|
+
|
|
16
|
+
// Wait for popover to be visible
|
|
17
|
+
const popoverContent = page.getByTestId('basic-popover-content')
|
|
18
|
+
await expect(popoverContent).toBeVisible({ timeout: 5000 })
|
|
19
|
+
|
|
20
|
+
// Wait for auto-focus
|
|
21
|
+
await page.waitForTimeout(300)
|
|
22
|
+
|
|
23
|
+
const nameInput = popoverContent.getByTestId('popover-name-input')
|
|
24
|
+
await expect(nameInput).toBeFocused()
|
|
25
|
+
|
|
26
|
+
// Tab through all focusable elements
|
|
27
|
+
await page.keyboard.press('Tab')
|
|
28
|
+
const emailInput = popoverContent.getByTestId('popover-email-input')
|
|
29
|
+
await expect(emailInput).toBeFocused()
|
|
30
|
+
|
|
31
|
+
await page.keyboard.press('Tab')
|
|
32
|
+
const notesTextarea = popoverContent.getByTestId('popover-notes-textarea')
|
|
33
|
+
await expect(notesTextarea).toBeFocused()
|
|
34
|
+
|
|
35
|
+
await page.keyboard.press('Tab')
|
|
36
|
+
const cancelButton = popoverContent.getByTestId('popover-cancel-button')
|
|
37
|
+
await expect(cancelButton).toBeFocused()
|
|
38
|
+
|
|
39
|
+
await page.keyboard.press('Tab')
|
|
40
|
+
const saveButton = popoverContent.getByTestId('popover-save-button')
|
|
41
|
+
await expect(saveButton).toBeFocused()
|
|
42
|
+
|
|
43
|
+
// Tab should wrap back to first input (loop)
|
|
44
|
+
await page.keyboard.press('Tab')
|
|
45
|
+
await expect(nameInput).toBeFocused()
|
|
46
|
+
|
|
47
|
+
// Shift+Tab should go backwards and loop
|
|
48
|
+
await page.keyboard.press('Shift+Tab')
|
|
49
|
+
await expect(saveButton).toBeFocused()
|
|
50
|
+
|
|
51
|
+
// Close popover
|
|
52
|
+
await cancelButton.click()
|
|
53
|
+
await expect(popoverContent).not.toBeVisible()
|
|
54
|
+
|
|
55
|
+
// Focus should return to trigger
|
|
56
|
+
await expect(trigger).toBeFocused()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('does not trap focus when trapFocus is false', async ({ page }) => {
|
|
60
|
+
await page.waitForLoadState('networkidle')
|
|
61
|
+
|
|
62
|
+
// Open the non-trap popover
|
|
63
|
+
const trigger = page.getByTestId('no-trap-popover-trigger')
|
|
64
|
+
await trigger.click()
|
|
65
|
+
|
|
66
|
+
// Wait for popover to be visible
|
|
67
|
+
const popoverContent = page.getByTestId('no-trap-popover-content')
|
|
68
|
+
await expect(popoverContent).toBeVisible({ timeout: 5000 })
|
|
69
|
+
|
|
70
|
+
// Focus on the input
|
|
71
|
+
const input = popoverContent.getByTestId('no-trap-input')
|
|
72
|
+
await input.click()
|
|
73
|
+
await page.waitForTimeout(100)
|
|
74
|
+
await expect(input).toBeFocused({ timeout: 5000 })
|
|
75
|
+
|
|
76
|
+
// Tab to the close button
|
|
77
|
+
await page.keyboard.press('Tab')
|
|
78
|
+
await page.waitForTimeout(100)
|
|
79
|
+
|
|
80
|
+
const closeButton = popoverContent.getByTestId('no-trap-close-button')
|
|
81
|
+
await expect(closeButton).toBeFocused()
|
|
82
|
+
|
|
83
|
+
// Tab again - when trapFocus is false, focus can leave the popover
|
|
84
|
+
// Focus might go to browser chrome or other page elements
|
|
85
|
+
await page.keyboard.press('Tab')
|
|
86
|
+
await page.waitForTimeout(200)
|
|
87
|
+
|
|
88
|
+
// When trapFocus is false, focus is NOT trapped in the popover
|
|
89
|
+
// We can't reliably predict where focus will go as it depends on page structure
|
|
90
|
+
// The important thing is that FocusScope is not preventing the Tab key
|
|
91
|
+
|
|
92
|
+
// Let's verify that trapFocus=false is working by checking that
|
|
93
|
+
// we can focus back on the trigger button (outside the popover)
|
|
94
|
+
await page.getByTestId('no-trap-popover-trigger').focus()
|
|
95
|
+
await expect(page.getByTestId('no-trap-popover-trigger')).toBeFocused()
|
|
96
|
+
|
|
97
|
+
// And we can still focus elements inside the popover
|
|
98
|
+
await input.focus()
|
|
99
|
+
await expect(input).toBeFocused()
|
|
100
|
+
|
|
101
|
+
// The key difference from trapFocus=true is that FocusScope is not
|
|
102
|
+
// handling the Tab key - it's the browser's default behavior.
|
|
103
|
+
// With trapFocus=false and loop=false, FocusScope doesn't interfere.
|
|
104
|
+
|
|
105
|
+
// Verify that the popover has the correct setup
|
|
106
|
+
const focusScopeInfo = await page.evaluate(() => {
|
|
107
|
+
const popover = document.querySelector('[data-testid="no-trap-popover-content"]')
|
|
108
|
+
const focusScope = popover?.closest('[data-focus-scope]') || popover?.parentElement
|
|
109
|
+
|
|
110
|
+
// Check if there are any event handlers that would indicate focus trapping
|
|
111
|
+
const hasKeydownHandler = !!(focusScope as any)?._keydownHandler
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
popoverFound: !!popover,
|
|
115
|
+
// If FocusScope was trapping, it would have special attributes or handlers
|
|
116
|
+
hasFocusScopeAttributes:
|
|
117
|
+
focusScope?.hasAttribute?.('data-focus-scope-trapped') || false,
|
|
118
|
+
hasKeydownHandler,
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// FocusScope should not be actively trapping focus
|
|
123
|
+
expect(focusScopeInfo.hasFocusScopeAttributes).toBe(false)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('handles nested popovers focus correctly', async ({ page }) => {
|
|
127
|
+
await page.waitForLoadState('networkidle')
|
|
128
|
+
|
|
129
|
+
// Open parent popover
|
|
130
|
+
const parentTrigger = page.getByTestId('parent-popover-trigger')
|
|
131
|
+
await parentTrigger.click()
|
|
132
|
+
|
|
133
|
+
const parentPopover = page.getByTestId('parent-popover-content')
|
|
134
|
+
await expect(parentPopover).toBeVisible({ timeout: 5000 })
|
|
135
|
+
|
|
136
|
+
await page.waitForTimeout(300)
|
|
137
|
+
const parentInput = parentPopover.getByTestId('parent-popover-input')
|
|
138
|
+
await expect(parentInput).toBeFocused()
|
|
139
|
+
|
|
140
|
+
// Open nested popover
|
|
141
|
+
const nestedTrigger = parentPopover.getByTestId('nested-popover-trigger')
|
|
142
|
+
await nestedTrigger.click()
|
|
143
|
+
|
|
144
|
+
const nestedPopover = page.getByTestId('nested-popover-content')
|
|
145
|
+
await expect(nestedPopover).toBeVisible({ timeout: 5000 })
|
|
146
|
+
|
|
147
|
+
// Focus should be trapped in nested popover
|
|
148
|
+
await page.waitForTimeout(300)
|
|
149
|
+
const nestedInput = nestedPopover.getByTestId('nested-popover-input')
|
|
150
|
+
await expect(nestedInput).toBeFocused()
|
|
151
|
+
|
|
152
|
+
// Tab should stay within nested popover
|
|
153
|
+
await page.keyboard.press('Tab')
|
|
154
|
+
const nestedClose = nestedPopover.getByTestId('nested-popover-close')
|
|
155
|
+
await expect(nestedClose).toBeFocused()
|
|
156
|
+
|
|
157
|
+
await page.keyboard.press('Tab')
|
|
158
|
+
await expect(nestedInput).toBeFocused() // Should loop back
|
|
159
|
+
|
|
160
|
+
// Close nested popover
|
|
161
|
+
await nestedClose.click()
|
|
162
|
+
await expect(nestedPopover).not.toBeVisible()
|
|
163
|
+
|
|
164
|
+
// Focus should return to nested trigger in parent popover
|
|
165
|
+
await expect(nestedTrigger).toBeFocused()
|
|
166
|
+
|
|
167
|
+
// Close parent popover
|
|
168
|
+
const parentClose = parentPopover.getByTestId('parent-popover-close')
|
|
169
|
+
await parentClose.click()
|
|
170
|
+
await expect(parentPopover).not.toBeVisible()
|
|
171
|
+
|
|
172
|
+
// Focus should return to parent trigger
|
|
173
|
+
await expect(parentTrigger).toBeFocused()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
test('auto-focuses first element on mount', async ({ page }) => {
|
|
177
|
+
await page.waitForLoadState('networkidle')
|
|
178
|
+
|
|
179
|
+
// Check if popover content is already visible (might be open by default)
|
|
180
|
+
let popoverContent = page.locator('[data-testid="basic-popover-content"]')
|
|
181
|
+
const isAlreadyVisible = await popoverContent.isVisible()
|
|
182
|
+
|
|
183
|
+
if (!isAlreadyVisible) {
|
|
184
|
+
// If not visible, click the trigger
|
|
185
|
+
const trigger = page.getByTestId('basic-popover-trigger')
|
|
186
|
+
await expect(trigger).toBeVisible({ timeout: 5000 })
|
|
187
|
+
await trigger.click()
|
|
188
|
+
|
|
189
|
+
// Wait a bit for popover animation
|
|
190
|
+
await page.waitForTimeout(500)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
await expect(popoverContent).toBeVisible({ timeout: 5000 })
|
|
194
|
+
|
|
195
|
+
// Wait for auto-focus
|
|
196
|
+
await page.waitForTimeout(300)
|
|
197
|
+
|
|
198
|
+
// First input should be focused
|
|
199
|
+
const nameInput = popoverContent.getByTestId('popover-name-input')
|
|
200
|
+
await expect(nameInput).toBeFocused()
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('closes on escape key and returns focus', async ({ page }) => {
|
|
204
|
+
await page.waitForLoadState('networkidle')
|
|
205
|
+
|
|
206
|
+
const trigger = page.getByTestId('basic-popover-trigger')
|
|
207
|
+
await trigger.click()
|
|
208
|
+
|
|
209
|
+
const popoverContent = page.getByTestId('basic-popover-content')
|
|
210
|
+
await expect(popoverContent).toBeVisible({ timeout: 5000 })
|
|
211
|
+
|
|
212
|
+
// Focus needs to be inside the popover for escape to work
|
|
213
|
+
const nameInput = popoverContent.getByTestId('popover-name-input')
|
|
214
|
+
await expect(nameInput).toBeFocused({ timeout: 3000 })
|
|
215
|
+
|
|
216
|
+
// Press escape to close
|
|
217
|
+
await page.keyboard.press('Escape')
|
|
218
|
+
|
|
219
|
+
// Wait for popover to close (animation may take time)
|
|
220
|
+
await page.waitForTimeout(600)
|
|
221
|
+
await expect(popoverContent).not.toBeVisible({ timeout: 5000 })
|
|
222
|
+
|
|
223
|
+
// Focus should return to trigger
|
|
224
|
+
await expect(trigger).toBeFocused({ timeout: 5000 })
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('closes when clicking outside', async ({ page }) => {
|
|
228
|
+
await page.waitForLoadState('networkidle')
|
|
229
|
+
|
|
230
|
+
const trigger = page.getByTestId('basic-popover-trigger')
|
|
231
|
+
await trigger.click()
|
|
232
|
+
|
|
233
|
+
const popoverContent = page.getByTestId('basic-popover-content')
|
|
234
|
+
await expect(popoverContent).toBeVisible({ timeout: 5000 })
|
|
235
|
+
|
|
236
|
+
// Click outside to close
|
|
237
|
+
await page.click('body', { position: { x: 10, y: 10 } })
|
|
238
|
+
|
|
239
|
+
// Wait for popover to close (Playwright auto-retries until timeout)
|
|
240
|
+
await expect(popoverContent).not.toBeVisible({ timeout: 5000 })
|
|
241
|
+
})
|
|
242
|
+
})
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { getBoundingRect, setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
// helper: get opacity of element by id
|
|
5
|
+
async function getOpacity(page: any, id: string) {
|
|
6
|
+
return page.evaluate(
|
|
7
|
+
(sel: string) => parseFloat(getComputedStyle(document.getElementById(sel)!).opacity),
|
|
8
|
+
id
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Bug 1: delay should apply to both enter AND exit
|
|
13
|
+
test.describe('Popover hoverable delay', () => {
|
|
14
|
+
test.beforeEach(async ({ page }) => {
|
|
15
|
+
await setupPage(page, { name: 'PopoverHoverableDelayCase', type: 'useCase' })
|
|
16
|
+
await page.waitForLoadState('networkidle')
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('delay applies to enter: popover should not open before delay elapses', async ({
|
|
20
|
+
page,
|
|
21
|
+
}) => {
|
|
22
|
+
const trigger = page.locator('#delay-trigger')
|
|
23
|
+
const content = page.locator('#delay-content')
|
|
24
|
+
|
|
25
|
+
await expect(content).not.toBeVisible()
|
|
26
|
+
|
|
27
|
+
// hover over trigger
|
|
28
|
+
await trigger.hover()
|
|
29
|
+
|
|
30
|
+
// check immediately after hover (should NOT be open yet - delay is 400ms)
|
|
31
|
+
await expect(content).not.toBeVisible()
|
|
32
|
+
|
|
33
|
+
// wait less than delay - still should not open
|
|
34
|
+
await page.waitForTimeout(200)
|
|
35
|
+
await expect(content).not.toBeVisible()
|
|
36
|
+
|
|
37
|
+
// wait full delay time - should now be open
|
|
38
|
+
await page.waitForTimeout(300)
|
|
39
|
+
await expect(content).toBeVisible({ timeout: 3000 })
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('delay applies to exit: popover should not close before delay elapses', async ({
|
|
43
|
+
page,
|
|
44
|
+
}) => {
|
|
45
|
+
const trigger = page.locator('#delay-trigger')
|
|
46
|
+
const content = page.locator('#delay-content')
|
|
47
|
+
|
|
48
|
+
// hover to open (wait for delay)
|
|
49
|
+
await trigger.hover()
|
|
50
|
+
await page.waitForTimeout(800)
|
|
51
|
+
await expect(content).toBeVisible({ timeout: 3000 })
|
|
52
|
+
|
|
53
|
+
// move mouse far away (outside safe zone)
|
|
54
|
+
await page.mouse.move(10, 10)
|
|
55
|
+
|
|
56
|
+
// should still be visible right after (delay is 400ms)
|
|
57
|
+
await expect(content).toBeVisible()
|
|
58
|
+
|
|
59
|
+
// wait less than delay - still should be visible
|
|
60
|
+
await page.waitForTimeout(200)
|
|
61
|
+
await expect(content).toBeVisible()
|
|
62
|
+
|
|
63
|
+
// wait for delay to elapse + animation - should now be closing
|
|
64
|
+
await page.waitForTimeout(600)
|
|
65
|
+
await expect(content).not.toBeVisible({ timeout: 3000 })
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
// Bug 2: restMs should only apply to enter (not exit)
|
|
70
|
+
test.describe('Popover hoverable restMs', () => {
|
|
71
|
+
test.beforeEach(async ({ page }) => {
|
|
72
|
+
await setupPage(page, { name: 'PopoverHoverableRestMsCase', type: 'useCase' })
|
|
73
|
+
await page.waitForLoadState('networkidle')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('restMs delays enter: popover should open after mouse rests', async ({ page }) => {
|
|
77
|
+
const trigger = page.locator('#restms-trigger')
|
|
78
|
+
const content = page.locator('#restms-content')
|
|
79
|
+
|
|
80
|
+
await expect(content).not.toBeVisible()
|
|
81
|
+
|
|
82
|
+
// hover over trigger
|
|
83
|
+
await trigger.hover()
|
|
84
|
+
|
|
85
|
+
// should not open immediately
|
|
86
|
+
await expect(content).not.toBeVisible()
|
|
87
|
+
|
|
88
|
+
// wait for restMs to elapse
|
|
89
|
+
await page.waitForTimeout(500)
|
|
90
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('exit without restMs: popover should close quickly after mouse leaves', async ({
|
|
94
|
+
page,
|
|
95
|
+
}) => {
|
|
96
|
+
const trigger = page.locator('#restms-trigger')
|
|
97
|
+
const content = page.locator('#restms-content')
|
|
98
|
+
|
|
99
|
+
// open it
|
|
100
|
+
await trigger.hover()
|
|
101
|
+
await page.waitForTimeout(500)
|
|
102
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
103
|
+
|
|
104
|
+
// move mouse away - with restMs but no delay, exit should be handled by safePolygon (quick)
|
|
105
|
+
await page.mouse.move(10, 10)
|
|
106
|
+
|
|
107
|
+
// exit should happen quickly (no restMs delay on exit, only animation duration ~200ms)
|
|
108
|
+
await expect(content).not.toBeVisible({ timeout: 1000 })
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Bug 3: exit animation should play when hoverable closes popover
|
|
113
|
+
test.describe('Popover hoverable exit animation', () => {
|
|
114
|
+
test.beforeEach(async ({ page }) => {
|
|
115
|
+
await setupPage(page, { name: 'PopoverHoverableExitAnimCase', type: 'useCase' })
|
|
116
|
+
await page.waitForLoadState('networkidle')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('exit animation plays: content should still be visible briefly after mouse leaves', async ({
|
|
120
|
+
page,
|
|
121
|
+
}) => {
|
|
122
|
+
const trigger = page.locator('#exitanim-trigger')
|
|
123
|
+
const content = page.locator('#exitanim-content')
|
|
124
|
+
|
|
125
|
+
// hover to open
|
|
126
|
+
await trigger.hover()
|
|
127
|
+
await expect(content).toBeVisible({ timeout: 3000 })
|
|
128
|
+
|
|
129
|
+
// wait for enter animation to complete (500ms transition + buffer)
|
|
130
|
+
await page.waitForTimeout(700)
|
|
131
|
+
|
|
132
|
+
// confirm fully visible now
|
|
133
|
+
const opacityBefore = await content.evaluate((el) =>
|
|
134
|
+
parseFloat(getComputedStyle(el).opacity)
|
|
135
|
+
)
|
|
136
|
+
expect(opacityBefore).toBeGreaterThan(0.9)
|
|
137
|
+
|
|
138
|
+
// move mouse far away (outside safe zone) to trigger close
|
|
139
|
+
await page.mouse.move(10, 10)
|
|
140
|
+
|
|
141
|
+
// immediately after leaving - content should still be visible (animation in progress)
|
|
142
|
+
// the transition is 500ms so it should still be partially visible
|
|
143
|
+
await page.waitForTimeout(50)
|
|
144
|
+
|
|
145
|
+
const opacityDuring = await content.evaluate((el) =>
|
|
146
|
+
parseFloat(getComputedStyle(el).opacity)
|
|
147
|
+
)
|
|
148
|
+
// should be animating - opacity should be > 0 (not instantly gone to 0)
|
|
149
|
+
expect(opacityDuring).toBeGreaterThan(0)
|
|
150
|
+
|
|
151
|
+
// wait for animation to complete (500ms + buffer)
|
|
152
|
+
await page.waitForTimeout(600)
|
|
153
|
+
await expect(content).not.toBeVisible({ timeout: 2000 })
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Bug: safePolygon should allow hovering from trigger to content through the gap
|
|
158
|
+
test.describe('Popover hoverable safePolygon', () => {
|
|
159
|
+
test.beforeEach(async ({ page }) => {
|
|
160
|
+
await setupPage(page, { name: 'PopoverHoverableSafePolygonCase', type: 'useCase' })
|
|
161
|
+
await page.waitForLoadState('networkidle')
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
test('can hover from trigger to content through the gap', async ({ page }) => {
|
|
165
|
+
const trigger = page.locator('#safepoly-trigger')
|
|
166
|
+
const content = page.locator('#safepoly-content')
|
|
167
|
+
|
|
168
|
+
// hover trigger to open (restMs is 260ms)
|
|
169
|
+
await trigger.hover()
|
|
170
|
+
await page.waitForTimeout(350)
|
|
171
|
+
await expect(content).toBeVisible({ timeout: 200 })
|
|
172
|
+
// wait for enter animation to settle
|
|
173
|
+
await page.waitForTimeout(100)
|
|
174
|
+
|
|
175
|
+
// slowly move from trigger down to content (through the 80px offset gap)
|
|
176
|
+
const triggerBox = await trigger.boundingBox()
|
|
177
|
+
const contentBox = await content.boundingBox()
|
|
178
|
+
if (triggerBox && contentBox) {
|
|
179
|
+
const startY = triggerBox.y + triggerBox.height
|
|
180
|
+
const endY = contentBox.y + 10
|
|
181
|
+
const x = triggerBox.x + triggerBox.width / 2
|
|
182
|
+
for (let y = startY; y <= endY; y += 4) {
|
|
183
|
+
await page.mouse.move(x, y)
|
|
184
|
+
await page.waitForTimeout(10)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// wait well past any grace period or animation - if the popover closed
|
|
189
|
+
// during the gap crossing, it won't be visible after this
|
|
190
|
+
await page.waitForTimeout(500)
|
|
191
|
+
await expect(content).toBeVisible({ timeout: 1000 })
|
|
192
|
+
|
|
193
|
+
// verify content is fully opaque (not mid-exit-animation)
|
|
194
|
+
const opacity = await content.evaluate((el) =>
|
|
195
|
+
parseFloat(getComputedStyle(el).opacity)
|
|
196
|
+
)
|
|
197
|
+
expect(opacity).toBeGreaterThan(0.9)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('restMs applies on re-hover (not just first hover)', async ({ page }) => {
|
|
201
|
+
const trigger = page.locator('#safepoly-trigger')
|
|
202
|
+
const content = page.locator('#safepoly-content')
|
|
203
|
+
|
|
204
|
+
// first hover: should respect restMs (260ms)
|
|
205
|
+
await trigger.hover()
|
|
206
|
+
// should NOT be open before restMs elapses
|
|
207
|
+
await page.waitForTimeout(100)
|
|
208
|
+
await expect(content).not.toBeVisible()
|
|
209
|
+
// wait for restMs
|
|
210
|
+
await page.waitForTimeout(250)
|
|
211
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
212
|
+
|
|
213
|
+
// move far away to close
|
|
214
|
+
await page.mouse.move(10, 10)
|
|
215
|
+
await page.waitForTimeout(500)
|
|
216
|
+
await expect(content).not.toBeVisible({ timeout: 2000 })
|
|
217
|
+
|
|
218
|
+
// re-hover: restMs should still apply (not open instantly)
|
|
219
|
+
await trigger.hover()
|
|
220
|
+
// check after 100ms - should NOT be open yet (restMs is 260ms)
|
|
221
|
+
await page.waitForTimeout(100)
|
|
222
|
+
await expect(content).not.toBeVisible()
|
|
223
|
+
|
|
224
|
+
// wait for restMs to elapse
|
|
225
|
+
await page.waitForTimeout(250)
|
|
226
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// Bug: scoped multi-trigger hoverable - mimics WebsiteHeader.tsx pattern
|
|
231
|
+
// uses CSS driver since animatePosition needs a driver that supports classNames
|
|
232
|
+
test.describe('Popover hoverable scoped multi-trigger', () => {
|
|
233
|
+
test.beforeEach(async ({ page }) => {
|
|
234
|
+
await setupPage(page, {
|
|
235
|
+
name: 'PopoverHoverableScopedCase',
|
|
236
|
+
type: 'useCase',
|
|
237
|
+
searchParams: { animationDriver: 'css' },
|
|
238
|
+
})
|
|
239
|
+
await page.waitForLoadState('networkidle')
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
test('scoped: delay applies to enter - should not open immediately', async ({
|
|
243
|
+
page,
|
|
244
|
+
}) => {
|
|
245
|
+
const trigger = page.locator('#nav-trigger-about')
|
|
246
|
+
const content = page.locator('#nav-content')
|
|
247
|
+
|
|
248
|
+
await trigger.hover()
|
|
249
|
+
|
|
250
|
+
// should NOT be open right away (300ms delay)
|
|
251
|
+
await expect(content).not.toBeVisible()
|
|
252
|
+
|
|
253
|
+
// wait for delay
|
|
254
|
+
await page.waitForTimeout(450)
|
|
255
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
test('scoped: exit animation plays when hovering away', async ({ page }) => {
|
|
259
|
+
const trigger = page.locator('#nav-trigger-about')
|
|
260
|
+
const content = page.locator('#nav-content')
|
|
261
|
+
|
|
262
|
+
// open it
|
|
263
|
+
await trigger.hover()
|
|
264
|
+
await page.waitForTimeout(450)
|
|
265
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
266
|
+
|
|
267
|
+
// wait for enter animation to finish (500ms transition)
|
|
268
|
+
await page.waitForTimeout(700)
|
|
269
|
+
|
|
270
|
+
// verify content is correctly positioned below trigger (not stuck at y=0)
|
|
271
|
+
const triggerRect = await getBoundingRect(page, '#nav-trigger-about')
|
|
272
|
+
const contentRect = await getBoundingRect(page, '#nav-content')
|
|
273
|
+
|
|
274
|
+
expect(contentRect!.y).toBeGreaterThan(triggerRect!.y)
|
|
275
|
+
|
|
276
|
+
const opacityBefore = await getOpacity(page, 'nav-content')
|
|
277
|
+
expect(opacityBefore).toBeGreaterThan(0.9)
|
|
278
|
+
|
|
279
|
+
// move far away to trigger close
|
|
280
|
+
await page.mouse.move(10, 10)
|
|
281
|
+
|
|
282
|
+
// check opacity shortly after - should still be > 0 (animating out with 500ms transition)
|
|
283
|
+
await page.waitForTimeout(50)
|
|
284
|
+
const opacityDuring = await getOpacity(page, 'nav-content')
|
|
285
|
+
expect(opacityDuring).toBeGreaterThan(0)
|
|
286
|
+
|
|
287
|
+
// wait for animation to complete (500ms + buffer)
|
|
288
|
+
await page.waitForTimeout(600)
|
|
289
|
+
await expect(content).not.toBeVisible({ timeout: 2000 })
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
test('scoped: switching between triggers keeps popover open', async ({ page }) => {
|
|
293
|
+
const aboutTrigger = page.locator('#nav-trigger-about')
|
|
294
|
+
const blogTrigger = page.locator('#nav-trigger-blog')
|
|
295
|
+
const content = page.locator('#nav-content')
|
|
296
|
+
|
|
297
|
+
// open about
|
|
298
|
+
await aboutTrigger.hover()
|
|
299
|
+
await page.waitForTimeout(450)
|
|
300
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
301
|
+
|
|
302
|
+
// switch to blog trigger - popover should stay open
|
|
303
|
+
await blogTrigger.hover()
|
|
304
|
+
await page.waitForTimeout(100)
|
|
305
|
+
await expect(content).toBeVisible()
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
test('scoped: content → different trigger repositions popover', async ({ page }) => {
|
|
309
|
+
const aboutTrigger = page.locator('#nav-trigger-about')
|
|
310
|
+
const contactTrigger = page.locator('#nav-trigger-contact')
|
|
311
|
+
const content = page.locator('#nav-content')
|
|
312
|
+
|
|
313
|
+
// open at about
|
|
314
|
+
await aboutTrigger.hover()
|
|
315
|
+
await page.waitForTimeout(450)
|
|
316
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
317
|
+
await page.waitForTimeout(300)
|
|
318
|
+
|
|
319
|
+
const contentRectAtAbout = await getBoundingRect(page, '#nav-content')
|
|
320
|
+
|
|
321
|
+
// move into the popover content
|
|
322
|
+
await content.hover()
|
|
323
|
+
await page.waitForTimeout(100)
|
|
324
|
+
await expect(content).toBeVisible()
|
|
325
|
+
|
|
326
|
+
// move from content to "contact" trigger (rightmost)
|
|
327
|
+
await contactTrigger.hover()
|
|
328
|
+
await page.waitForTimeout(600)
|
|
329
|
+
|
|
330
|
+
// popover should still be visible and repositioned
|
|
331
|
+
await expect(content).toBeVisible()
|
|
332
|
+
const contentRectAtContact = await getBoundingRect(page, '#nav-content')
|
|
333
|
+
|
|
334
|
+
// contact is to the right of about, so content x should shift right
|
|
335
|
+
expect(contentRectAtContact!.x).toBeGreaterThan(contentRectAtAbout!.x + 20)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
test('scoped: content → gap → different trigger repositions popover', async ({
|
|
339
|
+
page,
|
|
340
|
+
}) => {
|
|
341
|
+
const aboutTrigger = page.locator('#nav-trigger-about')
|
|
342
|
+
const contactTrigger = page.locator('#nav-trigger-contact')
|
|
343
|
+
const content = page.locator('#nav-content')
|
|
344
|
+
|
|
345
|
+
// open at about
|
|
346
|
+
await aboutTrigger.hover()
|
|
347
|
+
await page.waitForTimeout(450)
|
|
348
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
349
|
+
await page.waitForTimeout(300)
|
|
350
|
+
|
|
351
|
+
const contentRectAtAbout = await getBoundingRect(page, '#nav-content')
|
|
352
|
+
|
|
353
|
+
// move into the popover content
|
|
354
|
+
await content.hover()
|
|
355
|
+
await page.waitForTimeout(100)
|
|
356
|
+
await expect(content).toBeVisible()
|
|
357
|
+
|
|
358
|
+
// move mouse to empty space BELOW the content (not to a trigger)
|
|
359
|
+
// this simulates the recording where mouse exits content into the page gap
|
|
360
|
+
const contentBox = await content.boundingBox()
|
|
361
|
+
if (contentBox) {
|
|
362
|
+
await page.mouse.move(
|
|
363
|
+
contentBox.x + contentBox.width / 2,
|
|
364
|
+
contentBox.y + contentBox.height + 50
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// wait a bit for the popover to close (safePolygon should eventually close it)
|
|
369
|
+
await page.waitForTimeout(300)
|
|
370
|
+
|
|
371
|
+
// now move to a different trigger (contact)
|
|
372
|
+
await contactTrigger.hover()
|
|
373
|
+
// wait for restMs (300ms) + buffer
|
|
374
|
+
await page.waitForTimeout(500)
|
|
375
|
+
|
|
376
|
+
// popover should be visible and repositioned at the new trigger
|
|
377
|
+
await expect(content).toBeVisible({ timeout: 2000 })
|
|
378
|
+
const contentRectAtContact = await getBoundingRect(page, '#nav-content')
|
|
379
|
+
|
|
380
|
+
// contact is to the right of about, so content x should shift right
|
|
381
|
+
expect(contentRectAtContact!.x).toBeGreaterThan(contentRectAtAbout!.x + 20)
|
|
382
|
+
})
|
|
383
|
+
})
|