@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,234 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
import { setupPage } from './test-utils'
|
|
4
|
+
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'PopoverCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
// Test simple popover functionality
|
|
10
|
+
test('simple popover opens and closes', async ({ page }) => {
|
|
11
|
+
// Wait for page to load
|
|
12
|
+
await page.waitForLoadState('networkidle')
|
|
13
|
+
|
|
14
|
+
const trigger = page.locator('#simple-popover-trigger')
|
|
15
|
+
const content = page.locator('#simple-popover-content')
|
|
16
|
+
const closeButton = page.locator('#simple-popover-close')
|
|
17
|
+
|
|
18
|
+
// Check initial state
|
|
19
|
+
await expect(trigger).toBeVisible()
|
|
20
|
+
await expect(content).not.toBeVisible()
|
|
21
|
+
|
|
22
|
+
// Click trigger to open popover
|
|
23
|
+
await trigger.click()
|
|
24
|
+
|
|
25
|
+
// Wait for content to be visible
|
|
26
|
+
await expect(content).toBeVisible({ timeout: 5000 })
|
|
27
|
+
|
|
28
|
+
// Click close button
|
|
29
|
+
await closeButton.click()
|
|
30
|
+
|
|
31
|
+
// Verify popover is closed
|
|
32
|
+
await expect(content).not.toBeVisible()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Test basic popover with icon trigger
|
|
36
|
+
test('basic popover with icon trigger', async ({ page }) => {
|
|
37
|
+
// Wait for page to load
|
|
38
|
+
await page.waitForLoadState('networkidle')
|
|
39
|
+
|
|
40
|
+
const trigger = page.locator('#popover-bottom-trigger')
|
|
41
|
+
const content = page.locator('#popover-bottom-content')
|
|
42
|
+
const input = page.locator('#popover-bottom-input')
|
|
43
|
+
const closeButton = page.locator('#popover-bottom-close')
|
|
44
|
+
|
|
45
|
+
// Check initial state
|
|
46
|
+
await expect(trigger).toBeVisible()
|
|
47
|
+
await expect(content).not.toBeVisible()
|
|
48
|
+
|
|
49
|
+
// Click trigger to open popover
|
|
50
|
+
await trigger.click()
|
|
51
|
+
|
|
52
|
+
// Wait for enter animation to start
|
|
53
|
+
await page.waitForTimeout(100)
|
|
54
|
+
|
|
55
|
+
// Wait for content to be visible
|
|
56
|
+
await expect(content).toBeVisible({ timeout: 5000 })
|
|
57
|
+
|
|
58
|
+
// Click close button
|
|
59
|
+
await closeButton.click()
|
|
60
|
+
|
|
61
|
+
// Wait for exit animation
|
|
62
|
+
await page.waitForTimeout(500)
|
|
63
|
+
|
|
64
|
+
// Verify popover is closed
|
|
65
|
+
await expect(content).not.toBeVisible()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// Test popover placement variations
|
|
69
|
+
test('popover placement variations', async ({ page }) => {
|
|
70
|
+
// Wait for page to load
|
|
71
|
+
await page.waitForLoadState('networkidle')
|
|
72
|
+
|
|
73
|
+
const placements = ['left', 'top', 'right']
|
|
74
|
+
|
|
75
|
+
for (const placement of placements) {
|
|
76
|
+
const trigger = page.locator(`#popover-${placement}-trigger`)
|
|
77
|
+
const content = page.locator(`#popover-${placement}-content`)
|
|
78
|
+
|
|
79
|
+
// Check initial state
|
|
80
|
+
await expect(trigger).toBeVisible()
|
|
81
|
+
await expect(content).not.toBeVisible()
|
|
82
|
+
|
|
83
|
+
// Click trigger to open popover
|
|
84
|
+
await trigger.click()
|
|
85
|
+
|
|
86
|
+
// Wait for enter animation to start
|
|
87
|
+
await page.waitForTimeout(100)
|
|
88
|
+
|
|
89
|
+
// Wait for content to be visible
|
|
90
|
+
await expect(content).toBeVisible({ timeout: 5000 })
|
|
91
|
+
|
|
92
|
+
// Close by clicking outside (ESC key)
|
|
93
|
+
await page.keyboard.press('Escape')
|
|
94
|
+
|
|
95
|
+
// Wait for exit animation to complete
|
|
96
|
+
await page.waitForTimeout(500)
|
|
97
|
+
|
|
98
|
+
// Verify popover is closed
|
|
99
|
+
await expect(content).not.toBeVisible()
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
// Test popover with keyboard navigation
|
|
104
|
+
test('popover keyboard navigation', async ({ page }) => {
|
|
105
|
+
// Wait for page to load
|
|
106
|
+
await page.waitForLoadState('networkidle')
|
|
107
|
+
|
|
108
|
+
const trigger = page.locator('#simple-popover-trigger')
|
|
109
|
+
const content = page.locator('#simple-popover-content')
|
|
110
|
+
|
|
111
|
+
// Focus trigger and open with Enter key
|
|
112
|
+
await trigger.focus()
|
|
113
|
+
await page.keyboard.press('Enter')
|
|
114
|
+
|
|
115
|
+
// Wait for content to be visible
|
|
116
|
+
await expect(content).toBeVisible({ timeout: 5000 })
|
|
117
|
+
|
|
118
|
+
// Close with Escape key
|
|
119
|
+
await page.keyboard.press('Escape')
|
|
120
|
+
|
|
121
|
+
// Verify popover is closed
|
|
122
|
+
await expect(content).not.toBeVisible()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Test popover accessibility
|
|
126
|
+
test('popover accessibility attributes', async ({ page }) => {
|
|
127
|
+
// Wait for page to load
|
|
128
|
+
await page.waitForLoadState('networkidle')
|
|
129
|
+
|
|
130
|
+
const trigger = page.locator('#simple-popover-trigger')
|
|
131
|
+
const content = page.locator('#simple-popover-content')
|
|
132
|
+
|
|
133
|
+
// Check initial state
|
|
134
|
+
await expect(trigger).toHaveAttribute('aria-expanded', 'false')
|
|
135
|
+
|
|
136
|
+
// Open popover
|
|
137
|
+
await trigger.click()
|
|
138
|
+
await expect(content).toBeVisible({ timeout: 5000 })
|
|
139
|
+
|
|
140
|
+
// Check expanded state
|
|
141
|
+
await expect(trigger).toHaveAttribute('aria-expanded', 'true')
|
|
142
|
+
|
|
143
|
+
// Close popover
|
|
144
|
+
await page.keyboard.press('Escape')
|
|
145
|
+
await expect(content).not.toBeVisible()
|
|
146
|
+
|
|
147
|
+
// Check collapsed state
|
|
148
|
+
await expect(trigger).toHaveAttribute('aria-expanded', 'false')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// Test multiple popovers behavior
|
|
152
|
+
test('multiple popovers - opening one closes others', async ({ page }) => {
|
|
153
|
+
// Wait for page to load
|
|
154
|
+
await page.waitForLoadState('networkidle')
|
|
155
|
+
|
|
156
|
+
const leftTrigger = page.locator('#popover-left-trigger')
|
|
157
|
+
const leftContent = page.locator('#popover-left-content')
|
|
158
|
+
const rightTrigger = page.locator('#popover-right-trigger')
|
|
159
|
+
const rightContent = page.locator('#popover-right-content')
|
|
160
|
+
const topTrigger = page.locator('#popover-top-trigger')
|
|
161
|
+
const topContent = page.locator('#popover-top-content')
|
|
162
|
+
|
|
163
|
+
// Check initial state - all popovers should be closed
|
|
164
|
+
await expect(leftContent).not.toBeVisible()
|
|
165
|
+
await expect(rightContent).not.toBeVisible()
|
|
166
|
+
await expect(topContent).not.toBeVisible()
|
|
167
|
+
|
|
168
|
+
// Open first popover (left)
|
|
169
|
+
await leftTrigger.click()
|
|
170
|
+
await page.waitForTimeout(100) // Wait for enter animation
|
|
171
|
+
await expect(leftContent).toBeVisible({ timeout: 5000 })
|
|
172
|
+
await expect(rightContent).not.toBeVisible()
|
|
173
|
+
await expect(topContent).not.toBeVisible()
|
|
174
|
+
|
|
175
|
+
// Open second popover (right) - should close the first one
|
|
176
|
+
await rightTrigger.click()
|
|
177
|
+
await page.waitForTimeout(600) // Wait for previous exit + new enter animations
|
|
178
|
+
await expect(leftContent).not.toBeVisible()
|
|
179
|
+
await expect(rightContent).toBeVisible({ timeout: 5000 })
|
|
180
|
+
await expect(topContent).not.toBeVisible()
|
|
181
|
+
|
|
182
|
+
// Open third popover (top) - should close the second one
|
|
183
|
+
await topTrigger.click()
|
|
184
|
+
await page.waitForTimeout(600) // Wait for previous exit + new enter animations
|
|
185
|
+
await expect(leftContent).not.toBeVisible()
|
|
186
|
+
await expect(rightContent).not.toBeVisible()
|
|
187
|
+
await expect(topContent).toBeVisible({ timeout: 5000 })
|
|
188
|
+
|
|
189
|
+
// Close the top popover
|
|
190
|
+
await page.keyboard.press('Escape')
|
|
191
|
+
await page.waitForTimeout(500) // Wait for exit animation
|
|
192
|
+
await expect(leftContent).not.toBeVisible()
|
|
193
|
+
await expect(rightContent).not.toBeVisible()
|
|
194
|
+
await expect(topContent).not.toBeVisible()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// Test multiple popovers with simple popover
|
|
198
|
+
test('multiple popovers - simple popover with others', async ({ page }) => {
|
|
199
|
+
// Wait for page to load
|
|
200
|
+
await page.waitForLoadState('networkidle')
|
|
201
|
+
|
|
202
|
+
const simpleTrigger = page.locator('#simple-popover-trigger')
|
|
203
|
+
const simpleContent = page.locator('#simple-popover-content')
|
|
204
|
+
const leftTrigger = page.locator('#popover-left-trigger')
|
|
205
|
+
const leftContent = page.locator('#popover-left-content')
|
|
206
|
+
|
|
207
|
+
// Check initial state
|
|
208
|
+
await expect(simpleContent).not.toBeVisible()
|
|
209
|
+
await expect(leftContent).not.toBeVisible()
|
|
210
|
+
|
|
211
|
+
// Open simple popover
|
|
212
|
+
await simpleTrigger.click()
|
|
213
|
+
await page.waitForTimeout(100) // Wait for enter animation
|
|
214
|
+
await expect(simpleContent).toBeVisible({ timeout: 5000 })
|
|
215
|
+
await expect(leftContent).not.toBeVisible()
|
|
216
|
+
|
|
217
|
+
// Open left popover - should close simple popover
|
|
218
|
+
await leftTrigger.click()
|
|
219
|
+
await page.waitForTimeout(600) // Wait for previous exit + new enter animations
|
|
220
|
+
await expect(simpleContent).not.toBeVisible()
|
|
221
|
+
await expect(leftContent).toBeVisible({ timeout: 5000 })
|
|
222
|
+
|
|
223
|
+
// Open simple popover again - should close left popover
|
|
224
|
+
await simpleTrigger.click()
|
|
225
|
+
await page.waitForTimeout(600) // Wait for previous exit + new enter animations
|
|
226
|
+
await expect(simpleContent).toBeVisible({ timeout: 5000 })
|
|
227
|
+
await expect(leftContent).not.toBeVisible()
|
|
228
|
+
|
|
229
|
+
// Close simple popover
|
|
230
|
+
await page.keyboard.press('Escape')
|
|
231
|
+
await page.waitForTimeout(500) // Wait for exit animation
|
|
232
|
+
await expect(simpleContent).not.toBeVisible()
|
|
233
|
+
await expect(leftContent).not.toBeVisible()
|
|
234
|
+
})
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { expect, test, type Page } from '@playwright/test'
|
|
2
|
+
import { getBoundingRect, setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
// shared multi-trigger tests for both Popover and Menu
|
|
5
|
+
// validates render isolation, position re-anchoring, state tracking,
|
|
6
|
+
// focus return, and rapid cycling — the same infra backs both components
|
|
7
|
+
|
|
8
|
+
function parseRenderCount(text: string | null): number {
|
|
9
|
+
if (!text) return 0
|
|
10
|
+
const match = text.match(/renders:\s*(\d+)/)
|
|
11
|
+
return match ? Number(match[1]) : 0
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// abstracts the common interaction + assertion pattern for any floating element
|
|
15
|
+
// with multiple triggers, regardless of whether it's Popover or Menu
|
|
16
|
+
type MultiTriggerHarness = {
|
|
17
|
+
prefix: string // "pop" | "menu"
|
|
18
|
+
contentTestId: string
|
|
19
|
+
triggerCount: number
|
|
20
|
+
open: (page: Page, idx: number) => Promise<void>
|
|
21
|
+
close: (page: Page) => Promise<void>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const popoverHarness: MultiTriggerHarness = {
|
|
25
|
+
prefix: 'pop',
|
|
26
|
+
contentTestId: 'pop-content',
|
|
27
|
+
triggerCount: 3,
|
|
28
|
+
open: async (page, idx) => {
|
|
29
|
+
await page.getByTestId(`pop-trigger-${idx}`).click()
|
|
30
|
+
await page.waitForTimeout(300)
|
|
31
|
+
},
|
|
32
|
+
close: async (page) => {
|
|
33
|
+
// use Escape (same as Menu) so focus returns to trigger
|
|
34
|
+
await page.keyboard.press('Escape')
|
|
35
|
+
await page.waitForTimeout(300)
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const menuHarness: MultiTriggerHarness = {
|
|
40
|
+
prefix: 'menu',
|
|
41
|
+
contentTestId: 'menu-content',
|
|
42
|
+
triggerCount: 3,
|
|
43
|
+
open: async (page, idx) => {
|
|
44
|
+
await page.getByTestId(`menu-trigger-${idx}`).click()
|
|
45
|
+
await page.waitForTimeout(300)
|
|
46
|
+
},
|
|
47
|
+
close: async (page) => {
|
|
48
|
+
await page.keyboard.press('Escape')
|
|
49
|
+
await page.waitForTimeout(300)
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function multiTriggerSuite(h: MultiTriggerHarness) {
|
|
54
|
+
const triggerTestId = (idx: number) => `${h.prefix}-trigger-${idx}`
|
|
55
|
+
|
|
56
|
+
test(`[${h.prefix}] only the active trigger re-renders`, async ({ page }) => {
|
|
57
|
+
const getRenderCounts = async () => {
|
|
58
|
+
const counts: number[] = []
|
|
59
|
+
for (let i = 1; i <= h.triggerCount; i++) {
|
|
60
|
+
counts.push(
|
|
61
|
+
parseRenderCount(
|
|
62
|
+
await page.getByTestId(`${triggerTestId(i)}-render-count`).textContent()
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
return counts
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const initial = await getRenderCounts()
|
|
70
|
+
|
|
71
|
+
// open via trigger 1
|
|
72
|
+
await h.open(page, 1)
|
|
73
|
+
await expect(page.getByTestId(h.contentTestId)).toBeVisible()
|
|
74
|
+
|
|
75
|
+
const afterOpen = await getRenderCounts()
|
|
76
|
+
// trigger 2 and 3 should NOT re-render
|
|
77
|
+
expect(afterOpen[1]).toBe(initial[1])
|
|
78
|
+
expect(afterOpen[2]).toBe(initial[2])
|
|
79
|
+
|
|
80
|
+
// close
|
|
81
|
+
await h.close(page)
|
|
82
|
+
await expect(page.getByTestId(h.contentTestId)).not.toBeVisible()
|
|
83
|
+
|
|
84
|
+
const afterClose = await getRenderCounts()
|
|
85
|
+
expect(afterClose[1]).toBe(initial[1])
|
|
86
|
+
expect(afterClose[2]).toBe(initial[2])
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test(`[${h.prefix}] content re-anchors when switching triggers`, async ({ page }) => {
|
|
90
|
+
// open via trigger 1
|
|
91
|
+
await h.open(page, 1)
|
|
92
|
+
await expect(page.getByTestId(h.contentTestId)).toBeVisible()
|
|
93
|
+
|
|
94
|
+
const trigger1Box = await getBoundingRect(page, `[data-testid="${triggerTestId(1)}"]`)
|
|
95
|
+
const contentBox1 = await getBoundingRect(page, `[data-testid="${h.contentTestId}"]`)
|
|
96
|
+
expect(trigger1Box).not.toBeNull()
|
|
97
|
+
expect(contentBox1).not.toBeNull()
|
|
98
|
+
expect(Math.abs(contentBox1!.x - trigger1Box!.x)).toBeLessThan(50)
|
|
99
|
+
|
|
100
|
+
// close then open via trigger 3
|
|
101
|
+
await h.close(page)
|
|
102
|
+
await h.open(page, 3)
|
|
103
|
+
await expect(page.getByTestId(h.contentTestId)).toBeVisible()
|
|
104
|
+
|
|
105
|
+
const trigger3Box = await getBoundingRect(page, `[data-testid="${triggerTestId(3)}"]`)
|
|
106
|
+
const contentBox3 = await getBoundingRect(page, `[data-testid="${h.contentTestId}"]`)
|
|
107
|
+
expect(trigger3Box).not.toBeNull()
|
|
108
|
+
expect(contentBox3).not.toBeNull()
|
|
109
|
+
expect(Math.abs(contentBox3!.x - trigger3Box!.x)).toBeLessThan(50)
|
|
110
|
+
|
|
111
|
+
// content should have moved — trigger 3 is rightward of trigger 1
|
|
112
|
+
expect(contentBox3!.x).toBeGreaterThan(contentBox1!.x + 20)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test(`[${h.prefix}] only the active trigger has data-state=open`, async ({ page }) => {
|
|
116
|
+
await h.open(page, 2)
|
|
117
|
+
await expect(page.getByTestId(h.contentTestId)).toBeVisible()
|
|
118
|
+
|
|
119
|
+
await expect(page.getByTestId(triggerTestId(1))).toHaveAttribute(
|
|
120
|
+
'data-state',
|
|
121
|
+
'closed'
|
|
122
|
+
)
|
|
123
|
+
await expect(page.getByTestId(triggerTestId(2))).toHaveAttribute('data-state', 'open')
|
|
124
|
+
await expect(page.getByTestId(triggerTestId(3))).toHaveAttribute(
|
|
125
|
+
'data-state',
|
|
126
|
+
'closed'
|
|
127
|
+
)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
test(`[${h.prefix}] close returns focus to active trigger`, async ({ page }) => {
|
|
131
|
+
const trigger = page.getByTestId(triggerTestId(2))
|
|
132
|
+
await trigger.click()
|
|
133
|
+
await page.waitForTimeout(300)
|
|
134
|
+
await expect(page.getByTestId(h.contentTestId)).toBeVisible()
|
|
135
|
+
|
|
136
|
+
await h.close(page)
|
|
137
|
+
await page.waitForTimeout(300)
|
|
138
|
+
await expect(trigger).toBeFocused()
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
test(`[${h.prefix}] rapid open/close cycles work reliably`, async ({ page }) => {
|
|
142
|
+
for (let cycle = 0; cycle < 4; cycle++) {
|
|
143
|
+
const idx = (cycle % h.triggerCount) + 1
|
|
144
|
+
await h.open(page, idx)
|
|
145
|
+
await expect(page.getByTestId(h.contentTestId)).toBeVisible()
|
|
146
|
+
await h.close(page)
|
|
147
|
+
await expect(page.getByTestId(h.contentTestId)).not.toBeVisible()
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test(`[${h.prefix}] rapid trigger switching re-anchors each time`, async ({ page }) => {
|
|
152
|
+
for (let idx = 1; idx <= h.triggerCount; idx++) {
|
|
153
|
+
await h.open(page, idx)
|
|
154
|
+
await expect(page.getByTestId(h.contentTestId)).toBeVisible()
|
|
155
|
+
|
|
156
|
+
const triggerBox = await getBoundingRect(
|
|
157
|
+
page,
|
|
158
|
+
`[data-testid="${triggerTestId(idx)}"]`
|
|
159
|
+
)
|
|
160
|
+
const contentBox = await getBoundingRect(page, `[data-testid="${h.contentTestId}"]`)
|
|
161
|
+
expect(Math.abs(contentBox!.x - triggerBox!.x)).toBeLessThan(50)
|
|
162
|
+
|
|
163
|
+
await h.close(page)
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
test.describe('Popover & Menu Multi-Trigger', () => {
|
|
169
|
+
test.beforeEach(async ({ page }) => {
|
|
170
|
+
await setupPage(page, {
|
|
171
|
+
name: 'PopoverAndMenuMultiTriggerCase',
|
|
172
|
+
type: 'useCase',
|
|
173
|
+
waitExtra: true,
|
|
174
|
+
})
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
test.describe('Popover', () => {
|
|
178
|
+
multiTriggerSuite(popoverHarness)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test.describe('Menu', () => {
|
|
182
|
+
multiTriggerSuite(menuHarness)
|
|
183
|
+
})
|
|
184
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
import { setupPage } from './test-utils'
|
|
4
|
+
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'PopoverAnimatePositionCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
// TODO: test passes on actual site but fails here - likely test setup difference
|
|
10
|
+
test.skip('popover with animatePosition shows correct position on re-open', async ({
|
|
11
|
+
page,
|
|
12
|
+
}) => {
|
|
13
|
+
await page.waitForLoadState('networkidle')
|
|
14
|
+
|
|
15
|
+
const trigger = page.locator('#animate-position-trigger')
|
|
16
|
+
const content = page.locator('#animate-position-content')
|
|
17
|
+
const closeButton = page.locator('#animate-position-close')
|
|
18
|
+
|
|
19
|
+
await expect(trigger).toBeVisible()
|
|
20
|
+
|
|
21
|
+
// first open
|
|
22
|
+
await trigger.click()
|
|
23
|
+
await expect(content).toBeVisible({ timeout: 5000 })
|
|
24
|
+
// wait for animation to settle
|
|
25
|
+
await page.waitForTimeout(500)
|
|
26
|
+
|
|
27
|
+
const box1 = await content.boundingBox()
|
|
28
|
+
|
|
29
|
+
// close
|
|
30
|
+
await closeButton.click()
|
|
31
|
+
await expect(content).not.toBeVisible({ timeout: 5000 })
|
|
32
|
+
|
|
33
|
+
// second open
|
|
34
|
+
await trigger.click()
|
|
35
|
+
await expect(content).toBeVisible({ timeout: 5000 })
|
|
36
|
+
// wait for animation to settle
|
|
37
|
+
await page.waitForTimeout(500)
|
|
38
|
+
|
|
39
|
+
const box2 = await content.boundingBox()
|
|
40
|
+
|
|
41
|
+
// verify position is consistent between opens
|
|
42
|
+
// the x position should be the same (within small tolerance for animation)
|
|
43
|
+
expect(box1).toBeTruthy()
|
|
44
|
+
expect(box2).toBeTruthy()
|
|
45
|
+
if (box1 && box2) {
|
|
46
|
+
const xDiff = Math.abs(box1.x - box2.x)
|
|
47
|
+
const yDiff = Math.abs(box1.y - box2.y)
|
|
48
|
+
expect(xDiff).toBeLessThan(5)
|
|
49
|
+
expect(yDiff).toBeLessThan(5)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests that clicking a hoverable popover trigger while the enter animation
|
|
6
|
+
* is still playing produces a smooth exit - no snap-back, no re-enter cycle.
|
|
7
|
+
*
|
|
8
|
+
* Bug: when clicking during enter, the popover:
|
|
9
|
+
* 1. Snaps to closed (no animation)
|
|
10
|
+
* 2. Starts animating back in
|
|
11
|
+
* 3. Then finally animates out
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
test.beforeEach(async ({ page }, testInfo) => {
|
|
15
|
+
test.skip(
|
|
16
|
+
testInfo.project.name === 'animated-native',
|
|
17
|
+
'Native driver does not support hover animations on web'
|
|
18
|
+
)
|
|
19
|
+
await setupPage(page, { name: 'PopoverHoverableDelayCase', type: 'useCase' })
|
|
20
|
+
await page.waitForLoadState('networkidle')
|
|
21
|
+
await page.waitForTimeout(500)
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test('click during enter animation produces smooth exit (no snap-back)', async ({
|
|
25
|
+
page,
|
|
26
|
+
}) => {
|
|
27
|
+
const trigger = page.locator('#delay-trigger')
|
|
28
|
+
const content = page.locator('#delay-content')
|
|
29
|
+
|
|
30
|
+
await expect(content).not.toBeVisible()
|
|
31
|
+
|
|
32
|
+
// start per-frame opacity sampling
|
|
33
|
+
await page.evaluate(() => {
|
|
34
|
+
;(window as any).__opLog = [] as number[]
|
|
35
|
+
;(window as any).__rafOn = true
|
|
36
|
+
const track = () => {
|
|
37
|
+
if (!(window as any).__rafOn) return
|
|
38
|
+
const el = document.getElementById('delay-content')
|
|
39
|
+
if (el) {
|
|
40
|
+
;(window as any).__opLog.push(parseFloat(getComputedStyle(el).opacity))
|
|
41
|
+
}
|
|
42
|
+
requestAnimationFrame(track)
|
|
43
|
+
}
|
|
44
|
+
requestAnimationFrame(track)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// hover to trigger open (delay is 400ms)
|
|
48
|
+
await trigger.hover()
|
|
49
|
+
// wait for delay + a bit of animation time
|
|
50
|
+
await page.waitForTimeout(500)
|
|
51
|
+
|
|
52
|
+
// popover should be visible and animating
|
|
53
|
+
const isVisible = await content.isVisible().catch(() => false)
|
|
54
|
+
if (!isVisible) {
|
|
55
|
+
// if not visible yet, wait more
|
|
56
|
+
await page.waitForTimeout(300)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// now click the trigger (should close the popover)
|
|
60
|
+
await trigger.click()
|
|
61
|
+
|
|
62
|
+
// wait for exit animation
|
|
63
|
+
await page.waitForTimeout(1500)
|
|
64
|
+
|
|
65
|
+
// stop sampling
|
|
66
|
+
await page.evaluate(() => {
|
|
67
|
+
;(window as any).__rafOn = false
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const frames: number[] = await page.evaluate(() => (window as any).__opLog || [])
|
|
71
|
+
|
|
72
|
+
console.log(
|
|
73
|
+
'opacity timeline:',
|
|
74
|
+
JSON.stringify(frames.slice(0, 40).map((f) => +f.toFixed(3)))
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if (frames.length < 3) {
|
|
78
|
+
// not enough data, skip analysis
|
|
79
|
+
console.log('Not enough frames captured')
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// find the peak opacity
|
|
84
|
+
const peak = Math.max(...frames)
|
|
85
|
+
const peakIdx = frames.indexOf(peak)
|
|
86
|
+
|
|
87
|
+
// look for dips (snap-back indicators)
|
|
88
|
+
let maxDip = 0
|
|
89
|
+
for (let i = 1; i < frames.length - 1; i++) {
|
|
90
|
+
const prev = frames[i - 1]
|
|
91
|
+
const curr = frames[i]
|
|
92
|
+
const next = frames[i + 1]
|
|
93
|
+
if (curr < prev && curr < next) {
|
|
94
|
+
const dip = Math.min(prev - curr, next - curr)
|
|
95
|
+
if (dip > maxDip) maxDip = dip
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// look for re-enter pattern: after opacity decreases, it increases again significantly
|
|
100
|
+
let foundDecrease = false
|
|
101
|
+
let maxReIncrease = 0
|
|
102
|
+
for (let i = peakIdx + 1; i < frames.length; i++) {
|
|
103
|
+
if (frames[i] < frames[i - 1] - 0.01) {
|
|
104
|
+
foundDecrease = true
|
|
105
|
+
}
|
|
106
|
+
if (foundDecrease && frames[i] > frames[i - 1] + 0.05) {
|
|
107
|
+
const increase = frames[i] - frames[i - 1]
|
|
108
|
+
if (increase > maxReIncrease) maxReIncrease = increase
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(`peak: ${peak.toFixed(3)} at frame ${peakIdx}`)
|
|
113
|
+
console.log(`max dip: ${maxDip.toFixed(3)}`)
|
|
114
|
+
console.log(`max re-increase after exit starts: ${maxReIncrease.toFixed(3)}`)
|
|
115
|
+
|
|
116
|
+
// no significant dip (snap-back)
|
|
117
|
+
expect(
|
|
118
|
+
maxDip,
|
|
119
|
+
`Opacity dip of ${maxDip.toFixed(3)} detected - element snapped back during exit`
|
|
120
|
+
).toBeLessThan(0.15)
|
|
121
|
+
|
|
122
|
+
// no significant re-increase after exit starts (re-enter)
|
|
123
|
+
expect(
|
|
124
|
+
maxReIncrease,
|
|
125
|
+
`Opacity increased by ${maxReIncrease.toFixed(3)} after exit started - re-enter detected`
|
|
126
|
+
).toBeLessThan(0.15)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('click during mid-enter: transform should not jump', async ({ page }) => {
|
|
130
|
+
const trigger = page.locator('#delay-trigger')
|
|
131
|
+
const content = page.locator('#delay-content')
|
|
132
|
+
|
|
133
|
+
await expect(content).not.toBeVisible()
|
|
134
|
+
|
|
135
|
+
// start per-frame Y position sampling
|
|
136
|
+
await page.evaluate(() => {
|
|
137
|
+
;(window as any).__yLog = [] as number[]
|
|
138
|
+
;(window as any).__rafOn = true
|
|
139
|
+
const track = () => {
|
|
140
|
+
if (!(window as any).__rafOn) return
|
|
141
|
+
const el = document.getElementById('delay-content')
|
|
142
|
+
if (el) {
|
|
143
|
+
const transform = getComputedStyle(el).transform
|
|
144
|
+
let y = 0
|
|
145
|
+
if (transform && transform !== 'none') {
|
|
146
|
+
const matrix = new DOMMatrix(transform)
|
|
147
|
+
y = matrix.m42
|
|
148
|
+
}
|
|
149
|
+
;(window as any).__yLog.push(y)
|
|
150
|
+
}
|
|
151
|
+
requestAnimationFrame(track)
|
|
152
|
+
}
|
|
153
|
+
requestAnimationFrame(track)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// hover to trigger open
|
|
157
|
+
await trigger.hover()
|
|
158
|
+
await page.waitForTimeout(500)
|
|
159
|
+
|
|
160
|
+
const isVisible = await content.isVisible().catch(() => false)
|
|
161
|
+
if (!isVisible) {
|
|
162
|
+
await page.waitForTimeout(300)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// click to close during enter animation
|
|
166
|
+
await trigger.click()
|
|
167
|
+
await page.waitForTimeout(1500)
|
|
168
|
+
|
|
169
|
+
await page.evaluate(() => {
|
|
170
|
+
;(window as any).__rafOn = false
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const frames: number[] = await page.evaluate(() => (window as any).__yLog || [])
|
|
174
|
+
|
|
175
|
+
console.log(
|
|
176
|
+
'Y positions:',
|
|
177
|
+
JSON.stringify(frames.slice(0, 30).map((f) => +f.toFixed(2)))
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if (frames.length < 3) {
|
|
181
|
+
console.log('Not enough frames captured')
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// check for single-frame jumps
|
|
186
|
+
let maxJump = 0
|
|
187
|
+
for (let i = 1; i < frames.length; i++) {
|
|
188
|
+
const jump = Math.abs(frames[i] - frames[i - 1])
|
|
189
|
+
if (jump > maxJump) maxJump = jump
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
console.log(`max Y jump: ${maxJump.toFixed(2)}`)
|
|
193
|
+
|
|
194
|
+
expect(maxJump, `Y position jumped ${maxJump.toFixed(1)}px in one frame`).toBeLessThan(
|
|
195
|
+
5
|
|
196
|
+
)
|
|
197
|
+
})
|