[DismissableLayer] After clicking on element that stops propagation, 2 outside clicks are required to close the layer
Bug report
Current Behavior
I'm using a Radix Popover that internally uses DismissableLayer. Inside of that Popover I have buttons that call event.stopPropagation() in their onPointerDown handler. After clicking on such a button and trying to exit the Popover relying on onPointerDownOutside, it requires 2 clicks instead of 1.
Expected behavior
I'd expect the Popover to close using a single click outside regardless if my content inside is stopping propagation.
Reproducible example
Suggested solution
The issue seems to come from the fact that after the 1st click the isPointerInsideReactTreeRef ref is being set to true in the onPointerDownCapture handler. During the same event, we should expect the handlePointerDown function to be called and set the ref to false. This ultimately does not happen because the event does not bubble up to the pointerdown listener.
I don't really have an idea how this could be fixed.
Additional context
For context, I discovered this when trying to combine Radix Popover with React-Aria Calendar inside. Related: https://github.com/adobe/react-spectrum/issues/5799#issuecomment-1936132882
Your environment
| Software | Name(s) | Version |
|---|---|---|
| Radix Package(s) | popover | latest |
| React | n/a | latest |
| Browser | Chrome | 122 |
| Assistive tech | ||
| Node | n/a | |
| npm/yarn | ||
| Operating System | MacOS | Sonoma 14.4 |
Same happens if using react-aria button inside a Popover.
Same happens if using react-aria button inside a Popover.
Had that issue for years and ended up moving everything to react-aria-components.
I solved it by dispatching a synthetic event.
const { onPointerDown, buttonCellProps } = cellProps // button props from react-aria
// ...
<div ref={parentRef}>
<div
{...buttonCellProps}
onPointerDown={(event) => {
onPointerDown?.(event)
parentRef.current?.dispatchEvent(
new PointerEvent('pointerdown', {
pointerId: event.pointerId,
bubbles: true,
cancelable: true,
clientX: event.clientX,
clientY: event.clientY,
button: event.button,
buttons: event.buttons,
})
)
}}>
{/* ... */}
</div>
</div>```