Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -883,9 +883,18 @@ export const PopupMenuSubmenuTrigger = React.forwardRef<
(event: React.PointerEvent<HTMLDivElement>) => {
// Prevent focus from leaving the input
event.preventDefault()

if (open) {
suppressAutoOpenRef.current = true
logAimTrace('pointerdown-suppress-auto-open', {
clientX: event.clientX,
clientY: event.clientY,
})
}

onPointerDown?.(event)
},
[onPointerDown],
[open, logAimTrace, onPointerDown],
)

// Custom pointer move handler for submenu triggers
Expand Down Expand Up @@ -928,6 +937,14 @@ export const PopupMenuSubmenuTrigger = React.forwardRef<
return
}

if (suppressAutoOpenRef.current) {
logAimTrace('pointermove-open-suppressed-after-explicit-close', {
clientX: event.clientX,
clientY: event.clientY,
})
return
}

clearLeaveMonitor()
clearCloseTimer()

Expand Down Expand Up @@ -957,6 +974,7 @@ export const PopupMenuSubmenuTrigger = React.forwardRef<
item.storeId,
openOnHighlight,
open,
suppressAutoOpenRef,
clearLeaveMonitor,
clearCloseTimer,
delay.pointer,
Expand Down Expand Up @@ -1004,6 +1022,14 @@ export const PopupMenuSubmenuTrigger = React.forwardRef<
// Skip submenu opening if openOnHighlight is disabled
if (!openOnHighlight) return

if (suppressAutoOpenRef.current) {
logAimTrace('pointerenter-open-suppressed-after-explicit-close', {
clientX: event.clientX,
clientY: event.clientY,
})
return
}

// Clear any existing aim guard and schedule open with delay
clearLeaveMonitor()
clearAimGuard()
Expand All @@ -1029,6 +1055,7 @@ export const PopupMenuSubmenuTrigger = React.forwardRef<
item.storeId,
parentStore,
openOnHighlight,
suppressAutoOpenRef,
clearLeaveMonitor,
clearAimGuard,
clearOpenTimer,
Expand Down
40 changes: 40 additions & 0 deletions packages/react/src/internal/popup-menu/popup-menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,46 @@ describe('PopupMenu', () => {
scenario.cleanup()
}
})

it('does not reopen submenu on pointermove after explicit click close until pointer leaves', async () => {
const user = userEvent.setup()
render(<NestedMenuForDataAttrs />)

await user.click(screen.getByTestId('trigger'))

await waitFor(() => {
expect(screen.getByTestId('popup-root')).toBeInTheDocument()
})

const submenuTrigger = screen.getByTestId('submenu-trigger-1')

await user.hover(submenuTrigger)

await waitFor(() => {
expect(screen.getByTestId('popup-submenu-1')).toBeInTheDocument()
})

await user.click(submenuTrigger)

await waitFor(() => {
expect(screen.queryByTestId('popup-submenu-1')).not.toBeInTheDocument()
})

fireEvent.pointerMove(submenuTrigger, { clientX: 182, clientY: 92 })
fireEvent.pointerMove(submenuTrigger, { clientX: 186, clientY: 94 })

await sleep(120)

expect(screen.queryByTestId('popup-submenu-1')).not.toBeInTheDocument()

const rootItem = screen.getByTestId('root-item-1')
await user.hover(rootItem)
await user.hover(submenuTrigger)

await waitFor(() => {
expect(screen.getByTestId('popup-submenu-1')).toBeInTheDocument()
})
})
})

describe('search and filtering', () => {
Expand Down
Loading