ui icon indicating copy to clipboard operation
ui copied to clipboard

Feature: Close Functionality for Dialog Component

Open yashsehgal-clamp opened this issue 2 years ago • 19 comments

Right now there's no such wrapper code available in the docs for close functionality in Dialog component. I have tried to implement the same and it's working all fine.

const DialogClose = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Close>,
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close>
>(({ className, ...props }, ref) => (
  <DialogPrimitive.Close
    ref={ref}
    {...props}
  />
))
DialogClose.displayName = DialogPrimitive.Title.displayName

This can be used like the snippet shown below

<Dialog>
  <DialogTrigger>Open</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Are you sure absolutely sure?</DialogTitle>
      <DialogDescription>
        This action cannot be undone. This will permanently delete your account
        and remove your data from our servers.
      </DialogDescription>
    </DialogHeader>
    <DialogFooter>
      {/* this is how it can be used */}
      <DialogClose />
    </DialogFooter>
  </DialogContent>
</Dialog>

Hope this helps! Happy to have a chat around this issue/feature-request.

yashsehgal-clamp avatar Feb 15 '23 19:02 yashsehgal-clamp

@yashsehgal-clamp Yeah, that works fine, now, a better example can be:

  <DialogFooter>
    <DialogClose asChild>
      <Button onClick={editTask}>Save changes</Button>
    </DialogClose>
  </DialogFooter>

Go ahead and send a PR, you could even create an example at https://ui.shadcn.com/docs/primitives/dialog showing how to close it from the footer

Angelfire avatar Feb 17 '23 18:02 Angelfire

Sure, thanks for spending time on this issue. Really good work with all the other components, really helpful.

I will be raising a PR shortly 👍🏽

yashsehgal avatar Feb 20 '23 11:02 yashsehgal

@yashsehgal could you apply this to sheets as well? Sheets are basically same as Dialogs

keon avatar Mar 06 '23 13:03 keon

Thank you for this @yashsehgal

joqim avatar May 22 '23 05:05 joqim

Sorry very new to this so hoping for some help. I have a react-hook-form form with a submit btn inside a dialog called from a dropdown. While I can add another button to close the form as suggested by @Angelfire I can't figure out how to make the form submit button submit the form and if validation successful close the dialog.

conana28 avatar May 30 '23 03:05 conana28

Thanks so much @yashsehgal!

pawelmisiura avatar Jun 05 '23 18:06 pawelmisiura

I did some changes and made function if you want to call it on the onSubmit

I added an id in the close button: id="closeDialog"

const DialogContent = React.forwardRef<
   // ...
      <DialogPrimitive.Close
        id="closeDialog"
        className="..."
      >
  // ...
  </DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

then exported a dialogClose()

const dialogClose = () => {
  document.getElementById('closeDialog')?.click();
};

and now I can call this inside a function for example, after validating with zod a form

BielCoelho avatar Jun 05 '23 19:06 BielCoelho

@conana28 @BielCoelho There is a way to close it on submit etc, it's described in the radix-ui docs here

const [open, setOpen] = useState(false)
<Dialog open={open} onOpenChange={setOpen}>

kaareloun avatar Jun 05 '23 21:06 kaareloun

@BielCoelho @kaareloun Thanks for your help. Got both solutions working

conana28 avatar Jun 06 '23 04:06 conana28

Sorry very new to this so hoping for some help. I have a react-hook-form form with a submit btn inside a dialog called from a dropdown. While I can add another button to close the form as suggested by @Angelfire I can't figure out how to make the form submit button submit the form and if validation successful close the dialog.

I also have the same issues . dialog not closing onSubmit

femiAdeyemo1 avatar Aug 16 '23 13:08 femiAdeyemo1

I did some changes and made function if you want to call it on the onSubmit

I added an id in the close button: id="closeDialog"

const DialogContent = React.forwardRef<
   // ...
      <DialogPrimitive.Close
        id="closeDialog"
        className="..."
      >
  // ...
  </DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

then exported a dialogClose()

const dialogClose = () => {
  document.getElementById('closeDialog')?.click();
};

and now I can call this inside a function for example, after validating with zod a form

This should be a feature.

Cyberistic avatar Aug 19 '23 16:08 Cyberistic

Here is how we do it and it works great.

IF you are using shadcn-ui

  1. Wrap the element that you want to use to close the dialog with DialogTrigger
  2. Pass asChild to the DialogTrigger in 2 If you were thinking, "This is the same way you can open the dialog", you are correct!

But if you want to use radix to close the dialog, you can import DialogClose from @radix-ui/react-dialog and it'll work the same. You just need to replace DialogTrigger with DialogClose.

<DialogTrigger asChild>
  <Button>Close</Button>
</DialogTrigger>

byonghun avatar Sep 01 '23 20:09 byonghun

THANKS SM BRO <3 @kaareloun

mr-abdellah avatar Nov 01 '23 13:11 mr-abdellah

I did some changes and made function if you want to call it on the onSubmit

I added an id in the close button: id="closeDialog"

const DialogContent = React.forwardRef<
   // ...
      <DialogPrimitive.Close
        id="closeDialog"
        className="..."
      >
  // ...
  </DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;

then exported a dialogClose()

const dialogClose = () => {
  document.getElementById('closeDialog')?.click();
};

and now I can call this inside a function for example, after validating with zod a form

i did the same, just used ref instead

shubhamkashyapdev avatar Nov 05 '23 10:11 shubhamkashyapdev

Thank you @yashsehgal-clamp!

This helped answer my question.

Here was my solution.

// import DialogPrimitive in your component using Dialog component
import * as DialogPrimitive from "@radix-ui/react-dialog"
<Dialog>
    <DialogContent>
        <DialogPrimitive.Close>
            {/*  add any element here to trigger dialog to close */}
        </DialogPrimitive.Close>
    </DialogContent>
</Dialog>

yeahitsmejayyy avatar Feb 18 '24 02:02 yeahitsmejayyy

Hey @benyaminl ! You can add a prop & new interface type into your DialogContent in components/ui/dialog.tsx that looks like this:

interface DialogContentProps {
  showCloseButton: boolean;
  className?: string;
  children?: React.ReactNode;
}

const DialogContent = React.forwardRef<
  React.ElementRef<typeof DialogPrimitive.Content>,
  DialogContentProps &
    React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ showCloseButton = true, className, children, ...props }, ref) => (
  <DialogPortal>
    <DialogOverlay />
    <DialogPrimitive.Content
      ref={ref}
      className={cn(
        'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
        className
      )}
      {...props}
    >
      {children}
      {showCloseButton ? (
        <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
          <X className="h-4 w-4" />
          <span className="sr-only">Close</span>
        </DialogPrimitive.Close>
      ) : (
        ''
      )}
    </DialogPrimitive.Content>
  </DialogPortal>
));

I have added the showCloseButton prop to the component, you can then simply pass in showCloseButton={true/false} to show/hide the button 😄

Logannford avatar Mar 02 '24 22:03 Logannford

Sorry very new to this so hoping for some help. I have a react-hook-form form with a submit btn inside a dialog called from a dropdown. While I can add another button to close the form as suggested by @Angelfire I can't figure out how to make the form submit button submit the form and if validation successful close the dialog.

I am looking for the same thing

ashreyas431 avatar Mar 05 '24 11:03 ashreyas431

For the commenters asking about how to control open/close:

Remember shadcn/ui is mostly a collection of tailwind styles for radix-ui components. Therefore you should check the radix-ui docs to understand how to control and manage the behaviour of their components.

Controlling open/close state

By default radix manages the open/close state of the dialog for you. However you also have the option to control the open/close state yourself and pass it to the Dialog via props. You can use this option if you want to close a dialog based on some external condition such as a form submission.

The docs refer to the choice as uncontrolled (default -- they control the state) vs. controlled (you control the state).

If you look at the shadcn/ui code for Dialog, it simply re-exports the underlying radix-ui Dialog.Root as Dialog without changing anything (i.e. shadcn/ui Dialog is radix-ui's Dialog.Root, it's only exported with a slightly different name). That means everything in the radix docs including all the examples fully apply to shadcn/ui.

To control Dialog.Root you pass open (boolean) and setOpen callback via props (setOpen is a useState callback that sets the value of open).

Now that you control the state in your parent component you can change it however and whenever you like (such as when a form submission is successful) and the dialog will respond.

Examples

There are solid examples in the docs:

  • https://www.radix-ui.com/primitives/docs/components/dialog#examples
  • https://www.radix-ui.com/primitives/docs/components/dialog#close-after-asynchronous-form-submission

firxworx avatar Apr 15 '24 01:04 firxworx

Here's a working EditDialog component I created for a project I'm working on. It supports Zod validation in a form inside a shadcn <Dialog>:

import { PropsWithChildren, useState } from 'react'
import { UseFormReturn } from 'react-hook-form'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle
} from '../../../ui/dialog'
import { Form } from '../../../ui/form'
import { Button } from '../../../ui/button'

export const EditDialog = ({
  children,
  form,
  onSubmit,
  triggerContainer
}: PropsWithChildren & {
  form: UseFormReturn
  onSubmit: (event) => void
  triggerContainer: React.JSX.Element
}) => {
  const [open, setOpen] = useState(false)

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      {triggerContainer}
      <DialogContent className="sm:max-w-md">
        <Form {...form}>
          <form
            onSubmit={form.handleSubmit((event) => {
              onSubmit(event)
              setOpen(false)
            })}
          >
            <DialogHeader>
              <DialogTitle>Add New Area</DialogTitle>
            </DialogHeader>
            {children}
            <DialogFooter className="sm:justify-start pt-4">
              <DialogClose asChild>
                <Button type="button" variant="outline">
                  Cancel
                </Button>
              </DialogClose>
              <Button type="submit">Save</Button>
            </DialogFooter>
          </form>
        </Form>
      </DialogContent>
    </Dialog>
  )
}

The triggerContainer element should contain a <DialogTrigger> somewhere inside. Could be the root element, could be nested inside other elements. Just make sure there are no other <Dialog> elements inside it. Form submission works both by pressing the button as well as the Enter key.

Enjoy! :)

faribauc avatar Apr 25 '24 22:04 faribauc

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.

shadcn avatar Jul 10 '24 23:07 shadcn