radix-ui / primitives

Radix Primitives is an open-source UI component library for building high-quality, accessible design systems and web apps. Maintained by @workos.
https://radix-ui.com/primitives
MIT License
15.44k stars 776 forks source link

The element in dialog lost focus state. #2436

Open taoliujun opened 11 months ago

taoliujun commented 11 months ago

Bug report

Current Behavior

There is an input element A comes with a custom clear icon in a dialog component, and in its focusout event, the focus behavior of another input element B in the dialog component is triggered. It is found that the trigger failed and the cursor does not enter the B element.

Expected behavior

The cursor enters the B element.

Reproducible example

Suggested solution

I looked at the context source code of the dialog component and read the following in the focus-scope file:

// When the focused element gets removed from the DOM, browsers move focus
// back to the document.body. In this case, we move focus to the container
// to keep focus trapped correctly.
function handleMutations(mutations: MutationRecord[]) {
  const focusedElement = document.activeElement as HTMLElement | null;
  if (focusedElement !== document.body) return;
  for (const mutation of mutations) {
    if (mutation.removedNodes.length > 0) focus(container);
  }
}

With the MutationObserver feature, if an element is removed (the input A lost focus, the clear icon is removed), then the focus will be placed on the container (react-dialog root) node, cause the focus of the element B to be lost immediately.

Additional context

Your environment

Software Name(s) Version
Radix Package(s)
React n/a
Browser
Assistive tech
Node n/a
npm/yarn
Operating System
enkot commented 10 months ago

RadixVue also has this issue, it uses the same logic with MutationObserver - https://github.com/radix-vue/radix-vue/issues/518

benoitgrelard commented 6 months ago

Hi @taoliujun could you provide a reproduction in a sandbox?

BenLorantfy commented 1 week ago

Hey @benoitgrelard, I fixed this issue and created a PR: https://github.com/radix-ui/primitives/pull/3115

The PR contains a reproduction as a storybook story / cypress test

Also please note the vue fork fixed this already here with a similar fix: https://github.com/radix-vue/radix-vue/pull/519