feat(dialog): enhance Dialog component and add comprehensive Storybook examples

- Updated DialogContent styling to use a rounded border and improved shadow effects for better visual appeal.
- Introduced a new Storybook file for the Dialog component, featuring multiple stories that demonstrate various use cases, including default, alert, form, and customizable dialogs.
- Enhanced accessibility and usability by providing examples for different dialog configurations and actions.
This commit is contained in:
MyPrototypeWhat 2025-12-02 18:31:49 +08:00
parent 3cd048868a
commit 1f5abf86a0
2 changed files with 344 additions and 1 deletions

View File

@ -46,7 +46,7 @@ function DialogContent({
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
'bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
'bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-xs border border-border p-6 shadow-lg duration-200 sm:max-w-lg',
className
)}
{...props}>

View File

@ -0,0 +1,343 @@
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@cherrystudio/ui'
import type { Meta, StoryObj } from '@storybook/react'
const meta: Meta<typeof Dialog> = {
title: 'Components/Primitives/Dialog',
component: Dialog,
parameters: {
layout: 'centered',
docs: {
description: {
component:
'A modal dialog component that interrupts the user with important content and expects a response. Based on Radix UI Dialog primitive.'
}
}
},
tags: ['autodocs']
}
export default meta
type Story = StoryObj<typeof meta>
// Default
export const Default: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Open Dialog</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>This is a description of the dialog content.</DialogDescription>
</DialogHeader>
<p className="text-sm text-muted-foreground">Dialog body content goes here.</p>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Confirm</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
// Without Close Button
export const WithoutCloseButton: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Open Dialog</Button>
</DialogTrigger>
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>No Close Button</DialogTitle>
<DialogDescription>This dialog does not have a close button in the corner.</DialogDescription>
</DialogHeader>
<p className="text-sm text-muted-foreground">Users must use the footer buttons to close this dialog.</p>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Confirm</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
// Simple Alert
export const SimpleAlert: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">Delete Item</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
<DialogDescription>
This action cannot be undone. This will permanently delete the item from the database.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
// With Form
export const WithForm: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button>Edit Profile</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Edit Profile</DialogTitle>
<DialogDescription>Make changes to your profile here. Click save when you're done.</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<label htmlFor="name" className="text-right text-sm font-medium">
Name
</label>
<input
id="name"
defaultValue="John Doe"
className="col-span-3 h-9 rounded-md border px-3 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<label htmlFor="email" className="text-right text-sm font-medium">
Email
</label>
<input
id="email"
defaultValue="john@example.com"
className="col-span-3 h-9 rounded-md border px-3 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
/>
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button type="submit">Save Changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
// Custom Width
export const CustomWidth: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Wide Dialog</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2xl">
<DialogHeader>
<DialogTitle>Wide Dialog</DialogTitle>
<DialogDescription>This dialog has a custom max-width of 2xl (672px).</DialogDescription>
</DialogHeader>
<div className="py-4">
<p className="text-sm text-muted-foreground">
You can customize the dialog width by passing a className prop with a max-width utility class.
</p>
</div>
<DialogFooter>
<DialogClose asChild>
<Button>Close</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
// Scrollable Content
export const ScrollableContent: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Terms of Service</Button>
</DialogTrigger>
<DialogContent className="max-h-[80vh] overflow-y-auto sm:max-w-lg">
<DialogHeader>
<DialogTitle>Terms of Service</DialogTitle>
<DialogDescription>Please read the following terms carefully.</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4 text-sm text-muted-foreground">
{Array.from({ length: 10 }, (_, i) => (
<p key={i}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
ea commodo consequat.
</p>
))}
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Decline</Button>
</DialogClose>
<Button>Accept</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
// Header Only
export const HeaderOnly: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Simple Info</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Information</DialogTitle>
<DialogDescription>
This is a simple informational dialog with only a header and description.
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
)
}
// Multiple Actions
export const MultipleActions: Story = {
render: () => (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Save Document</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Save Document</DialogTitle>
<DialogDescription>Choose how you want to save your document.</DialogDescription>
</DialogHeader>
<DialogFooter className="flex-col gap-2 sm:flex-row">
<DialogClose asChild>
<Button variant="outline" className="w-full sm:w-auto">
Don't Save
</Button>
</DialogClose>
<Button variant="secondary" className="w-full sm:w-auto">
Save as Draft
</Button>
<Button className="w-full sm:w-auto">Publish</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
// Real World Examples
export const RealWorldExamples: Story = {
render: () => (
<div className="flex flex-wrap gap-4">
{/* Delete Confirmation */}
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">Delete Account</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Account</DialogTitle>
<DialogDescription>
Are you sure you want to delete your account? All of your data will be permanently removed. This action
cannot be undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button variant="destructive">Delete Account</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Settings Dialog */}
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Settings</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Settings</DialogTitle>
<DialogDescription>Manage your application preferences.</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">Dark Mode</p>
<p className="text-xs text-muted-foreground">Enable dark theme</p>
</div>
<input type="checkbox" className="h-4 w-4" />
</div>
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium">Notifications</p>
<p className="text-xs text-muted-foreground">Receive email notifications</p>
</div>
<input type="checkbox" className="h-4 w-4" defaultChecked />
</div>
</div>
<DialogFooter>
<DialogClose asChild>
<Button>Save Settings</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Share Dialog */}
<Dialog>
<DialogTrigger asChild>
<Button>Share</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Share Link</DialogTitle>
<DialogDescription>Anyone with this link can view this document.</DialogDescription>
</DialogHeader>
<div className="flex items-center gap-2 py-4">
<input
readOnly
value="https://example.com/share/abc123"
className="h-9 flex-1 rounded-md border px-3 text-sm"
/>
<Button size="sm">Copy</Button>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Close</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}