@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,181 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.describe('Menu Focus Leave Behavior', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, {
|
|
7
|
+
name: 'MenuFocusLeaveCase',
|
|
8
|
+
type: 'useCase',
|
|
9
|
+
waitExtra: true,
|
|
10
|
+
})
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Bug 1: When leaving the menu entirely, focus style stays on last element.
|
|
15
|
+
* Expected: When mouse leaves the menu, no items should have the focus highlight.
|
|
16
|
+
*/
|
|
17
|
+
test('focus style clears when mouse leaves menu entirely', async ({ page }) => {
|
|
18
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
19
|
+
await trigger.focus()
|
|
20
|
+
await page.keyboard.press('Enter')
|
|
21
|
+
await page.waitForTimeout(300)
|
|
22
|
+
|
|
23
|
+
const menuContent = page.getByTestId('menu-content')
|
|
24
|
+
await expect(menuContent).toBeVisible()
|
|
25
|
+
|
|
26
|
+
const item1 = page.getByTestId('menu-item-1')
|
|
27
|
+
|
|
28
|
+
// hover item 1 - it should be focused and highlighted
|
|
29
|
+
await item1.hover()
|
|
30
|
+
await page.waitForTimeout(100)
|
|
31
|
+
await expect(item1).toBeFocused()
|
|
32
|
+
|
|
33
|
+
const item1BgWhenFocused = await item1.evaluate(
|
|
34
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
35
|
+
)
|
|
36
|
+
const hasHighlightAttr = await item1.getAttribute('data-highlighted')
|
|
37
|
+
expect(hasHighlightAttr).toBe('')
|
|
38
|
+
|
|
39
|
+
// move mouse outside the menu using raw mouse.move to avoid the portal overlay issue
|
|
40
|
+
// get the menu bounds and move outside of them
|
|
41
|
+
const menuBox = await menuContent.boundingBox()
|
|
42
|
+
if (!menuBox) throw new Error('Could not get menu bounds')
|
|
43
|
+
|
|
44
|
+
// move mouse below and to the left of the menu (outside the content)
|
|
45
|
+
await page.mouse.move(menuBox.x - 50, menuBox.y + menuBox.height + 50)
|
|
46
|
+
await page.waitForTimeout(200)
|
|
47
|
+
|
|
48
|
+
// CRITICAL: item1 should no longer have the highlight
|
|
49
|
+
const item1BgAfterLeave = await item1.evaluate(
|
|
50
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
51
|
+
)
|
|
52
|
+
const hasHighlightAttrAfter = await item1.getAttribute('data-highlighted')
|
|
53
|
+
|
|
54
|
+
// the highlight attribute should be removed
|
|
55
|
+
expect(hasHighlightAttrAfter).toBeNull()
|
|
56
|
+
// the background should be different (default, not highlighted)
|
|
57
|
+
expect(item1BgAfterLeave).not.toBe(item1BgWhenFocused)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Bug 2: Two items can appear focused when moving quickly from one submenu
|
|
62
|
+
* trigger to another submenu trigger below it while the first submenu is
|
|
63
|
+
* animating open.
|
|
64
|
+
*
|
|
65
|
+
* Scenario:
|
|
66
|
+
* 1. Hover first submenu trigger (it starts opening its submenu)
|
|
67
|
+
* 2. Move quickly down to the second submenu trigger below
|
|
68
|
+
* 3. Only ONE item should have the focus highlight, not both
|
|
69
|
+
*/
|
|
70
|
+
test('only one item highlighted when moving from submenu trigger to another below', async ({
|
|
71
|
+
page,
|
|
72
|
+
}) => {
|
|
73
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
74
|
+
await trigger.focus()
|
|
75
|
+
await page.keyboard.press('Enter')
|
|
76
|
+
await page.waitForTimeout(300)
|
|
77
|
+
|
|
78
|
+
const menuContent = page.getByTestId('menu-content')
|
|
79
|
+
await expect(menuContent).toBeVisible()
|
|
80
|
+
|
|
81
|
+
const submenuTrigger1 = page.getByTestId('submenu-trigger-1')
|
|
82
|
+
const submenuTrigger2 = page.getByTestId('submenu-trigger-2')
|
|
83
|
+
|
|
84
|
+
// hover the first submenu trigger
|
|
85
|
+
await submenuTrigger1.hover()
|
|
86
|
+
await page.waitForTimeout(50) // brief hover, not enough for submenu to fully open
|
|
87
|
+
|
|
88
|
+
// confirm trigger is focused
|
|
89
|
+
await expect(submenuTrigger1).toBeFocused()
|
|
90
|
+
|
|
91
|
+
// move quickly to the second submenu trigger below
|
|
92
|
+
const trigger1Box = await submenuTrigger1.boundingBox()
|
|
93
|
+
const trigger2Box = await submenuTrigger2.boundingBox()
|
|
94
|
+
|
|
95
|
+
if (trigger1Box && trigger2Box) {
|
|
96
|
+
// move mouse in a quick path from trigger 1 to trigger 2
|
|
97
|
+
await page.mouse.move(
|
|
98
|
+
trigger1Box.x + trigger1Box.width / 2,
|
|
99
|
+
trigger1Box.y + trigger1Box.height
|
|
100
|
+
)
|
|
101
|
+
await page.mouse.move(
|
|
102
|
+
trigger2Box.x + trigger2Box.width / 2,
|
|
103
|
+
trigger2Box.y + trigger2Box.height / 2
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await page.waitForTimeout(150)
|
|
108
|
+
|
|
109
|
+
// CRITICAL: only submenuTrigger2 should be highlighted, not both
|
|
110
|
+
const trigger1Highlight = await submenuTrigger1.getAttribute('data-highlighted')
|
|
111
|
+
const trigger2Highlight = await submenuTrigger2.getAttribute('data-highlighted')
|
|
112
|
+
|
|
113
|
+
// the second trigger should be highlighted
|
|
114
|
+
expect(trigger2Highlight).toBe('')
|
|
115
|
+
|
|
116
|
+
// the first trigger should NOT be highlighted
|
|
117
|
+
expect(trigger1Highlight).toBeNull()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Bug 2b: More specific test - when moving at a very specific speed
|
|
122
|
+
* during the submenu opening animation, both triggers can appear focused.
|
|
123
|
+
*/
|
|
124
|
+
test('no double highlight during submenu open with rapid downward movement', async ({
|
|
125
|
+
page,
|
|
126
|
+
}) => {
|
|
127
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
128
|
+
await trigger.focus()
|
|
129
|
+
await page.keyboard.press('Enter')
|
|
130
|
+
await page.waitForTimeout(300)
|
|
131
|
+
|
|
132
|
+
const menuContent = page.getByTestId('menu-content')
|
|
133
|
+
await expect(menuContent).toBeVisible()
|
|
134
|
+
|
|
135
|
+
const submenuTrigger1 = page.getByTestId('submenu-trigger-1')
|
|
136
|
+
const submenuTrigger2 = page.getByTestId('submenu-trigger-2')
|
|
137
|
+
|
|
138
|
+
// get bounding boxes
|
|
139
|
+
const trigger1Box = await submenuTrigger1.boundingBox()
|
|
140
|
+
const trigger2Box = await submenuTrigger2.boundingBox()
|
|
141
|
+
|
|
142
|
+
if (!trigger1Box || !trigger2Box) {
|
|
143
|
+
throw new Error('Could not get bounding boxes')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// hover first submenu trigger to start opening
|
|
147
|
+
await page.mouse.move(
|
|
148
|
+
trigger1Box.x + trigger1Box.width / 2,
|
|
149
|
+
trigger1Box.y + trigger1Box.height / 2
|
|
150
|
+
)
|
|
151
|
+
await page.waitForTimeout(60) // just after the 100ms open delay starts
|
|
152
|
+
|
|
153
|
+
// now move down at varying speeds to try to trigger the bug
|
|
154
|
+
// the bug happens at a specific speed where submenu is animating
|
|
155
|
+
for (let i = 0; i < 5; i++) {
|
|
156
|
+
const y = trigger1Box.y + trigger1Box.height / 2 + i * 5
|
|
157
|
+
await page.mouse.move(trigger1Box.x + trigger1Box.width / 2, y)
|
|
158
|
+
await page.waitForTimeout(15)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// move to second submenu trigger
|
|
162
|
+
await page.mouse.move(
|
|
163
|
+
trigger2Box.x + trigger2Box.width / 2,
|
|
164
|
+
trigger2Box.y + trigger2Box.height / 2
|
|
165
|
+
)
|
|
166
|
+
await page.waitForTimeout(150)
|
|
167
|
+
|
|
168
|
+
// count how many items have the highlight
|
|
169
|
+
const allHighlighted = await menuContent.locator('[data-highlighted]').count()
|
|
170
|
+
|
|
171
|
+
// CRITICAL: should only be 1 highlighted item
|
|
172
|
+
expect(allHighlighted).toBeLessThanOrEqual(1)
|
|
173
|
+
|
|
174
|
+
// if trigger2 is highlighted, trigger1 should not be
|
|
175
|
+
const trigger2Highlight = await submenuTrigger2.getAttribute('data-highlighted')
|
|
176
|
+
if (trigger2Highlight === '') {
|
|
177
|
+
const trigger1Highlight = await submenuTrigger1.getAttribute('data-highlighted')
|
|
178
|
+
expect(trigger1Highlight).toBeNull()
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
})
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.describe('Menu Highlight Behavior', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'MenuHighlightCase', type: 'useCase', waitExtra: true })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test('hovering an item focuses it', async ({ page }) => {
|
|
10
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
11
|
+
await expect(trigger).toBeVisible()
|
|
12
|
+
|
|
13
|
+
// use keyboard to open menu (avoids overlay click interception in dev)
|
|
14
|
+
await trigger.focus()
|
|
15
|
+
await page.keyboard.press('Enter')
|
|
16
|
+
await page.waitForTimeout(300)
|
|
17
|
+
|
|
18
|
+
const menuContent = page.getByTestId('menu-content')
|
|
19
|
+
await expect(menuContent).toBeVisible()
|
|
20
|
+
|
|
21
|
+
// hover over item 2
|
|
22
|
+
const item2 = page.getByTestId('menu-item-2')
|
|
23
|
+
await item2.hover()
|
|
24
|
+
await page.waitForTimeout(100)
|
|
25
|
+
|
|
26
|
+
// item should be focused (not just hovered)
|
|
27
|
+
await expect(item2).toBeFocused()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('only one item is highlighted at a time when switching mouse to keyboard', async ({
|
|
31
|
+
page,
|
|
32
|
+
}) => {
|
|
33
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
34
|
+
await trigger.focus()
|
|
35
|
+
await page.keyboard.press('Enter')
|
|
36
|
+
await page.waitForTimeout(300)
|
|
37
|
+
|
|
38
|
+
const menuContent = page.getByTestId('menu-content')
|
|
39
|
+
await expect(menuContent).toBeVisible()
|
|
40
|
+
|
|
41
|
+
// hover item 2 to highlight it
|
|
42
|
+
const item2 = page.getByTestId('menu-item-2')
|
|
43
|
+
await item2.hover()
|
|
44
|
+
await page.waitForTimeout(100)
|
|
45
|
+
await expect(item2).toBeFocused()
|
|
46
|
+
|
|
47
|
+
// now use keyboard to move to item 3
|
|
48
|
+
await page.keyboard.press('ArrowDown')
|
|
49
|
+
await page.waitForTimeout(100)
|
|
50
|
+
|
|
51
|
+
const item3 = page.getByTestId('menu-item-3')
|
|
52
|
+
await expect(item3).toBeFocused()
|
|
53
|
+
|
|
54
|
+
// item 2 should no longer be focused
|
|
55
|
+
await expect(item2).not.toBeFocused()
|
|
56
|
+
|
|
57
|
+
// verify only item3 has the highlighted data attribute
|
|
58
|
+
const item2Highlighted = await item2.getAttribute('data-highlighted')
|
|
59
|
+
const item3Highlighted = await item3.getAttribute('data-highlighted')
|
|
60
|
+
expect(item2Highlighted).toBeNull()
|
|
61
|
+
expect(item3Highlighted).toBe('')
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('hover then arrow down shows only ONE highlighted item (no double highlight)', async ({
|
|
65
|
+
page,
|
|
66
|
+
}) => {
|
|
67
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
68
|
+
await trigger.focus()
|
|
69
|
+
await page.keyboard.press('Enter')
|
|
70
|
+
await page.waitForTimeout(300)
|
|
71
|
+
|
|
72
|
+
const item1 = page.getByTestId('menu-item-1')
|
|
73
|
+
const item2 = page.getByTestId('menu-item-2')
|
|
74
|
+
|
|
75
|
+
// hover item 1 - it should get background highlight
|
|
76
|
+
await item1.hover()
|
|
77
|
+
await page.waitForTimeout(100)
|
|
78
|
+
|
|
79
|
+
const item1BgAfterHover = await item1.evaluate(
|
|
80
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
81
|
+
)
|
|
82
|
+
const item2BgAfterHover = await item2.evaluate(
|
|
83
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// item1 should have highlight bg, item2 should not
|
|
87
|
+
expect(item1BgAfterHover).not.toBe(item2BgAfterHover)
|
|
88
|
+
|
|
89
|
+
// now press ArrowDown to move to item 2
|
|
90
|
+
await page.keyboard.press('ArrowDown')
|
|
91
|
+
await page.waitForTimeout(100)
|
|
92
|
+
|
|
93
|
+
const item1BgAfterArrow = await item1.evaluate(
|
|
94
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
95
|
+
)
|
|
96
|
+
const item2BgAfterArrow = await item2.evaluate(
|
|
97
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
// CRITICAL: item1 should NO LONGER have highlight bg
|
|
101
|
+
// item2 should now have the highlight bg
|
|
102
|
+
// they should NOT both be highlighted
|
|
103
|
+
expect(item1BgAfterArrow).toBe(item2BgAfterHover) // item1 now has default bg
|
|
104
|
+
expect(item2BgAfterArrow).toBe(item1BgAfterHover) // item2 now has highlight bg
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('keyboard navigation then mouse hover transfers highlight cleanly', async ({
|
|
108
|
+
page,
|
|
109
|
+
}) => {
|
|
110
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
111
|
+
await trigger.focus()
|
|
112
|
+
await page.keyboard.press('Enter')
|
|
113
|
+
await page.waitForTimeout(300)
|
|
114
|
+
|
|
115
|
+
// keyboard opens menu and focuses first item
|
|
116
|
+
const item1 = page.getByTestId('menu-item-1')
|
|
117
|
+
await expect(item1).toBeFocused()
|
|
118
|
+
|
|
119
|
+
// navigate down with keyboard
|
|
120
|
+
await page.keyboard.press('ArrowDown')
|
|
121
|
+
await page.waitForTimeout(100)
|
|
122
|
+
const item2 = page.getByTestId('menu-item-2')
|
|
123
|
+
await expect(item2).toBeFocused()
|
|
124
|
+
|
|
125
|
+
// now hover item 1 with mouse
|
|
126
|
+
await item1.hover()
|
|
127
|
+
await page.waitForTimeout(100)
|
|
128
|
+
|
|
129
|
+
// item 1 should now be focused, item 2 should not
|
|
130
|
+
await expect(item1).toBeFocused()
|
|
131
|
+
await expect(item2).not.toBeFocused()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('item has consistent background on focus regardless of input method', async ({
|
|
135
|
+
page,
|
|
136
|
+
}) => {
|
|
137
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
138
|
+
await trigger.focus()
|
|
139
|
+
await page.keyboard.press('Enter')
|
|
140
|
+
await page.waitForTimeout(300)
|
|
141
|
+
|
|
142
|
+
const menuContent = page.getByTestId('menu-content')
|
|
143
|
+
await expect(menuContent).toBeVisible()
|
|
144
|
+
|
|
145
|
+
const item1 = page.getByTestId('menu-item-1')
|
|
146
|
+
const item2 = page.getByTestId('menu-item-2')
|
|
147
|
+
|
|
148
|
+
// hover item 1
|
|
149
|
+
await item1.hover()
|
|
150
|
+
await page.waitForTimeout(100)
|
|
151
|
+
const item1BgOnHover = await item1.evaluate(
|
|
152
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
// move to item 2 via keyboard
|
|
156
|
+
await page.keyboard.press('ArrowDown')
|
|
157
|
+
await page.waitForTimeout(100)
|
|
158
|
+
const item2BgOnKeyboard = await item2.evaluate(
|
|
159
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// backgrounds should match (same highlight style for both input methods)
|
|
163
|
+
expect(item1BgOnHover).toBe(item2BgOnKeyboard)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.describe('Menu hover/keyboard bug fixes', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'MenuAccessibilityCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Bug 1: hoverStyle should not cause double highlighting
|
|
11
|
+
* When you hover an item and then use keyboard to navigate away WITHOUT moving the mouse,
|
|
12
|
+
* the hovered item's hoverStyle can persist alongside the keyboard-focused item's highlight,
|
|
13
|
+
* resulting in two items appearing highlighted at once.
|
|
14
|
+
*
|
|
15
|
+
* The key scenario: mouse is still over item1, but keyboard navigates to item2.
|
|
16
|
+
* Both items should NOT have highlighted background - only item2 should.
|
|
17
|
+
*/
|
|
18
|
+
test('no double highlight: when keyboard navigates while mouse stays over original item', async ({
|
|
19
|
+
page,
|
|
20
|
+
}) => {
|
|
21
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
22
|
+
await trigger.focus()
|
|
23
|
+
await page.keyboard.press('Enter')
|
|
24
|
+
await page.waitForTimeout(300)
|
|
25
|
+
|
|
26
|
+
const menuContent = page.getByTestId('menu-content')
|
|
27
|
+
await expect(menuContent).toBeVisible()
|
|
28
|
+
|
|
29
|
+
const item1 = page.getByTestId('menu-item-1')
|
|
30
|
+
const item2 = page.getByTestId('menu-item-2')
|
|
31
|
+
|
|
32
|
+
// get the default (non-highlighted) background color of item2
|
|
33
|
+
const defaultBg = await item2.evaluate((el) => getComputedStyle(el).backgroundColor)
|
|
34
|
+
|
|
35
|
+
// hover item 1 with mouse - it should get highlighted
|
|
36
|
+
await item1.hover()
|
|
37
|
+
await page.waitForTimeout(150)
|
|
38
|
+
|
|
39
|
+
// item1 should be focused and highlighted
|
|
40
|
+
await expect(item1).toBeFocused()
|
|
41
|
+
const item1BgAfterHover = await item1.evaluate(
|
|
42
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
// item1 should have a different (highlighted) background
|
|
46
|
+
expect(item1BgAfterHover).not.toBe(defaultBg)
|
|
47
|
+
|
|
48
|
+
// now use keyboard to move to item 2
|
|
49
|
+
// CRITICAL: do NOT move the mouse - it stays over item1
|
|
50
|
+
// this is the exact scenario where hoverStyle causes double highlight
|
|
51
|
+
await page.keyboard.press('ArrowDown')
|
|
52
|
+
await page.waitForTimeout(150)
|
|
53
|
+
|
|
54
|
+
// item2 should now be focused and highlighted
|
|
55
|
+
await expect(item2).toBeFocused()
|
|
56
|
+
|
|
57
|
+
// get backgrounds - CRITICAL TEST: only item2 should have highlight bg
|
|
58
|
+
const item1BgAfterArrow = await item1.evaluate(
|
|
59
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
60
|
+
)
|
|
61
|
+
const item2BgAfterArrow = await item2.evaluate(
|
|
62
|
+
(el) => getComputedStyle(el).backgroundColor
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
// item2 should have highlighted bg (via data-highlighted/focusStyle)
|
|
66
|
+
expect(item2BgAfterArrow).not.toBe(defaultBg)
|
|
67
|
+
|
|
68
|
+
// CRITICAL: item1 should NOT have highlighted background even though mouse is still over it
|
|
69
|
+
// If this fails, it means hoverStyle is causing double highlight
|
|
70
|
+
expect(item1BgAfterArrow).toBe(defaultBg)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Bug 2: After hovering a submenu trigger (before the auto-open timer fires),
|
|
75
|
+
* pressing ArrowRight should open the submenu and focus its first item.
|
|
76
|
+
*
|
|
77
|
+
* When you hover over a submenu trigger with the mouse and IMMEDIATELY press ArrowRight
|
|
78
|
+
* (before the 100ms auto-open delay), the submenu should open and focus should move into it.
|
|
79
|
+
*/
|
|
80
|
+
test('hovering submenu trigger then immediately pressing ArrowRight opens and focuses submenu', async ({
|
|
81
|
+
page,
|
|
82
|
+
}) => {
|
|
83
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
84
|
+
await trigger.focus()
|
|
85
|
+
await page.keyboard.press('Enter')
|
|
86
|
+
await page.waitForTimeout(300)
|
|
87
|
+
|
|
88
|
+
const menuContent = page.getByTestId('menu-content')
|
|
89
|
+
await expect(menuContent).toBeVisible()
|
|
90
|
+
|
|
91
|
+
const submenuTrigger = page.getByTestId('submenu-trigger')
|
|
92
|
+
|
|
93
|
+
// hover the submenu trigger with mouse
|
|
94
|
+
await submenuTrigger.hover()
|
|
95
|
+
// don't wait for the auto-open timer - press arrow key immediately
|
|
96
|
+
await page.waitForTimeout(50) // just enough for the focus to happen
|
|
97
|
+
|
|
98
|
+
// the submenu trigger should be focused from the hover
|
|
99
|
+
await expect(submenuTrigger).toBeFocused()
|
|
100
|
+
|
|
101
|
+
// now press ArrowRight to open the submenu - the submenu might not be open yet
|
|
102
|
+
// because the 100ms auto-open timer hasn't fired
|
|
103
|
+
const submenuContent = page.getByTestId('submenu-content')
|
|
104
|
+
|
|
105
|
+
await page.keyboard.press('ArrowRight')
|
|
106
|
+
await page.waitForTimeout(300)
|
|
107
|
+
|
|
108
|
+
// the submenu should be open
|
|
109
|
+
await expect(submenuContent).toBeVisible({ timeout: 2000 })
|
|
110
|
+
|
|
111
|
+
// CRITICAL: the first submenu item should now be focused
|
|
112
|
+
// this is the key difference from mouse-hover behavior
|
|
113
|
+
const submenuItem1 = page.getByTestId('submenu-item-1')
|
|
114
|
+
await expect(submenuItem1).toBeFocused()
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Bug 2b: When submenu is already open via hover, pressing ArrowRight should focus
|
|
119
|
+
* the first submenu item (moving focus into the submenu).
|
|
120
|
+
*
|
|
121
|
+
* Scenario: User hovers submenu trigger, submenu auto-opens, user presses ArrowRight
|
|
122
|
+
* expecting to move focus into the already-open submenu.
|
|
123
|
+
*/
|
|
124
|
+
test('when submenu is already open via hover, ArrowRight focuses first submenu item', async ({
|
|
125
|
+
page,
|
|
126
|
+
}) => {
|
|
127
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
128
|
+
await trigger.focus()
|
|
129
|
+
await page.keyboard.press('Enter')
|
|
130
|
+
await page.waitForTimeout(300)
|
|
131
|
+
|
|
132
|
+
const menuContent = page.getByTestId('menu-content')
|
|
133
|
+
await expect(menuContent).toBeVisible()
|
|
134
|
+
|
|
135
|
+
const submenuTrigger = page.getByTestId('submenu-trigger')
|
|
136
|
+
|
|
137
|
+
// hover the submenu trigger and wait for it to auto-open
|
|
138
|
+
await submenuTrigger.hover()
|
|
139
|
+
await page.waitForTimeout(200) // wait for 100ms auto-open delay
|
|
140
|
+
|
|
141
|
+
const submenuContent = page.getByTestId('submenu-content')
|
|
142
|
+
await expect(submenuContent).toBeVisible({ timeout: 2000 })
|
|
143
|
+
|
|
144
|
+
// at this point, submenu is open but trigger is still focused (from hover)
|
|
145
|
+
await expect(submenuTrigger).toBeFocused()
|
|
146
|
+
|
|
147
|
+
// pressing ArrowRight should move focus INTO the submenu
|
|
148
|
+
await page.keyboard.press('ArrowRight')
|
|
149
|
+
await page.waitForTimeout(150)
|
|
150
|
+
|
|
151
|
+
// the first submenu item should now be focused
|
|
152
|
+
const submenuItem1 = page.getByTestId('submenu-item-1')
|
|
153
|
+
await expect(submenuItem1).toBeFocused()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Bug 2c: When a submenu is open via mouse hover (not keyboard), pressing keyboard keys
|
|
158
|
+
* should still work to navigate within it.
|
|
159
|
+
*
|
|
160
|
+
* Scenario: User opens menu, hovers over submenu trigger (which opens submenu),
|
|
161
|
+
* then hovers over a submenu item, then tries to use keyboard to navigate.
|
|
162
|
+
*/
|
|
163
|
+
test('submenu opened via hover then keyboard navigation works', async ({ page }) => {
|
|
164
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
165
|
+
await trigger.focus()
|
|
166
|
+
await page.keyboard.press('Enter')
|
|
167
|
+
await page.waitForTimeout(300)
|
|
168
|
+
|
|
169
|
+
const menuContent = page.getByTestId('menu-content')
|
|
170
|
+
await expect(menuContent).toBeVisible()
|
|
171
|
+
|
|
172
|
+
// hover the submenu trigger - this should open the submenu after a delay
|
|
173
|
+
const submenuTrigger = page.getByTestId('submenu-trigger')
|
|
174
|
+
await submenuTrigger.hover()
|
|
175
|
+
await page.waitForTimeout(300) // wait for the 100ms delay to open
|
|
176
|
+
|
|
177
|
+
const submenuContent = page.getByTestId('submenu-content')
|
|
178
|
+
await expect(submenuContent).toBeVisible({ timeout: 2000 })
|
|
179
|
+
|
|
180
|
+
// now hover a submenu item
|
|
181
|
+
const submenuItem1 = page.getByTestId('submenu-item-1')
|
|
182
|
+
await submenuItem1.hover()
|
|
183
|
+
await page.waitForTimeout(150)
|
|
184
|
+
await expect(submenuItem1).toBeFocused()
|
|
185
|
+
|
|
186
|
+
// now try keyboard navigation - ArrowDown should go to submenu-item-2
|
|
187
|
+
await page.keyboard.press('ArrowDown')
|
|
188
|
+
await page.waitForTimeout(150)
|
|
189
|
+
|
|
190
|
+
const submenuItem2 = page.getByTestId('submenu-item-2')
|
|
191
|
+
await expect(submenuItem2).toBeFocused()
|
|
192
|
+
|
|
193
|
+
// ArrowLeft should close submenu and return to parent
|
|
194
|
+
await page.keyboard.press('ArrowLeft')
|
|
195
|
+
await page.waitForTimeout(300)
|
|
196
|
+
|
|
197
|
+
await expect(submenuContent).not.toBeVisible()
|
|
198
|
+
await expect(submenuTrigger).toBeFocused()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Bug 2c: After hovering a submenu item (inside an open submenu), pressing ArrowRight
|
|
203
|
+
* should NOT do anything weird (or should do the right thing depending on context).
|
|
204
|
+
*
|
|
205
|
+
* This tests that after the user hovers items in the submenu with their mouse,
|
|
206
|
+
* then switches to keyboard navigation, it works correctly.
|
|
207
|
+
*/
|
|
208
|
+
test('hovering submenu item then using keyboard navigation works', async ({ page }) => {
|
|
209
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
210
|
+
await trigger.focus()
|
|
211
|
+
await page.keyboard.press('Enter')
|
|
212
|
+
await page.waitForTimeout(300)
|
|
213
|
+
|
|
214
|
+
const menuContent = page.getByTestId('menu-content')
|
|
215
|
+
await expect(menuContent).toBeVisible()
|
|
216
|
+
|
|
217
|
+
// open the submenu via keyboard first
|
|
218
|
+
const submenuTrigger = page.getByTestId('submenu-trigger')
|
|
219
|
+
await submenuTrigger.focus()
|
|
220
|
+
await page.keyboard.press('ArrowRight')
|
|
221
|
+
await page.waitForTimeout(300)
|
|
222
|
+
|
|
223
|
+
const submenuContent = page.getByTestId('submenu-content')
|
|
224
|
+
await expect(submenuContent).toBeVisible()
|
|
225
|
+
|
|
226
|
+
// now hover the first submenu item with mouse
|
|
227
|
+
const submenuItem1 = page.getByTestId('submenu-item-1')
|
|
228
|
+
await submenuItem1.hover()
|
|
229
|
+
await page.waitForTimeout(150)
|
|
230
|
+
await expect(submenuItem1).toBeFocused()
|
|
231
|
+
|
|
232
|
+
// hover the second submenu item
|
|
233
|
+
const submenuItem2 = page.getByTestId('submenu-item-2')
|
|
234
|
+
await submenuItem2.hover()
|
|
235
|
+
await page.waitForTimeout(150)
|
|
236
|
+
await expect(submenuItem2).toBeFocused()
|
|
237
|
+
|
|
238
|
+
// now use keyboard to navigate back up
|
|
239
|
+
await page.keyboard.press('ArrowUp')
|
|
240
|
+
await page.waitForTimeout(150)
|
|
241
|
+
|
|
242
|
+
// the first item should now be focused (keyboard navigation should work)
|
|
243
|
+
await expect(submenuItem1).toBeFocused()
|
|
244
|
+
|
|
245
|
+
// pressing ArrowLeft should close the submenu and return focus to the trigger
|
|
246
|
+
await page.keyboard.press('ArrowLeft')
|
|
247
|
+
await page.waitForTimeout(300)
|
|
248
|
+
|
|
249
|
+
await expect(submenuContent).not.toBeVisible()
|
|
250
|
+
await expect(submenuTrigger).toBeFocused()
|
|
251
|
+
})
|
|
252
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.describe('Menu Item Focus Preservation', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'MenuItemFocusCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test('menu item that focuses a new element keeps focus on that element', async ({
|
|
10
|
+
page,
|
|
11
|
+
}) => {
|
|
12
|
+
await page.waitForLoadState('networkidle')
|
|
13
|
+
|
|
14
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
15
|
+
await trigger.click()
|
|
16
|
+
await page.waitForTimeout(300)
|
|
17
|
+
|
|
18
|
+
const menuContent = page.getByTestId('menu-content')
|
|
19
|
+
await expect(menuContent).toBeVisible()
|
|
20
|
+
|
|
21
|
+
// click the item that shows and focuses an input
|
|
22
|
+
const focusInputItem = page.getByTestId('focus-input-item')
|
|
23
|
+
await focusInputItem.click()
|
|
24
|
+
await page.waitForTimeout(300)
|
|
25
|
+
|
|
26
|
+
// menu should be closed
|
|
27
|
+
await expect(menuContent).not.toBeVisible()
|
|
28
|
+
|
|
29
|
+
// the dynamic input should be focused, NOT the trigger
|
|
30
|
+
const dynamicInput = page.getByTestId('dynamic-input')
|
|
31
|
+
await expect(dynamicInput).toBeVisible()
|
|
32
|
+
await expect(dynamicInput).toBeFocused()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('menu item that focuses existing element keeps focus on that element', async ({
|
|
36
|
+
page,
|
|
37
|
+
}) => {
|
|
38
|
+
await page.waitForLoadState('networkidle')
|
|
39
|
+
|
|
40
|
+
const trigger = page.getByTestId('menu-trigger')
|
|
41
|
+
await trigger.click()
|
|
42
|
+
await page.waitForTimeout(300)
|
|
43
|
+
|
|
44
|
+
const menuContent = page.getByTestId('menu-content')
|
|
45
|
+
await expect(menuContent).toBeVisible()
|
|
46
|
+
|
|
47
|
+
// click the item that focuses the always-visible input
|
|
48
|
+
const focusExistingItem = page.getByTestId('focus-existing-item')
|
|
49
|
+
await focusExistingItem.click()
|
|
50
|
+
await page.waitForTimeout(300)
|
|
51
|
+
|
|
52
|
+
// menu should be closed
|
|
53
|
+
await expect(menuContent).not.toBeVisible()
|
|
54
|
+
|
|
55
|
+
// the always-visible input should be focused, NOT the trigger
|
|
56
|
+
const alwaysVisibleInput = page.getByTestId('always-visible-input')
|
|
57
|
+
await expect(alwaysVisibleInput).toBeFocused()
|
|
58
|
+
})
|
|
59
|
+
})
|