Skip to content

Commit cb83d4f

Browse files
authored
fix(menu): prevent click propagation to container components (#703)
1 parent 7b90d2e commit cb83d4f

File tree

2 files changed

+26
-3
lines changed

2 files changed

+26
-3
lines changed

packages/menu/src/MenuContainer.spec.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,23 @@ describe('MenuContainer', () => {
251251
expect(menu).not.toBeVisible();
252252
});
253253

254+
it('prevents menu click propagation', async () => {
255+
const handleClick = jest.fn();
256+
const { getByTestId } = render(
257+
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
258+
<div onClick={handleClick}>
259+
<TestMenu items={ITEMS} />
260+
</div>
261+
);
262+
const menu = getByTestId('menu');
263+
264+
await act(async () => {
265+
await user.click(menu);
266+
});
267+
268+
expect(handleClick).not.toHaveBeenCalled();
269+
});
270+
254271
it('closes the menu on blur due to a body click, returns focus to the trigger the first time, and focuses the body on following clicks.', async () => {
255272
const { getByTestId } = render(<TestMenu items={ITEMS} />);
256273
const trigger = getByTestId('trigger');

packages/menu/src/useMenu.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,11 @@ export const useMenu = <T extends HTMLElement = HTMLElement, M extends HTMLEleme
365365
]
366366
);
367367

368+
const handleMenuClick = useCallback((event: React.MouseEvent) => {
369+
// Prevent propagation to container; for example, accordion header expand/collapse
370+
event.stopPropagation();
371+
}, []);
372+
368373
const handleMenuKeyDown = useCallback(
369374
(event: KeyboardEvent) => {
370375
const { key } = event;
@@ -776,7 +781,7 @@ export const useMenu = <T extends HTMLElement = HTMLElement, M extends HTMLEleme
776781
);
777782

778783
const getMenuProps = useCallback<IUseMenuReturnValue['getMenuProps']>(
779-
({ role = 'menu', onBlur, onMouseLeave, ...other } = {}) => ({
784+
({ role = 'menu', onBlur, onClick, onMouseLeave, ...other } = {}) => ({
780785
...other,
781786
...getGroupProps({
782787
onMouseLeave: composeEventHandlers(onMouseLeave, handleMenuMouseLeave)
@@ -787,9 +792,10 @@ export const useMenu = <T extends HTMLElement = HTMLElement, M extends HTMLEleme
787792
tabIndex: -1,
788793
role: role === null ? undefined : role,
789794
ref: menuRef as any,
790-
onBlur: composeEventHandlers(onBlur, handleBlur)
795+
onBlur: composeEventHandlers(onBlur, handleBlur),
796+
onClick: composeEventHandlers(onClick, handleMenuClick)
791797
}),
792-
[getGroupProps, handleBlur, handleMenuMouseLeave, menuRef, triggerId]
798+
[getGroupProps, handleBlur, handleMenuClick, handleMenuMouseLeave, menuRef, triggerId]
793799
);
794800

795801
const getSeparatorProps = useCallback<IUseMenuReturnValue['getSeparatorProps']>(

0 commit comments

Comments
 (0)