Input fields inside react overlays doesnt work in modal

Viewed 575

I have modal screen (using react-bootstrap), on modal screen i have multiple overlays (popup menus) linked to items. These overlays has inputs, and when i click on input it immediately loses focus. I cant figure out whats wrong, because another one popup menu, that i have on normal screen, not modal, works fine. Tried to set autofocus, but it immediately loses too.

I wrote example, https://codesandbox.io/s/rkemy

I think it is somehow connected with popper, because bootstrap overlay uses it, dont know where to dig

2 Answers

The fix provided in the other response is a workaround that doesn't fix the real cause of the issue.

The issue is caused by an internal logic of the Modal component of react-overlay library that is a dependency library of react-bootstrap. Specifically, the issue is caused by code listed below

    const handleEnforceFocus = useEventCallback(() => {
      if (!enforceFocus || !isMounted() || !modal.isTopModal()) {
        return;
      }

      const currentActiveElement = activeElement();

      if (
        modal.dialog &&
        currentActiveElement &&
        !contains(modal.dialog, currentActiveElement)
      ) {
        modal.dialog.focus();
      }
    });

that enforce the focus on the first modal open as soon as that modal lose the focus, like when you move the focus on the input.

In order to solve the issue, you have to pass the enforceFocus={false} to your Modal component.

The documentation of the API can be found here: https://react-bootstrap.github.io/react-overlays/api/Modal#enforceFocus

As the docs says: Generally this should never be set to false as it makes the Modal less accessible to assistive technologies, like screen readers. but in your scenario this is a need to work properly.

Solution is to wrap Overlay in container:

import React from "react";
import { Overlay } from "react-bootstrap";
import { X } from "react-bootstrap-icons";

export const PopupMenuWrapper = (props) => {
  const { target, title, show, onClose, children } = props;
  const ref = React.useRef(null);

  return (
   <div ref={ref}>
    <Overlay
      container = {ref.current}
      target={target.current}
      show={show}
      placement="bottom-start"
      rootClose={true}
      onHide={onClose}
    >
    ...
   </div>
...
Related