ui
ui copied to clipboard
Unable to use Dialog inside a DropdownMenu ( with DialogTrigger or without it )
I'm having an issue with triggering a dropdown menu correctly. The dropdown menu contains a dialog, and I'm trying to trigger the dropdown using a DropdownMenuTrigger or a DialogTrigger, but neither is working as expected.
Minimal Reproducible Example:
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="bg-gray-800 text-white mb-4 h-12 w-full text-md pr-4 text-left">
Create Campaign
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="center" className="w-[260px] bg-white p-4">
<Dialog>
<DialogTrigger asChild>
<DropdownMenuItem className="text-md pt-2">
trigger dialog from DropdownMenuItem
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="text-black max-w-[60%] bg-white p-6 h-[600px]">
<div className="flex gap-10 items-start">
x
</div>
</DialogContent>
</Dialog>
<DropdownMenuSeparator className={"bg-gray-300"}/>
<DropdownMenuItem className="text-md pt-3">
option 2
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
`<DropdownMenu modal={false}> <DropdownMenuTrigger className="hover:bg-violet-200 h-8 w-8 flex justify-center items-center rounded-full"> <IoEllipsisVerticalOutline /> </DropdownMenuTrigger> <DropdownMenuContent className="shadow-md bg-white rounded-sm border p-2"> <AlertDialog> <AlertDialogTrigger asChild> <DropdownMenuItem onSelect={(e) => e.preventDefault()} className="flex items-center py-1 px-2 rounded-sm hover:!bg-violet-200 cursor-pointer"> <MdDriveFileRenameOutline className="mr-3" /> Rename </DropdownMenuItem> </AlertDialogTrigger> <AlertDialogContent> <AlertDialogHeader> <AlertDialogTitle>Rename</AlertDialogTitle> <AlertDialogDescription> Please enter a new name for the item: <input type="text" value={docName} onChange={(e) => { setDocName(e.target.value); }} className="py-1 px-2 w-full border-2 outline-violet-800 my-2" /> </AlertDialogDescription> </AlertDialogHeader> <AlertDialogFooter> <AlertDialogCancel onClick={() => { setDocName(Name); }} className="hover:text-gray-800 hover:border-violet-500 text-violet-800"> Cancel </AlertDialogCancel> <AlertDialogAction className={ docName == "" ? "bg-gray-200 text-gray-700 hover:bg-gray-200 hover:text-gray-700 cursor-not-allowed " : "" }> Save </AlertDialogAction> </AlertDialogFooter> </AlertDialogContent> </AlertDialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<DropdownMenuItem
onSelect={(e) => e.preventDefault()}
className="flex items-center py-1 px-2 rounded-sm hover:!bg-violet-200 cursor-pointer">
<MdDeleteForever className="mr-3" />
Delete
</DropdownMenuItem>
</AlertDialogTrigger>
<AlertDialogContent >
<AlertDialogHeader>
<AlertDialogTitle>Delete</AlertDialogTitle>
<AlertDialogDescription>
Are you to delete it permanently ?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="hover:text-gray-800 hover:border-violet-500 text-violet-800">
Cancel
</AlertDialogCancel>
<AlertDialogAction>Delete</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</DropdownMenuContent>
</DropdownMenu>`
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.
I've come up with the following:
<DropdownMenu open={open} onOpenChange={setOpen}> <Dialog> <DropdownMenuTrigger> <Icons.more /> </DropdownMenuTrigger> <DropdownMenuContent align="start"> <DialogTrigger asChild> <DropdownMenuItem> trigger dialog from DropdownMenuItem </DropdownMenuItem> </DialogTrigger> </DropdownMenuContent> <DialogContent> <div> x </div> </DialogContent> </Dialog> </DropdownMenu>
If you want to have a DialogBox pop up from a dropdown menu (for example, a dropdown menu to edit a user, which would pop up a DialogBox modal to edit it), you'll have to do it like this:
<DialogTrigger asChild>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>Edit</DropdownMenuItem>
</DialogTrigger>
the key is the preventDefault() call.
If you want to have a DialogBox pop up from a dropdown menu (for example, a dropdown menu to edit a user, which would pop up a DialogBox modal to edit it), you'll have to do it like this:
<DialogTrigger asChild> <DropdownMenuItem onSelect={(e) => e.preventDefault()}>Edit</DropdownMenuItem> </DialogTrigger>
the key is the preventDefault() call.
I was using open states all over the place and it was buggy to maintain the open state. this prevent default fixed it for me, because when you select a dropdown item the dropdown menu will close and will close with it the dialog by default
@Char99s, @shiftcontrol-dan, Using "preventDefault" works by keeping the dialog open. But it remains open even when the dialog is closed. Is there way to automatically close the dropdown menu when the dialog is closed?
@Char99s, @shiftcontrol-dan, Using "preventDefault" works by keeping the dialog open. But it remains open even when the dialog is closed. Is there way to automatically close the dropdown menu when the dialog is closed?
Perhaps managing the open state of the dropdown, and then setting it to false once close the dialog.
@Char99s, @shiftcontrol-dan, Using "preventDefault" works by keeping the dialog open. But it remains open even when the dialog is closed. Is there way to automatically close the dropdown menu when the dialog is closed?
+1
If you want to have a DialogBox pop up from a dropdown menu (for example, a dropdown menu to edit a user, which would pop up a DialogBox modal to edit it), you'll have to do it like this:
<DialogTrigger asChild> <DropdownMenuItem onSelect={(e) => e.preventDefault()}>Edit</DropdownMenuItem> </DialogTrigger>
the key is the preventDefault() call.
Thank you, your solution worked really well for me; I was frustrated with the code unable to figure out why?? then like savior this helped me a lot
ShadCn has provided also some guidance in the Dialog Page - here.
In cases of multiple different dialogs, you would need to tell react which dialog to render
export function HeaderDropDownMenu() {
const [dialogMenu, setDialogMenu] = useState<string>("none");
const handleDialogMenu = (): JSX.Element | null => {
switch (dialogMenu) {
case "create":
return <CreateDialog />
case "delete":
return <DeleteDialog />;
default:
return null;
}
};
return (
<Dialog>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="ghost"> Menu </Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => setDialogMenu("create")}>
<PlusIcon /> Create Campaign
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => setDialogMenu("delete")}>
<TrashIcon /> Delete Campaign
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
{handleDialogMenu()}
</Dialog>
);
}
And your DeleteDialog
should only contain the <DialogContent/>
as you've already the entire component wrapped in a dialog.
export function DeleteCampaignDialog() {
return (
<DialogContent>
<DialogHeader>
<DialogTitle>DELETE</DialogTitle>
<DialogDescription>
This action cannot be undone. Are you sure you want to permanently
delete this file from our servers?
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button type="submit">Confirm</Button>
</DialogFooter>
</DialogContent>
);
}