diff --git a/packages/react/src/Popover/Popup/PopoverPopup.tsx b/packages/react/src/Popover/Popup/PopoverPopup.tsx index dd3281fca..64f6679a9 100644 --- a/packages/react/src/Popover/Popup/PopoverPopup.tsx +++ b/packages/react/src/Popover/Popup/PopoverPopup.tsx @@ -54,6 +54,7 @@ const PopoverPopup = React.forwardRef(function PopoverPopup( descriptionId, popupRef, mounted, + openReason, } = usePopoverRootContext(); const positioner = usePopoverPositionerContext(); @@ -91,7 +92,7 @@ const PopoverPopup = React.forwardRef(function PopoverPopup( diff --git a/packages/react/src/Popover/Root/PopoverRoot.test.tsx b/packages/react/src/Popover/Root/PopoverRoot.test.tsx index 0f47eff5f..06f3a1fc5 100644 --- a/packages/react/src/Popover/Root/PopoverRoot.test.tsx +++ b/packages/react/src/Popover/Root/PopoverRoot.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Popover } from '@base-ui-components/react/Popover'; -import { fireEvent, flushMicrotasks, screen, waitFor } from '@mui/internal-test-utils'; +import { act, fireEvent, flushMicrotasks, screen, waitFor } from '@mui/internal-test-utils'; import userEvent from '@testing-library/user-event'; import { expect } from 'chai'; import { spy } from 'sinon'; @@ -333,4 +333,29 @@ describe('', () => { { timeout: 1500 }, ); }); + + it('does not move focus to the popover when opened with hover', async () => { + await render( + + Toggle + + + Close + + + , + ); + + const toggle = screen.getByRole('button', { name: 'Toggle' }); + + act(() => toggle.focus()); + + await user.hover(toggle); + await flushMicrotasks(); + + const close = screen.getByRole('button', { name: 'Close' }); + + expect(close).not.to.equal(null); + expect(close).not.to.toHaveFocus(); + }); }); diff --git a/packages/react/src/Popover/Root/usePopoverRoot.ts b/packages/react/src/Popover/Root/usePopoverRoot.ts index bb69abb9f..157863b19 100644 --- a/packages/react/src/Popover/Root/usePopoverRoot.ts +++ b/packages/react/src/Popover/Root/usePopoverRoot.ts @@ -31,7 +31,6 @@ export function usePopoverRoot(params: usePopoverRoot.Parameters): usePopoverRoo open: externalOpen, onOpenChange: onOpenChangeProp = () => {}, defaultOpen = false, - keepMounted = false, delay, closeDelay, openOnHover = false, @@ -69,22 +68,21 @@ export function usePopoverRoot(params: usePopoverRoot.Parameters): usePopoverRoo (nextOpen: boolean, event?: Event, reason?: OpenChangeReason) => { onOpenChange(nextOpen, event, reason); setOpenUnwrapped(nextOpen); - if (!keepMounted && !nextOpen) { + + if (!nextOpen) { if (animated) { runOnceAnimationsFinish(() => { if (!openRef.current) { setMounted(false); + setOpenReason(null); } }); } else { setMounted(false); + setOpenReason(null); } - } - - if (nextOpen) { - setOpenReason(reason ?? null); } else { - setOpenReason(null); + setOpenReason(reason ?? null); } }, ); @@ -209,11 +207,6 @@ export namespace usePopoverRoot { * @default 0 */ closeDelay?: number; - /** - * Whether the popover popup element stays mounted in the DOM when closed. - * @default false - */ - keepMounted?: boolean; /** * Whether the popover can animate, adding animation-related attributes and allowing for exit * animations to play. Useful to disable in tests to remove async behavior.