primitives icon indicating copy to clipboard operation
primitives copied to clipboard

[DismissableLayer] After clicking on element that stops propagation, 2 outside clicks are required to close the layer

Open jaknas opened this issue 1 year ago • 2 comments

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

CodeSandbox

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

jaknas avatar Mar 15 '24 16:03 jaknas

Same happens if using react-aria button inside a Popover.

ht-lovrozagar avatar Aug 20 '24 08:08 ht-lovrozagar

Same happens if using react-aria button inside a Popover.

Had that issue for years and ended up moving everything to react-aria-components.

theMosaad avatar Aug 20 '24 08:08 theMosaad

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>```

rang-ali avatar Jul 29 '25 08:07 rang-ali