From 199bf8908dd74e80549c6f1e8c21c5368c8e82af Mon Sep 17 00:00:00 2001 From: atomiks Date: Thu, 23 Jan 2025 18:45:19 +1100 Subject: [PATCH 1/4] [Menu, Popover, PreviewCard] Set pointer events style on backdrops when hoverable --- packages/react/src/menu/backdrop/MenuBackdrop.tsx | 9 +++++++-- packages/react/src/popover/backdrop/PopoverBackdrop.tsx | 9 +++++++-- .../src/preview-card/backdrop/PreviewCardBackdrop.tsx | 9 ++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/packages/react/src/menu/backdrop/MenuBackdrop.tsx b/packages/react/src/menu/backdrop/MenuBackdrop.tsx index 0f6671d963..b7c7b19fdf 100644 --- a/packages/react/src/menu/backdrop/MenuBackdrop.tsx +++ b/packages/react/src/menu/backdrop/MenuBackdrop.tsx @@ -8,6 +8,7 @@ import { type CustomStyleHookMapping } from '../../utils/getStyleHookProps'; import { popupStateMapping as baseMapping } from '../../utils/popupStateMapping'; import type { TransitionStatus } from '../../utils/useTransitionStatus'; import { transitionStatusMapping } from '../../utils/styleHookMapping'; +import { mergeReactProps } from '../../utils/mergeReactProps'; const customStyleHookMapping: CustomStyleHookMapping = { ...baseMapping, @@ -25,7 +26,7 @@ const MenuBackdrop = React.forwardRef(function MenuBackdrop( forwardedRef: React.ForwardedRef, ) { const { className, render, ...other } = props; - const { open, mounted, transitionStatus } = useMenuRootContext(); + const { open, mounted, transitionStatus, openReason } = useMenuRootContext(); const state: MenuBackdrop.State = React.useMemo( () => ({ @@ -40,7 +41,11 @@ const MenuBackdrop = React.forwardRef(function MenuBackdrop( className, state, ref: forwardedRef, - extraProps: { role: 'presentation', hidden: !mounted, ...other }, + extraProps: mergeReactProps<'div'>(other, { + role: 'presentation', + hidden: !mounted, + style: openReason === 'hover' ? { pointerEvents: 'none' } : {}, + }), customStyleHookMapping, }); diff --git a/packages/react/src/popover/backdrop/PopoverBackdrop.tsx b/packages/react/src/popover/backdrop/PopoverBackdrop.tsx index 1f2128bc10..441b8ab173 100644 --- a/packages/react/src/popover/backdrop/PopoverBackdrop.tsx +++ b/packages/react/src/popover/backdrop/PopoverBackdrop.tsx @@ -8,6 +8,7 @@ import { type CustomStyleHookMapping } from '../../utils/getStyleHookProps'; import { popupStateMapping as baseMapping } from '../../utils/popupStateMapping'; import type { TransitionStatus } from '../../utils/useTransitionStatus'; import { transitionStatusMapping } from '../../utils/styleHookMapping'; +import { mergeReactProps } from '../../utils/mergeReactProps'; const customStyleHookMapping: CustomStyleHookMapping = { ...baseMapping, @@ -26,7 +27,7 @@ const PopoverBackdrop = React.forwardRef(function PopoverBackdrop( ) { const { className, render, ...other } = props; - const { open, mounted, transitionStatus } = usePopoverRootContext(); + const { open, mounted, transitionStatus, openReason } = usePopoverRootContext(); const state: PopoverBackdrop.State = React.useMemo( () => ({ @@ -41,7 +42,11 @@ const PopoverBackdrop = React.forwardRef(function PopoverBackdrop( className, state, ref: forwardedRef, - extraProps: { role: 'presentation', hidden: !mounted, ...other }, + extraProps: mergeReactProps<'div'>(other, { + role: 'presentation', + hidden: !mounted, + style: openReason === 'hover' ? { pointerEvents: 'none' } : {}, + }), customStyleHookMapping, }); diff --git a/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.tsx b/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.tsx index 947e2a658e..b61435ea29 100644 --- a/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.tsx +++ b/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.tsx @@ -8,6 +8,7 @@ import { type CustomStyleHookMapping } from '../../utils/getStyleHookProps'; import { popupStateMapping as baseMapping } from '../../utils/popupStateMapping'; import type { TransitionStatus } from '../../utils/useTransitionStatus'; import { transitionStatusMapping } from '../../utils/styleHookMapping'; +import { mergeReactProps } from '../../utils/mergeReactProps'; const customStyleHookMapping: CustomStyleHookMapping = { ...baseMapping, @@ -41,7 +42,13 @@ const PreviewCardBackdrop = React.forwardRef(function PreviewCardBackdrop( className, state, ref: forwardedRef, - extraProps: { role: 'presentation', hidden: !mounted, ...other }, + extraProps: mergeReactProps<'div'>(other, { + role: 'presentation', + hidden: !mounted, + style: { + pointerEvents: 'none', + }, + }), customStyleHookMapping, }); From b35c49db1a17ee8bcc0d59b775c223e150ff2c37 Mon Sep 17 00:00:00 2001 From: atomiks Date: Thu, 23 Jan 2025 19:06:09 +1100 Subject: [PATCH 2/4] Unit tests --- .../src/menu/backdrop/MenuBackdrop.test.tsx | 24 +++++++++++++++++++ .../popover/backdrop/PopoverBackdrop.test.tsx | 24 +++++++++++++++++++ .../backdrop/PreviewCardBackdrop.test.tsx | 19 +++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx b/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx index 820e55b32d..9221e8e502 100644 --- a/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx +++ b/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Menu } from '@base-ui-components/react/menu'; import { createRenderer, describeConformance } from '#test-utils'; +import { fireEvent, screen } from '@mui/internal-test-utils'; describe('', () => { const { render } = createRenderer(); @@ -11,4 +12,27 @@ describe('', () => { return render({node}); }, })); + + it('sets `pointer-events: none` style on backdrop if opened by hover', async () => { + const { user } = await render( + + Open + + + + + + + , + ); + + fireEvent.click(screen.getByText('Open')); + + expect(screen.getByTestId('backdrop').style.pointerEvents).not.to.equal('none'); + + fireEvent.click(screen.getByText('Open')); + await user.hover(screen.getByText('Open')); + + expect(screen.getByTestId('backdrop').style.pointerEvents).to.equal('none'); + }); }); diff --git a/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx b/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx index 5c9901b9a4..fd4cf84faf 100644 --- a/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx +++ b/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Popover } from '@base-ui-components/react/popover'; import { createRenderer, describeConformance } from '#test-utils'; +import { fireEvent, screen } from '@mui/internal-test-utils'; describe('', () => { const { render } = createRenderer(); @@ -11,4 +12,27 @@ describe('', () => { return render({node}); }, })); + + it.only('sets `pointer-events: none` style on backdrop if opened by hover', async () => { + const { user } = await render( + + Open + + + + + + + , + ); + + fireEvent.click(screen.getByText('Open')); + + expect(screen.getByTestId('backdrop').style.pointerEvents).not.to.equal('none'); + + fireEvent.click(screen.getByText('Open')); + await user.hover(screen.getByText('Open')); + + expect(screen.getByTestId('backdrop').style.pointerEvents).to.equal('none'); + }); }); diff --git a/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.test.tsx b/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.test.tsx index 419c1bfda7..0136a46b70 100644 --- a/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.test.tsx +++ b/packages/react/src/preview-card/backdrop/PreviewCardBackdrop.test.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { PreviewCard } from '@base-ui-components/react/preview-card'; +import { screen } from '@mui/internal-test-utils'; import { createRenderer, describeConformance } from '#test-utils'; describe('', () => { @@ -11,4 +12,22 @@ describe('', () => { return render({node}); }, })); + + it('sets `pointer-events: none` style', async () => { + const { user } = await render( + + Open + + + + + + + , + ); + + await user.hover(screen.getByText('Open')); + + expect(screen.getByTestId('backdrop').style.pointerEvents).to.equal('none'); + }); }); From e5c2791628df589196c8b782610f8a5d8a9c7dde Mon Sep 17 00:00:00 2001 From: atomiks Date: Fri, 24 Jan 2025 13:56:42 +1100 Subject: [PATCH 3/4] Fix test --- packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx b/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx index fd4cf84faf..6c2db5330d 100644 --- a/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx +++ b/packages/react/src/popover/backdrop/PopoverBackdrop.test.tsx @@ -13,7 +13,7 @@ describe('', () => { }, })); - it.only('sets `pointer-events: none` style on backdrop if opened by hover', async () => { + it('sets `pointer-events: none` style on backdrop if opened by hover', async () => { const { user } = await render( Open From 7e33258aa07bab7e57272b49b10d581fa8020912 Mon Sep 17 00:00:00 2001 From: atomiks Date: Fri, 24 Jan 2025 17:46:33 +1100 Subject: [PATCH 4/4] Split menu tests --- .../src/menu/backdrop/MenuBackdrop.test.tsx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx b/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx index 9221e8e502..04b84143f9 100644 --- a/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx +++ b/packages/react/src/menu/backdrop/MenuBackdrop.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Menu } from '@base-ui-components/react/menu'; import { createRenderer, describeConformance } from '#test-utils'; -import { fireEvent, screen } from '@mui/internal-test-utils'; +import { screen } from '@mui/internal-test-utils'; describe('', () => { const { render } = createRenderer(); @@ -26,13 +26,26 @@ describe('', () => { , ); - fireEvent.click(screen.getByText('Open')); - - expect(screen.getByTestId('backdrop').style.pointerEvents).not.to.equal('none'); - - fireEvent.click(screen.getByText('Open')); await user.hover(screen.getByText('Open')); expect(screen.getByTestId('backdrop').style.pointerEvents).to.equal('none'); }); + + it('does not set `pointer-events: none` style on backdrop if opened by click', async () => { + const { user } = await render( + + Open + + + + + + + , + ); + + await user.click(screen.getByText('Open')); + + expect(screen.getByTestId('backdrop').style.pointerEvents).not.to.equal('none'); + }); });