ui
ui copied to clipboard
Page becomes unresponsive after closing a modal
I want a dropdown-menu
with two options: delete and edit, When the user selects an option, a popover
will show to confirm his selection.
I know we shouldn't put the dialog inside the dropdown menu other wise the popover will not even be vissible on the page, but I also had another issue: the page becomes unresponsive after a modal is closed. This is a video of the issue; the code is copied from the examples/playground
page, where it functions properly.
i have this probleme only when when the modal is opened from a dropdown
thanks yall
https://github.com/shadcn-ui/ui/assets/93702837/287bc707-df04-4831-a31c-f25e9e3a3ddf
Having this issue too. Repeat of #1859.
I have been facing the same issue. Did you find a solution?
For now I've just put !pointer-events-auto
in the className for body, but it's hacky and temporary.
Adding modal={false}
on dropdown menu root worked for me!
Setting modal={false}
will allow users to interact with elements outside of the dialog while it is open which I don't think is usually desirable!
I have the same issues. What happened to me was that I had a dialog triggered in a dropdown menu, and when the dialog was closed, all the clickable elements on page were not clickable.
Yeah it seems to only be an issue when triggering a dialog from a dropdown menu. It's as if the pointer-events: none
class is being toggled. Clicking on the dropdown menu adds pointer-events: none
to body, then selecting an item to open the dialog removes the class, and then closing the dialog adds the class back again. I've found related issue with a lack of automatic focus inside the dialog when it's triggered from a dropdown menu... But don't have a solution as of yet.
Then again, I haven't been using this implementation which has been passed around quite a bit in the past:
https://codesandbox.io/embed/r9sq1q
hi all,
I just found a solution: https://github.com/radix-ui/primitives/issues/837#issuecomment-1455160154
here's a example of shadcn-ui:
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
<span className="sr-only">Actions</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<AlertDialog
open={showDeleteDialog}
onOpenChange={setShowDeleteDialog}
>
<AlertDialogTrigger asChild>
<DropdownMenuItem
className="text-red-600"
onSelect={(event) => {
event.preventDefault();
setShowDeleteDialog(true);
}}
>
<Trash className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. The document will no longer be
accessible by you or others you've shared it with.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<Button
variant="destructive"
onClick={() => {
deleteSelectedFiles();
setShowDeleteDialog(false);
}}
>
Delete
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</DropdownMenuContent>
</DropdownMenu>
Two main points:
- have
event.preventDefault();
in theDropdownMenuItem
. This actually prevents the dialog closing due to the dropdown menu closing. - Wrap
AlertDialog
inDropdownMenuContent
Two main points:
- have
event.preventDefault();
in theDropdownMenuItem
. This actually prevents the dialog closing due to the dropdown menu closing.- Wrap
AlertDialog
inDropdownMenuContent
I got this working also but instead with ContextMenu
and Sheet
.
This solution has worked, thank you @stimw ❤️
This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.
This issue is still present, and manually managing state is not a viable solution imo.
I'm unsure why this is got closed, but I'd like this to be revisited again. This is issue is still present. Once I close the sheet the entire page is unresponsive.
I have a hard time believing that the code below is suggested because the entire page still freezes, and if I wanted to have more that one sheet open from inside the dropdown menu i'm SOL? Am I missing something?
return (
<Sheet>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="outline" className="h-8 w-8">
<MoreHorizontal className="h-3.5 w-3.5" />
<span className="sr-only">More</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-52">
<SheetTrigger asChild>
<DropdownMenuItem>
<Pencil className="mr-2 h-4 w-4" />
Edit Profile
<DropdownMenuShortcut>⌘ + E</DropdownMenuShortcut>
</DropdownMenuItem>
</SheetTrigger>
<DropdownMenuItem>
<Trash className="mr-2 h-4 w-4" />
Trash
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<SheetContent>
<EditProfile name="Jason Anrico" username="anricoj1" />
</SheetContent>
</Sheet>
);
Updating from "@radix-ui/react-dialog": "^1.0.4"
to 1.0.5
worked for me. You might as well update "@radix-ui/react-alert-dialog": "^1.0.4"
to 1.0.5
as well too.
Adding this as well for anyone who comes to this thread looking for a more useful solution. I ended up making a SheetItem
and SheetItemTrigger
components as well as AlertDialogItem
, AlertDialogItemTrigger
, DialogItem
, and DialogItemTrigger
variants in the case where you might want to have multiple dialog options from one dropdown menu. Lets face it, if you're opening one dialog in the menu, you'll probably end up adding more in the future.
/** sheet within dropdown menu item */
function SheetItem({
onOpenChange,
children,
...props
}: ComponentPropsWithoutRef<typeof Sheet>) {
return (
<Sheet onOpenChange={onOpenChange} {...props}>
{children}
</Sheet>
);
}
/** trigger sheet from dropdown menu */
const SheetItemTrigger = forwardRef<
ElementRef<typeof DropdownMenuItem>,
ComponentPropsWithoutRef<typeof DropdownMenuItem>
>(({ onSelect, children, ...props }, ref) => {
return (
<SheetTrigger asChild>
<DropdownMenuItem
{...props}
ref={ref}
onSelect={(event) => {
event.preventDefault();
onSelect && onSelect(event);
}}
>
{children}
</DropdownMenuItem>
</SheetTrigger>
);
});
SheetItemTrigger.displayName = "SheetItemTrigger";
If you want to leverage AlertDialogs
and Dialogs
you need the same code but replace Sheet
with AlertDialog
and Dialog
.
So now I can write a DropdownMenu
that contains any of these, if not all of them and it works as expected
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">Open</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuLabel>Dialog Options</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<AlertDialogItem>
<AlertDialogItemTrigger>
<FileWarning className="mr-2 h-4 w-4" />
<span>Alert Dialog</span>
<DropdownMenuShortcut>⇧⌘A</DropdownMenuShortcut>
</AlertDialogItemTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Edit Profile</AlertDialogTitle>
<AlertDialogDescription>
Update your profile information.
</AlertDialogDescription>
</AlertDialogHeader>
<EditProfileForm name="Jason Anrico" username="anricoj1" />
<AlertDialogFooter>
<AlertDialogAction>Save</AlertDialogAction>
<AlertDialogCancel>Cancel</AlertDialogCancel>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogItem>
<DialogItem>
<DialogItemTrigger>
<MessageCircle className="mr-2 h-4 w-4" />
<span>Dialog</span>
<DropdownMenuShortcut>⌘D</DropdownMenuShortcut>
</DialogItemTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
<DialogDescription>
Update profile information.
</DialogDescription>
</DialogHeader>
<EditProfileForm name="Jason Anrico" username="anricoj1" />
<DialogFooter>
<Button variant="outline">Save</Button>
<Button variant="outline">Cancel</Button>
</DialogFooter>
</DialogContent>
</DialogItem>
<SheetItem>
<SheetItemTrigger>
<FileSpreadsheet className="mr-2 h-4 w-4" />
<span>Sheet</span>
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
</SheetItemTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Edit Profile</SheetTitle>
<SheetDescription>
Update profile information.
</SheetDescription>
</SheetHeader>
<EditProfileForm name="Jason Anrico" username="anricoj1" />
<SheetFooter>
<Button variant="outline">Save</Button>
<Button variant="outline">Cancel</Button>
</SheetFooter>
</SheetContent>
</SheetItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
)
Hope this is helpful information to someone in the future!
@anricoj1 suggestion by updating the dependencies for dialog and alert dialog to 1.0.5 fixed it for me
it's happening again in version @radix-ui/react-dialog 1.1.1
I can confirm it is happening in 1.1.1
as well.
As a workaround I force the pointerEvents on the body set by the context menu/dropdown to be reset.
<Dialog
open={show}
onOpenChange={(open) => {
setShow(open)
setTimeout(() => {
if (!open) {
document.body.style.pointerEvents = ''
}
}, 100)
}}
>
IMHO, it is better than wrapping a dialog around the entire context menu, which is very odd to me.
This still seems to be an issue using [email protected]
and [email protected]
.
I was able to fix the problem by introducing two separate states, one that tracks the state of the Dialog
, the other one for the DropdownMenu
:
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
Then apply the onOpenChange
handler to the DropdownMenu
...
<DropdownMenu onOpenChange={setIsDropdownOpen}>
...and only open the Dialog
, if the dropdown is not open:
<Dialog
open={isDialogOpen && !isDropdownOpen}
onOpenChange={setIsDialogOpen}
>
Works perfectly fine without placing the dialog inside the Dropdown menu or the need for a setTimeout
.
@yagudaev @tomraithel combining both of your approaches has worked for me. I did not find using two states sufficient, as the UI was still unresponsive after closing the dialog. Here's a full working example with [email protected]
and [email protected]
:
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/src/components/shadcn/ui/dropdown-menu";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/src/components/shadcn/ui/dialog";
import { Button } from "@/src/components/shadcn/ui/button";
function TemplateDropdownMenu() {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const {register, handleSubmit, reset} = useForm();
const onSubmit = (data: any) => {
console.log(data);
setIsDialogOpen(false)
};
return (
<>
<Dialog
open={isDialogOpen && !isDropdownOpen}
onOpenChange={(open) => {
setIsDialogOpen(open)
setTimeout(() => {
if (!open) {
document.body.style.pointerEvents = ''
}
}, 100)
}}
>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Form</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("example")} />
<DialogFooter>
<Button type="submit">Submit</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
<DropdownMenu onOpenChange={setIsDropdownOpen}>
<DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => setIsDialogOpen(true)} >Edit</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
);
}
export default TemplateDropdownMenu;
@yagudaev @tomraithel combining both of your approaches has worked for me. I did not find using two states sufficient, as the UI was still unresponsive after closing the dialog. Here's a full working example with
[email protected]
and[email protected]
:import React, { useState } from 'react'; import { useForm } from 'react-hook-form'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/src/components/shadcn/ui/dropdown-menu"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/src/components/shadcn/ui/dialog"; import { Button } from "@/src/components/shadcn/ui/button"; function TemplateDropdownMenu() { const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const {register, handleSubmit, reset} = useForm(); const onSubmit = (data: any) => { console.log(data); setIsDialogOpen(false) }; return ( <> <Dialog open={isDialogOpen && !isDropdownOpen} onOpenChange={(open) => { setIsDialogOpen(open) setTimeout(() => { if (!open) { document.body.style.pointerEvents = '' } }, 100) }} > <DialogContent> <DialogHeader> <DialogTitle>Dialog Form</DialogTitle> </DialogHeader> <form onSubmit={handleSubmit(onSubmit)}> <input {...register("example")} /> <DialogFooter> <Button type="submit">Submit</Button> </DialogFooter> </form> </DialogContent> </Dialog> <DropdownMenu onOpenChange={setIsDropdownOpen}> <DropdownMenuTrigger>Open Menu</DropdownMenuTrigger> <DropdownMenuContent> <DropdownMenuItem onClick={() => setIsDialogOpen(true)} >Edit</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </> ); } export default TemplateDropdownMenu;
Interesting, I thought the approach of the two states by @tomraithel would work.
Looks like it waits for the context menu to properly close before opening the dialog which should make the problem go away.
Anyway, I'll keep an eye on this. We should re-open this issue.
Yeah strange, I wonder why it works in my case but for @lmyslinski it does not (without the timeout)... 🤔
Yeah strange, I wonder why it works in my case but for @lmyslinski it does not (without the timeout)... 🤔
your fix worked for me perfectly as well, is it because our dialog is outside the dropdown menu? mine is.
thanks for the fix @tomraithel
Can confirm by pinning to 1.0.5 fixes the bug 🫡
npm i @radix-ui/[email protected]
just make sure to set a reminder to update it once it's fixed!