@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,293 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.describe('Render Prop', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'RenderPropCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test.describe('styled() render prop', () => {
|
|
10
|
+
test('renders button element when render="button"', async ({ page }) => {
|
|
11
|
+
const element = page.getByTestId('styled-button')
|
|
12
|
+
await expect(element).toBeVisible()
|
|
13
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
14
|
+
expect(tagName).toBe('button')
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('renders anchor element when render="a"', async ({ page }) => {
|
|
18
|
+
const element = page.getByTestId('styled-anchor')
|
|
19
|
+
await expect(element).toBeVisible()
|
|
20
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
21
|
+
expect(tagName).toBe('a')
|
|
22
|
+
// Check href attribute is passed through
|
|
23
|
+
const href = await element.getAttribute('href')
|
|
24
|
+
expect(href).toBe('#')
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test.describe('semantic elements', () => {
|
|
29
|
+
test('renders nav element', async ({ page }) => {
|
|
30
|
+
const element = page.getByTestId('styled-nav')
|
|
31
|
+
await expect(element).toBeVisible()
|
|
32
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
33
|
+
expect(tagName).toBe('nav')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('renders main element', async ({ page }) => {
|
|
37
|
+
const element = page.getByTestId('styled-main')
|
|
38
|
+
await expect(element).toBeVisible()
|
|
39
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
40
|
+
expect(tagName).toBe('main')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('renders section element', async ({ page }) => {
|
|
44
|
+
const element = page.getByTestId('styled-section')
|
|
45
|
+
await expect(element).toBeVisible()
|
|
46
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
47
|
+
expect(tagName).toBe('section')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('renders article element', async ({ page }) => {
|
|
51
|
+
const element = page.getByTestId('styled-article')
|
|
52
|
+
await expect(element).toBeVisible()
|
|
53
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
54
|
+
expect(tagName).toBe('article')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('renders footer element', async ({ page }) => {
|
|
58
|
+
const element = page.getByTestId('styled-footer')
|
|
59
|
+
await expect(element).toBeVisible()
|
|
60
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
61
|
+
expect(tagName).toBe('footer')
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test.describe('form elements', () => {
|
|
66
|
+
test('renders form element', async ({ page }) => {
|
|
67
|
+
const element = page.getByTestId('styled-form')
|
|
68
|
+
await expect(element).toBeVisible()
|
|
69
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
70
|
+
expect(tagName).toBe('form')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('renders fieldset element', async ({ page }) => {
|
|
74
|
+
const element = page.getByTestId('styled-fieldset')
|
|
75
|
+
await expect(element).toBeVisible()
|
|
76
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
77
|
+
expect(tagName).toBe('fieldset')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('renders label element with htmlFor', async ({ page }) => {
|
|
81
|
+
const element = page.getByTestId('styled-label')
|
|
82
|
+
await expect(element).toBeVisible()
|
|
83
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
84
|
+
expect(tagName).toBe('label')
|
|
85
|
+
const htmlFor = await element.getAttribute('for')
|
|
86
|
+
expect(htmlFor).toBe('test-input')
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test.describe('heading elements', () => {
|
|
91
|
+
test('renders h1 element', async ({ page }) => {
|
|
92
|
+
const element = page.getByTestId('styled-h1')
|
|
93
|
+
await expect(element).toBeVisible()
|
|
94
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
95
|
+
expect(tagName).toBe('h1')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('renders h2 element', async ({ page }) => {
|
|
99
|
+
const element = page.getByTestId('styled-h2')
|
|
100
|
+
await expect(element).toBeVisible()
|
|
101
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
102
|
+
expect(tagName).toBe('h2')
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test.describe('list elements', () => {
|
|
107
|
+
test('renders ul element', async ({ page }) => {
|
|
108
|
+
const element = page.getByTestId('styled-ul')
|
|
109
|
+
await expect(element).toBeVisible()
|
|
110
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
111
|
+
expect(tagName).toBe('ul')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('renders li elements', async ({ page }) => {
|
|
115
|
+
const li1 = page.getByTestId('styled-li-1')
|
|
116
|
+
const li2 = page.getByTestId('styled-li-2')
|
|
117
|
+
await expect(li1).toBeVisible()
|
|
118
|
+
await expect(li2).toBeVisible()
|
|
119
|
+
const tagName1 = await li1.evaluate((el) => el.tagName.toLowerCase())
|
|
120
|
+
const tagName2 = await li2.evaluate((el) => el.tagName.toLowerCase())
|
|
121
|
+
expect(tagName1).toBe('li')
|
|
122
|
+
expect(tagName2).toBe('li')
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test.describe('runtime render prop override', () => {
|
|
127
|
+
test('overrides to button at runtime', async ({ page }) => {
|
|
128
|
+
const element = page.getByTestId('runtime-button')
|
|
129
|
+
await expect(element).toBeVisible()
|
|
130
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
131
|
+
expect(tagName).toBe('button')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test('overrides to anchor at runtime', async ({ page }) => {
|
|
135
|
+
const element = page.getByTestId('runtime-anchor')
|
|
136
|
+
await expect(element).toBeVisible()
|
|
137
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
138
|
+
expect(tagName).toBe('a')
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test.describe('Stack with render prop', () => {
|
|
143
|
+
test('Stack renders as section', async ({ page }) => {
|
|
144
|
+
const element = page.getByTestId('stack-as-section')
|
|
145
|
+
await expect(element).toBeVisible()
|
|
146
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
147
|
+
expect(tagName).toBe('section')
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('Stack renders as aside', async ({ page }) => {
|
|
151
|
+
const element = page.getByTestId('stack-as-aside')
|
|
152
|
+
await expect(element).toBeVisible()
|
|
153
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
154
|
+
expect(tagName).toBe('aside')
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test.describe('Text with render prop', () => {
|
|
159
|
+
test('Text renders as span', async ({ page }) => {
|
|
160
|
+
const element = page.getByTestId('text-as-span')
|
|
161
|
+
await expect(element).toBeVisible()
|
|
162
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
163
|
+
expect(tagName).toBe('span')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('Text renders as strong', async ({ page }) => {
|
|
167
|
+
const element = page.getByTestId('text-as-strong')
|
|
168
|
+
await expect(element).toBeVisible()
|
|
169
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
170
|
+
expect(tagName).toBe('strong')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('Text renders as em', async ({ page }) => {
|
|
174
|
+
const element = page.getByTestId('text-as-em')
|
|
175
|
+
await expect(element).toBeVisible()
|
|
176
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
177
|
+
expect(tagName).toBe('em')
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test.describe('styles are applied correctly', () => {
|
|
182
|
+
test('styled button has correct styles', async ({ page }) => {
|
|
183
|
+
const element = page.getByTestId('styled-button')
|
|
184
|
+
await expect(element).toBeVisible()
|
|
185
|
+
|
|
186
|
+
// Check cursor style
|
|
187
|
+
const cursor = await element.evaluate((el) => window.getComputedStyle(el).cursor)
|
|
188
|
+
expect(cursor).toBe('pointer')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('styled nav has correct background', async ({ page }) => {
|
|
192
|
+
const element = page.getByTestId('styled-nav')
|
|
193
|
+
await expect(element).toBeVisible()
|
|
194
|
+
|
|
195
|
+
// Just verify the element has some background set
|
|
196
|
+
const bg = await element.evaluate(
|
|
197
|
+
(el) => window.getComputedStyle(el).backgroundColor
|
|
198
|
+
)
|
|
199
|
+
expect(bg).toBeTruthy()
|
|
200
|
+
expect(bg).not.toBe('rgba(0, 0, 0, 0)')
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test.describe('JSX element render prop', () => {
|
|
205
|
+
test('renders JSX element with merged props', async ({ page }) => {
|
|
206
|
+
const element = page.getByTestId('jsx-element-render')
|
|
207
|
+
await expect(element).toBeVisible()
|
|
208
|
+
|
|
209
|
+
// Should render as anchor (from JSX element)
|
|
210
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
211
|
+
expect(tagName).toBe('a')
|
|
212
|
+
|
|
213
|
+
// Should have href from JSX element
|
|
214
|
+
const href = await element.getAttribute('href')
|
|
215
|
+
expect(href).toBe('/test-link')
|
|
216
|
+
|
|
217
|
+
// Should have custom data attribute from JSX element
|
|
218
|
+
const jsxAttr = await element.getAttribute('data-jsx-element')
|
|
219
|
+
expect(jsxAttr).toBe('true')
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test('renders JSX button with type attribute', async ({ page }) => {
|
|
223
|
+
const element = page.getByTestId('jsx-element-button')
|
|
224
|
+
await expect(element).toBeVisible()
|
|
225
|
+
|
|
226
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
227
|
+
expect(tagName).toBe('button')
|
|
228
|
+
|
|
229
|
+
const type = await element.getAttribute('type')
|
|
230
|
+
expect(type).toBe('submit')
|
|
231
|
+
|
|
232
|
+
const jsxAttr = await element.getAttribute('data-jsx-button')
|
|
233
|
+
expect(jsxAttr).toBe('true')
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
test('JSX element has styles from Stack', async ({ page }) => {
|
|
237
|
+
const element = page.getByTestId('jsx-element-render')
|
|
238
|
+
await expect(element).toBeVisible()
|
|
239
|
+
|
|
240
|
+
// Should have padding and background from Stack props
|
|
241
|
+
const bg = await element.evaluate(
|
|
242
|
+
(el) => window.getComputedStyle(el).backgroundColor
|
|
243
|
+
)
|
|
244
|
+
expect(bg).toBeTruthy()
|
|
245
|
+
expect(bg).not.toBe('rgba(0, 0, 0, 0)')
|
|
246
|
+
})
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test.describe('function render prop', () => {
|
|
250
|
+
test('renders with custom component via function', async ({ page }) => {
|
|
251
|
+
const element = page.getByTestId('function-render')
|
|
252
|
+
await expect(element).toBeVisible()
|
|
253
|
+
|
|
254
|
+
// Should render as button (from CustomButton)
|
|
255
|
+
const tagName = await element.evaluate((el) => el.tagName.toLowerCase())
|
|
256
|
+
expect(tagName).toBe('button')
|
|
257
|
+
|
|
258
|
+
// Should have custom data attribute from CustomButton
|
|
259
|
+
const customAttr = await element.getAttribute('data-custom-button')
|
|
260
|
+
expect(customAttr).toBe('true')
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
test('function render receives state object', async ({ page }) => {
|
|
264
|
+
const element = page.getByTestId('function-render-with-state')
|
|
265
|
+
await expect(element).toBeVisible()
|
|
266
|
+
|
|
267
|
+
// Check that state object is passed - initial state should be false
|
|
268
|
+
// Note: CSS driver doesn't track hover/press state - it uses CSS :hover
|
|
269
|
+
// The state object is still passed but hover/press may not update with CSS driver
|
|
270
|
+
const hoverAttr = await element.getAttribute('data-hover')
|
|
271
|
+
const pressAttr = await element.getAttribute('data-press')
|
|
272
|
+
|
|
273
|
+
// Should have the data attributes (state is passed to render function)
|
|
274
|
+
expect(hoverAttr).toBeDefined()
|
|
275
|
+
expect(pressAttr).toBeDefined()
|
|
276
|
+
|
|
277
|
+
// Initial values should be 'false'
|
|
278
|
+
expect(hoverAttr).toBe('false')
|
|
279
|
+
expect(pressAttr).toBe('false')
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
test('function render has styles from Stack', async ({ page }) => {
|
|
283
|
+
const element = page.getByTestId('function-render')
|
|
284
|
+
await expect(element).toBeVisible()
|
|
285
|
+
|
|
286
|
+
const bg = await element.evaluate(
|
|
287
|
+
(el) => window.getComputedStyle(el).backgroundColor
|
|
288
|
+
)
|
|
289
|
+
expect(bg).toBeTruthy()
|
|
290
|
+
expect(bg).not.toBe('rgba(0, 0, 0, 0)')
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
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: 'ScrollViewRefCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test(`ScrollView ref has scrollTo method`, async ({ page }) => {
|
|
10
|
+
const status = page.locator('#status')
|
|
11
|
+
await expect(status).toHaveText('ready')
|
|
12
|
+
|
|
13
|
+
await page.click('#scroll-to-btn')
|
|
14
|
+
await expect(status).toHaveText('scrolled-to-200')
|
|
15
|
+
|
|
16
|
+
// verify the scroll position changed
|
|
17
|
+
const scrollTop = await page.locator('#test-scrollview').evaluate((el) => el.scrollTop)
|
|
18
|
+
expect(scrollTop).toBe(200)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test(`ScrollView ref has scrollToEnd method`, async ({ page }) => {
|
|
22
|
+
const status = page.locator('#status')
|
|
23
|
+
await expect(status).toHaveText('ready')
|
|
24
|
+
|
|
25
|
+
await page.click('#scroll-to-end-btn')
|
|
26
|
+
await expect(status).toHaveText('scrolled-to-end')
|
|
27
|
+
|
|
28
|
+
// verify it scrolled to the end (scrollTop should be > 0)
|
|
29
|
+
const scrollTop = await page.locator('#test-scrollview').evaluate((el) => el.scrollTop)
|
|
30
|
+
expect(scrollTop).toBeGreaterThan(0)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test(`ScrollView ref has getScrollableNode method`, async ({ page }) => {
|
|
34
|
+
const status = page.locator('#status')
|
|
35
|
+
await expect(status).toHaveText('ready')
|
|
36
|
+
|
|
37
|
+
await page.click('#get-node-btn')
|
|
38
|
+
await expect(status).toHaveText('got-scrollable-node')
|
|
39
|
+
})
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests for Select click-and-hold-to-select behavior.
|
|
6
|
+
*
|
|
7
|
+
* This behavior allows users to:
|
|
8
|
+
* 1. Press and hold on the select trigger
|
|
9
|
+
* 2. Drag to an item while holding
|
|
10
|
+
* 3. Release to select that item
|
|
11
|
+
*
|
|
12
|
+
* This is a common native select pattern that was broken by Safari fixes.
|
|
13
|
+
*/
|
|
14
|
+
test.describe('Select Click and Hold', () => {
|
|
15
|
+
test.beforeEach(async ({ page }) => {
|
|
16
|
+
await setupPage(page, { name: 'SelectFocusScopeCase', type: 'useCase' })
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('click-and-hold-to-select works', async ({ page }) => {
|
|
20
|
+
const trigger = page.getByTestId('basic-select-trigger')
|
|
21
|
+
const triggerBox = await trigger.boundingBox()
|
|
22
|
+
expect(triggerBox).toBeTruthy()
|
|
23
|
+
|
|
24
|
+
// Get position of banana item (need to open first to measure)
|
|
25
|
+
await trigger.click()
|
|
26
|
+
await page.waitForTimeout(400) // Wait for menu to open and 300ms guard to pass
|
|
27
|
+
|
|
28
|
+
const bananaItem = page.getByTestId('select-banana')
|
|
29
|
+
await expect(bananaItem).toBeVisible()
|
|
30
|
+
const bananaBox = await bananaItem.boundingBox()
|
|
31
|
+
expect(bananaBox).toBeTruthy()
|
|
32
|
+
|
|
33
|
+
// Close the menu
|
|
34
|
+
await page.keyboard.press('Escape')
|
|
35
|
+
await page.waitForTimeout(200)
|
|
36
|
+
|
|
37
|
+
// Now perform click-and-hold-to-select:
|
|
38
|
+
// 1. Mouse down on trigger
|
|
39
|
+
// 2. Wait for menu to open (> 300ms to pass guard)
|
|
40
|
+
// 3. Move to banana item
|
|
41
|
+
// 4. Release to select
|
|
42
|
+
|
|
43
|
+
await page.mouse.move(
|
|
44
|
+
triggerBox!.x + triggerBox!.width / 2,
|
|
45
|
+
triggerBox!.y + triggerBox!.height / 2
|
|
46
|
+
)
|
|
47
|
+
await page.mouse.down()
|
|
48
|
+
|
|
49
|
+
// Wait for menu to open and the 300ms mouseUp guard to pass
|
|
50
|
+
await page.waitForTimeout(400)
|
|
51
|
+
|
|
52
|
+
// Menu should be open
|
|
53
|
+
await expect(bananaItem).toBeVisible()
|
|
54
|
+
|
|
55
|
+
// Move to banana item
|
|
56
|
+
await page.mouse.move(
|
|
57
|
+
bananaBox!.x + bananaBox!.width / 2,
|
|
58
|
+
bananaBox!.y + bananaBox!.height / 2
|
|
59
|
+
)
|
|
60
|
+
await page.waitForTimeout(50)
|
|
61
|
+
|
|
62
|
+
// Release to select
|
|
63
|
+
await page.mouse.up()
|
|
64
|
+
await page.waitForTimeout(200)
|
|
65
|
+
|
|
66
|
+
// Menu should close and banana should be selected
|
|
67
|
+
const selectViewport = page.getByTestId('basic-select-viewport')
|
|
68
|
+
await expect(selectViewport).not.toBeVisible()
|
|
69
|
+
|
|
70
|
+
// Trigger should show "Banana"
|
|
71
|
+
const triggerText = await trigger.textContent()
|
|
72
|
+
expect(triggerText).toContain('Banana')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('quick click does not select item under cursor (Safari fix)', async ({ page }) => {
|
|
76
|
+
// This tests that the Safari fix still works - a quick click-release
|
|
77
|
+
// should NOT select an item even if the cursor happens to be over one
|
|
78
|
+
|
|
79
|
+
const trigger = page.getByTestId('basic-select-trigger')
|
|
80
|
+
const triggerBox = await trigger.boundingBox()
|
|
81
|
+
expect(triggerBox).toBeTruthy()
|
|
82
|
+
|
|
83
|
+
// Quick click to open
|
|
84
|
+
await trigger.click()
|
|
85
|
+
|
|
86
|
+
// Wait a moment for menu to open but NOT long enough for the guard to pass
|
|
87
|
+
await page.waitForTimeout(100)
|
|
88
|
+
|
|
89
|
+
const selectViewport = page.getByTestId('basic-select-viewport')
|
|
90
|
+
await expect(selectViewport).toBeVisible()
|
|
91
|
+
|
|
92
|
+
// The menu should be open and nothing should be selected yet
|
|
93
|
+
// (The initial click's mouseUp is blocked)
|
|
94
|
+
const triggerText = await trigger.textContent()
|
|
95
|
+
expect(triggerText).not.toContain('Apple')
|
|
96
|
+
expect(triggerText).not.toContain('Banana')
|
|
97
|
+
|
|
98
|
+
// Close menu
|
|
99
|
+
await page.keyboard.press('Escape')
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('click-and-release-too-fast does not select', async ({ page }) => {
|
|
103
|
+
// If user clicks and releases before the 300ms guard passes,
|
|
104
|
+
// nothing should be selected even if mouse is over an item
|
|
105
|
+
|
|
106
|
+
const trigger = page.getByTestId('basic-select-trigger')
|
|
107
|
+
const triggerBox = await trigger.boundingBox()
|
|
108
|
+
expect(triggerBox).toBeTruthy()
|
|
109
|
+
|
|
110
|
+
// First, open and get item positions
|
|
111
|
+
await trigger.click()
|
|
112
|
+
await page.waitForTimeout(400)
|
|
113
|
+
|
|
114
|
+
const appleItem = page.getByTestId('select-apple')
|
|
115
|
+
await expect(appleItem).toBeVisible()
|
|
116
|
+
const appleBox = await appleItem.boundingBox()
|
|
117
|
+
expect(appleBox).toBeTruthy()
|
|
118
|
+
|
|
119
|
+
// Close
|
|
120
|
+
await page.keyboard.press('Escape')
|
|
121
|
+
await page.waitForTimeout(200)
|
|
122
|
+
|
|
123
|
+
// Now do a fast click-move-release (under 300ms)
|
|
124
|
+
await page.mouse.move(
|
|
125
|
+
triggerBox!.x + triggerBox!.width / 2,
|
|
126
|
+
triggerBox!.y + triggerBox!.height / 2
|
|
127
|
+
)
|
|
128
|
+
await page.mouse.down()
|
|
129
|
+
|
|
130
|
+
// Wait just a tiny bit - not enough for the guard
|
|
131
|
+
await page.waitForTimeout(50)
|
|
132
|
+
|
|
133
|
+
// Move toward where apple would be and release quickly
|
|
134
|
+
await page.mouse.move(
|
|
135
|
+
appleBox!.x + appleBox!.width / 2,
|
|
136
|
+
appleBox!.y + appleBox!.height / 2
|
|
137
|
+
)
|
|
138
|
+
await page.mouse.up()
|
|
139
|
+
|
|
140
|
+
await page.waitForTimeout(200)
|
|
141
|
+
|
|
142
|
+
// The menu might still be open (that's fine) but nothing should be selected
|
|
143
|
+
const triggerText = await trigger.textContent()
|
|
144
|
+
expect(triggerText).not.toContain('Apple')
|
|
145
|
+
expect(triggerText).not.toContain('Banana')
|
|
146
|
+
})
|
|
147
|
+
})
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test'
|
|
2
|
+
import { setupPage } from './test-utils'
|
|
3
|
+
|
|
4
|
+
test.describe('Select Focus Scope', () => {
|
|
5
|
+
test.beforeEach(async ({ page }) => {
|
|
6
|
+
await setupPage(page, { name: 'SelectFocusScopeCase', type: 'useCase' })
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
test('traps focus within select dropdown when open', async ({ page }) => {
|
|
10
|
+
await page.waitForLoadState('networkidle')
|
|
11
|
+
|
|
12
|
+
// open the basic select
|
|
13
|
+
const trigger = page.getByTestId('basic-select-trigger')
|
|
14
|
+
await trigger.click()
|
|
15
|
+
|
|
16
|
+
// wait for select viewport to be visible
|
|
17
|
+
const selectViewport = page.getByTestId('basic-select-viewport')
|
|
18
|
+
await expect(selectViewport).toBeVisible({ timeout: 5000 })
|
|
19
|
+
|
|
20
|
+
// wait for focus trap to settle (rAF-based)
|
|
21
|
+
await page.waitForTimeout(50)
|
|
22
|
+
|
|
23
|
+
// first item should be focused
|
|
24
|
+
const firstItem = page.getByTestId('select-apple')
|
|
25
|
+
await expect(firstItem).toBeFocused()
|
|
26
|
+
|
|
27
|
+
// arrow down to second item
|
|
28
|
+
await page.keyboard.press('ArrowDown')
|
|
29
|
+
await page.waitForTimeout(50)
|
|
30
|
+
|
|
31
|
+
const secondItem = page.getByTestId('select-banana')
|
|
32
|
+
await expect(secondItem).toBeFocused()
|
|
33
|
+
|
|
34
|
+
// enter selects the item and closes the select
|
|
35
|
+
await page.keyboard.press('Enter')
|
|
36
|
+
await page.waitForTimeout(100)
|
|
37
|
+
|
|
38
|
+
// select should be closed after enter
|
|
39
|
+
await expect(selectViewport).not.toBeVisible()
|
|
40
|
+
|
|
41
|
+
// focus should return to the trigger that opened it
|
|
42
|
+
await expect(trigger).toBeFocused()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('allows selection with Enter key', async ({ page }) => {
|
|
46
|
+
await page.waitForLoadState('networkidle')
|
|
47
|
+
|
|
48
|
+
const trigger = page.getByTestId('basic-select-trigger')
|
|
49
|
+
await trigger.click()
|
|
50
|
+
|
|
51
|
+
const selectViewport = page.getByTestId('basic-select-viewport')
|
|
52
|
+
await expect(selectViewport).toBeVisible({ timeout: 5000 })
|
|
53
|
+
await page.waitForTimeout(300)
|
|
54
|
+
|
|
55
|
+
// apple is focused initially, navigate to banana
|
|
56
|
+
await page.keyboard.press('ArrowDown')
|
|
57
|
+
await page.waitForTimeout(100)
|
|
58
|
+
|
|
59
|
+
// Select with Enter
|
|
60
|
+
await page.keyboard.press('Enter')
|
|
61
|
+
await page.waitForTimeout(200)
|
|
62
|
+
|
|
63
|
+
// Select should close
|
|
64
|
+
await expect(selectViewport).not.toBeVisible()
|
|
65
|
+
|
|
66
|
+
// Trigger should show selected value
|
|
67
|
+
const triggerText = await trigger.textContent()
|
|
68
|
+
expect(triggerText).toContain('Banana')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('allows selection with click', async ({ page }) => {
|
|
72
|
+
await page.waitForLoadState('networkidle')
|
|
73
|
+
|
|
74
|
+
const trigger = page.getByTestId('custom-select-trigger')
|
|
75
|
+
await trigger.click()
|
|
76
|
+
|
|
77
|
+
// Wait for any select viewport to be visible
|
|
78
|
+
const selectViewport = page.getByRole('listbox').first()
|
|
79
|
+
await expect(selectViewport).toBeVisible({ timeout: 5000 })
|
|
80
|
+
|
|
81
|
+
// Click on green option
|
|
82
|
+
const greenOption = page.getByTestId('select-green')
|
|
83
|
+
await greenOption.click()
|
|
84
|
+
|
|
85
|
+
// Select should close
|
|
86
|
+
await expect(selectViewport).not.toBeVisible()
|
|
87
|
+
|
|
88
|
+
// Trigger should show selected value
|
|
89
|
+
const triggerText = await trigger.textContent()
|
|
90
|
+
expect(triggerText).toContain('Green')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('closes on Escape', async ({ page }) => {
|
|
94
|
+
await page.waitForLoadState('networkidle')
|
|
95
|
+
|
|
96
|
+
const trigger = page.getByTestId('small-select-trigger')
|
|
97
|
+
await trigger.click()
|
|
98
|
+
|
|
99
|
+
// Wait for any select viewport to be visible
|
|
100
|
+
const selectViewport = page.getByRole('listbox').first()
|
|
101
|
+
await expect(selectViewport).toBeVisible({ timeout: 5000 })
|
|
102
|
+
|
|
103
|
+
// Press Escape to close
|
|
104
|
+
await page.keyboard.press('Escape')
|
|
105
|
+
await page.waitForTimeout(200)
|
|
106
|
+
|
|
107
|
+
// Select should close
|
|
108
|
+
await expect(selectViewport).not.toBeVisible()
|
|
109
|
+
|
|
110
|
+
// Note: Select doesn't automatically restore focus to trigger like Dialog/Popover do
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('Escape closes select and returns focus to trigger', async ({ page }) => {
|
|
114
|
+
await page.waitForLoadState('networkidle')
|
|
115
|
+
|
|
116
|
+
// Open the small select
|
|
117
|
+
const trigger = page.getByTestId('small-select-trigger')
|
|
118
|
+
await trigger.click()
|
|
119
|
+
|
|
120
|
+
// Wait for any select viewport to be visible
|
|
121
|
+
const selectViewport = page.getByRole('listbox').first()
|
|
122
|
+
await expect(selectViewport).toBeVisible({ timeout: 5000 })
|
|
123
|
+
await page.waitForTimeout(300)
|
|
124
|
+
|
|
125
|
+
// Escape closes the select
|
|
126
|
+
await page.keyboard.press('Escape')
|
|
127
|
+
await page.waitForTimeout(200)
|
|
128
|
+
|
|
129
|
+
// Select should be closed after Escape
|
|
130
|
+
await expect(selectViewport).not.toBeVisible()
|
|
131
|
+
|
|
132
|
+
// focus should return to the trigger
|
|
133
|
+
await expect(trigger).toBeFocused()
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('handles arrow key navigation correctly', async ({ page }) => {
|
|
137
|
+
await page.waitForLoadState('networkidle')
|
|
138
|
+
|
|
139
|
+
const trigger = page.getByTestId('basic-select-trigger')
|
|
140
|
+
await trigger.click()
|
|
141
|
+
|
|
142
|
+
const selectViewport = page.getByTestId('basic-select-viewport')
|
|
143
|
+
await expect(selectViewport).toBeVisible({ timeout: 5000 })
|
|
144
|
+
await page.waitForTimeout(300)
|
|
145
|
+
|
|
146
|
+
// first item (apple) is focused on open
|
|
147
|
+
const apple = page.getByTestId('select-apple')
|
|
148
|
+
const isAppleFocused = await apple.evaluate((el) => el === document.activeElement)
|
|
149
|
+
expect(isAppleFocused).toBe(true)
|
|
150
|
+
|
|
151
|
+
// Navigate down through all items
|
|
152
|
+
await page.keyboard.press('ArrowDown') // banana
|
|
153
|
+
await page.waitForTimeout(50)
|
|
154
|
+
await page.keyboard.press('ArrowDown') // orange
|
|
155
|
+
await page.waitForTimeout(50)
|
|
156
|
+
await page.keyboard.press('ArrowDown') // carrot
|
|
157
|
+
await page.waitForTimeout(50)
|
|
158
|
+
await page.keyboard.press('ArrowDown') // broccoli
|
|
159
|
+
await page.waitForTimeout(50)
|
|
160
|
+
|
|
161
|
+
// Should be at broccoli now
|
|
162
|
+
const broccoli = page.getByTestId('select-broccoli')
|
|
163
|
+
const isBroccoliFocused = await broccoli.evaluate(
|
|
164
|
+
(el) => el === document.activeElement
|
|
165
|
+
)
|
|
166
|
+
expect(isBroccoliFocused).toBe(true)
|
|
167
|
+
|
|
168
|
+
// Navigate up
|
|
169
|
+
await page.keyboard.press('ArrowUp')
|
|
170
|
+
await page.waitForTimeout(50)
|
|
171
|
+
|
|
172
|
+
const carrot = page.getByTestId('select-carrot')
|
|
173
|
+
const isCarrotFocused = await carrot.evaluate((el) => el === document.activeElement)
|
|
174
|
+
expect(isCarrotFocused).toBe(true)
|
|
175
|
+
})
|
|
176
|
+
})
|