dialog icon indicating copy to clipboard operation
dialog copied to clipboard

autoFocus input not working within dialog

Open SamKirkland opened this issue 3 years ago • 1 comments

When using <input autoFocus /> within a modal the input will not be focused because the modal captures focus. See library code examples or live examples

Appears to be due to focus capture implemented on first render https://github.com/react-component/dialog/blob/4f70824474b6988e79b8461341b37dce23ea19ff/src/Dialog/Content/Panel.tsx#L56

It might make sense to switch to dialog instead https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog as browser support is solid https://caniuse.com/?search=dialog

SamKirkland avatar Nov 16 '22 21:11 SamKirkland

This is a fairly big accessibility issue.

For posterity, W3C has documented (https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/dialog/) the correct UX as it pertains to focus when dialogs are opened. Which element to set focus to depends on the content of the modal and should be done as follows (this is me summarizing):

  • If there is one or more interactive element, namely a form field (eg. input, select), set focus to the first interactive element in the modal body.
  • If it is a confirmation modal with just an OK button, set focus to the OK button. This is for efficiency since most users will simply dismiss the dialog as soon as they have read the message.
  • If there is no interactive element and more than just an OK button, focus should be set to the first paragraph.

Ultimately, I've found there are two issues with rc-dialog:

  1. Auto-focusing an element doesn't work at all. This can be seen in the "ant-design" example which uses <input autoFocus /> which @SamKirkland has called out.
  2. If you auto-focus manually using the following code, this will allow focus to be set on an element of your choice, but it breaks focusTriggerAfterClose.
import React, { useRef, useEffect } from 'react';

function MyComponent() {
  const paragraphRef = useRef(null);

  useEffect(() => {
    if (paragraphRef.current) {
      paragraphRef.current.focus();
    }
  }, []);

  return (
    <p ref={paragraphRef} tabIndex="-1">
      This paragraph will be focused on mount.
    </p>
  );
}

export default MyComponent;

mellis481 avatar Feb 21 '25 16:02 mellis481