react-bootstrap / react-overlays

Utilities for creating robust overlay components
https://react-bootstrap.github.io/react-overlays
MIT License
897 stars 222 forks source link

Use parent overlay as default container for child overlays #1003

Open dleavitt opened 2 years ago

dleavitt commented 2 years ago

Is your feature request related to a problem? Please describe

Overlays, when not passed an explicit container prop, attach themselves to document.body. This doesn't work well if you nest overlays as the child overlay isn't actually inside the parent.

Specifically:

  1. If you've got an overlay with rootClose that contains another overlay, interacting with the inner overlay will close the outer overlay.
  2. If you have an overlay inside a modal you can't focus on the contents of the overlay, since they're attached to the root and outside the modal.

Repro (with bs5 react-bootstrap, also affects bs4): https://codesandbox.io/s/great-montalcini-s2yebc?file=/src/App.js

See:

Describe the solution you'd like

When an overlay is inside of another overlay, default its container to a child of the parent overlay instead of to document.body.

This would be accomplished with React context. Each overlay:

  1. Looks in context for a parent overlay ref and uses that as its default container if present.*
  2. Wraps its children in a provider that passes a ref to itself as the parent overlay ref to be used by its children.

*Except modals, which should always be attached to document.body.

Roughly like this:

const OverlayContext = createContext(null)

const Overlay = () => {
  // ... overlay stuff

  // grab parent ref from overlay context as the default container
  const containerRef = useContext<OverlayContext>()
  container ||= containerRef

  // ... more overlay stuff

  const overlayRef = useWaitForDOMRef(outerRef) // ish
  // inject ref of the overlay into context for any children
  <OverlayContext.Provider value={overlayRef}>
    {child}
  </OverlayContext.Provider>
}

Describe alternatives you've considered

Additional context

Perhaps you are wondering "what sort of monster would want to nest overlays like this?" A reasonable question! One specific place this has come up is inline UI for filtering a table. Here's an example of such an element from material UI:

Screen Shot 2022-05-18 at 13 06 19

Here we've got a popover-esque with controls inside of it. The controls may themselves have overlays (popovers or multi-pickers) or the popover and table may be inside a modal.