-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Bug]: Multiple/nested Dialog instances bugs #426
Comments
Hey! Thank you for your bug report! When you want nested Modals, then you should also nest it in the component tree. This allows us to create a stacking context so that when you press Dialogs have this feature where all other content is inert, meaning 2 open Dialogs at the same time is not possible when they are rendered as children. But the nesting should fix this. Here is an example: https://codesandbox.io/s/headlessui-dialog-multiple-modals-bugs-forked-o2ykb?file=/pages/index.js |
@RobinMalfait Interesting enough, your sandbox demo has a bug - where the first escape removes 2 dialogs. |
@RobinMalfait Is it the same for nested Popovers? I seem to be seeing a tabIndex bug where nested popovers tab 2 items for focus, including with the new But And |
How about Vue 3 nested Dialogs? |
@RobinMalfait Hi, I noticed that when the child modals should be hidden, you are simply not rendering them instead of passing the open prop. This however breaks transitions. Is there a way to achieve both? Thanks! |
I have a global fullscreen loading dialog, there are multi dialogs, but they are not nested. |
Hey all! Revisited this issue, and the main issue is that multiple open sibling Dialogs is not supported. You have 2 options:
For anyone running into issues, please open a new issue (if it doesn't already exist) and attach a minimal reproduction repo or codesandbox. |
@RobinMalfait Your example 1 from above breaks scrolling if you open modal 1, then open 2 from within 1, then close. The page is no longer scrollable. |
This is still and issue, I'm aware the library doesn't support this behavior, which I don't really see a reason why, but for the moment I come up with this solution: import { useEffect, useMemo } from 'react'
import { isServer } from '../utils/ssr'
import { useLatestValue } from './use-latest-value'
// Since headlessui/react Dialog does not support having nested opened dialogs
// Need to come up with a logic to close the dialogs in the order they are opened
let idCounter = 0
let listeners: ({
id: string
cb: (evt: KeyboardEvent) => void
})[] = []
export const useEscapeOverride = (
// Open/closed state
open: boolean | undefined,
// onClose function basically
listener: (evt: KeyboardEvent) => void,
_id?: string
) => {
// Get id for the listener
const id = useMemo(() => _id ?? `escape-override-${idCounter++}`, [_id])
// We can replace this for useMemo(() => listener, [listener])
const { current } = useLatestValue(listener)
useEffect(() => {
if (!open) return
const handleKeyDown = (evt: KeyboardEvent) => {
if (evt.code !== 'Escape') return
evt.preventDefault()
// Close the last opened modal
listeners[listeners.length - 1].cb(evt)
listeners.pop()
}
if (!isServer) {
// Add current event handler
if (!listeners.some(listener => listener.id === id)) {
listeners.push({ id, cb: current })
}
document.addEventListener('keydown', handleKeyDown)
}
return () => {
if (!isServer) {
document.removeEventListener('keydown', handleKeyDown)
// Remove current event handler if the component unmounts or "open" changes
listeners = listeners.filter((listener) => listener.id !== id)
}
}
}, [open])
} Usage: export const CustomModal = ({
open,
onClose,
children
}: ModalProps) => {
useEscapeOverride(open, onClose)
return (
// Pass an empty function to onClose, we don't want to trigger the onClose done by the Dialog automatically
<Dialog open={open} onClose={() => {}}>
{children}
</Dialog>
)
} |
I have such a scenario where there is a global dialog, such as a login or registration form, which can be triggered at any time, on the page layer, in the first-level dialog, or even in a second-level dialog. If I have to use component hierarchy to implement nesting, this global dialog component will be ubiquitous, leading to a lot of redundant code. Is there any solution for this situation? |
We also faced this issue recently due to modal being opened from a drawer which is also a modal. So we forced the body to be scrollable. But now the problem is the layout shift due to the right padding the lib adds whenever a modal is opened. Really hope they support nested modals. We end up using floating-ui |
@RobinMalfait Just have a query why would opening the opening a I have posted the question with versions and code snippet here: IssueLink CodeSandbox: CodeSandbox |
Can we have this in the documentation? I was trying to debug it for quite some time and just found out this solution. Maybe others can benefit from it. |
What package within Headless UI are you using?
@headlessui/react
What version of that package are you using?
1.0.0, 1.0.0-ef31bbe
What browser are you using?
Chrome MacOs, Chrome Android, Safari iOS
Reproduction repository
https://codesandbox.io/s/headlessui-dialog-multiple-modals-bugs-sq7sn?file=/pages/index.js
Describe your issue
First of all, thanks for great library!
In complex app sometimes content of dialog can open another one... I played around dialog component and find some issues:
FocusTrap
- can handle focus only ifautoFocusRef
provided (compare pressing"Open 1" -> "Open 2"
and"Open 1" -> "Open 3"
)The text was updated successfully, but these errors were encountered: