import React from 'react'
import { render, screen, fireEvent } from '@testing-library/react'
import '@testing-library/jest-dom'
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
DialogClose,
DialogForm,
} from '../dialog'
// Mock Radix UI Dialog components
/* eslint-disable @typescript-eslint/no-explicit-any */
jest.mock('@radix-ui/react-dialog', () => {
const mockForwardRef = (component: any) => {
const forwardedComponent = (props: any, ref: any) => component({ ...props, ref })
forwardedComponent.displayName = component.displayName || component.name
return forwardedComponent
}
return {
Root: ({ children, open, ...props }: any) => (
{children}
),
Trigger: ({ children, ...props }: any) => (
{children}
),
Portal: ({ children }: any) => (
{children}
),
Overlay: mockForwardRef(({ className, ref, ...props }: any) => (
)),
Content: mockForwardRef(({ className, children, ref, ...props }: any) => (
{children}
)),
Close: ({ children, onClick, ...props }: any) => (
{children}
),
Title: mockForwardRef(({ className, children, ref, ...props }: any) => (
{children}
)),
Description: mockForwardRef(({ className, children, ref, ...props }: any) => (
{children}
)),
}
})
// Mock Lucide React icons
jest.mock('lucide-react', () => ({
X: ({ className, ...props }: any) => (
),
Check: ({ className, ...props }: any) => (
),
Loader2: ({ className, ...props }: any) => (
),
}))
describe('Dialog Components', () => {
describe('Dialog Root Component', () => {
it('renders correctly', () => {
render(
Open Dialog
Dialog Content
)
expect(screen.getByTestId('dialog-root')).toBeInTheDocument()
expect(screen.getByTestId('dialog-trigger')).toBeInTheDocument()
})
it('handles open state', () => {
render(
Dialog Content
)
const root = screen.getByTestId('dialog-root')
expect(root).toHaveAttribute('data-open', 'true')
})
it('handles onOpenChange callback', () => {
const onOpenChange = jest.fn()
render(
Open Dialog
)
expect(screen.getByTestId('dialog-root')).toBeInTheDocument()
})
})
describe('DialogTrigger Component', () => {
it('renders correctly', () => {
render(
Open Dialog
)
const trigger = screen.getByTestId('dialog-trigger')
expect(trigger).toBeInTheDocument()
expect(trigger).toHaveTextContent('Open Dialog')
})
it('passes through HTML attributes', () => {
render(
Open Dialog
)
const trigger = screen.getByTestId('dialog-trigger')
expect(trigger).toHaveAttribute('id', 'trigger-1')
expect(trigger).toHaveClass('custom-trigger')
})
})
describe('DialogContent Component', () => {
it('renders correctly with default props', () => {
render(
Dialog Content
)
const content = screen.getByTestId('content')
expect(content).toBeInTheDocument()
expect(content).toHaveTextContent('Dialog Content')
})
it('applies custom className', () => {
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toHaveClass('custom-dialog')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toBeInTheDocument()
})
it('renders default variant correctly', () => {
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toHaveClass('border-gray-200 dark:border-gray-700')
})
it('renders primary variant correctly', () => {
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toHaveClass('border-primary/20 dark:border-primary/30')
})
it('renders ghost variant correctly', () => {
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toHaveClass('border-transparent shadow-xl')
})
it('renders different sizes correctly', () => {
const { rerender } = render(
Content
)
expect(screen.getByTestId('content')).toHaveClass('max-w-sm p-5')
rerender(
Content
)
expect(screen.getByTestId('content')).toHaveClass('max-w-2xl p-7')
})
it('renders different radius variants', () => {
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toHaveClass('rounded-2xl')
})
it('renders with title and description', () => {
render(
Content
)
expect(screen.getByTestId('dialog-title')).toHaveTextContent('Dialog Title')
expect(screen.getByTestId('dialog-description')).toHaveTextContent('Dialog Description')
})
it('renders with custom icon', () => {
const CustomIcon = () => Icon
render(
} title="Title">
Content
)
expect(screen.getByTestId('custom-icon')).toBeInTheDocument()
})
it('renders loading state', () => {
render(
Content
)
expect(screen.getByTestId('loader-icon')).toBeInTheDocument()
})
it('renders success state', () => {
render(
Content
)
expect(screen.getByTestId('check-icon')).toBeInTheDocument()
})
it('hides close button when hideCloseButton is true', () => {
render(
Content
)
expect(screen.queryByTestId('dialog-close')).not.toBeInTheDocument()
})
it('shows close button by default', () => {
render(
Content
)
expect(screen.getByTestId('dialog-close')).toBeInTheDocument()
expect(screen.getByTestId('x-icon')).toBeInTheDocument()
})
it('calls onClose when close button is clicked', () => {
const onClose = jest.fn()
render(
Content
)
const closeButton = screen.getByTestId('dialog-close')
fireEvent.click(closeButton)
expect(onClose).toHaveBeenCalledTimes(1)
})
it('renders different positions correctly', () => {
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toHaveClass('top-[5%]')
})
it('passes through HTML attributes', () => {
render(
Content
)
const content = screen.getByTestId('content')
expect(content).toHaveAttribute('id', 'dialog-1')
expect(content).toHaveAttribute('data-custom', 'value')
})
})
describe('DialogHeader Component', () => {
it('renders correctly', () => {
render(Header Content )
const header = screen.getByTestId('dialog-header')
expect(header).toBeInTheDocument()
expect(header).toHaveTextContent('Header Content')
expect(header).toHaveClass('flex flex-col space-y-2 text-center sm:text-left')
})
it('applies custom className', () => {
render(
Header
)
const header = screen.getByTestId('dialog-header')
expect(header).toHaveClass('custom-header')
})
it('passes through HTML attributes', () => {
render(
)
const header = screen.getByTestId('dialog-header')
expect(header).toHaveAttribute('id', 'header-1')
})
it('maintains displayName', () => {
expect(DialogHeader.displayName).toBe('DialogHeader')
})
})
describe('DialogFooter Component', () => {
it('renders correctly', () => {
render(Footer Content )
const footer = screen.getByTestId('dialog-footer')
expect(footer).toBeInTheDocument()
expect(footer).toHaveTextContent('Footer Content')
expect(footer).toHaveClass('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:space-x-2 mt-6')
})
it('applies custom className', () => {
render(
Footer
)
const footer = screen.getByTestId('dialog-footer')
expect(footer).toHaveClass('custom-footer')
})
it('passes through HTML attributes', () => {
render(
)
const footer = screen.getByTestId('dialog-footer')
expect(footer).toHaveAttribute('id', 'footer-1')
})
it('maintains displayName', () => {
expect(DialogFooter.displayName).toBe('DialogFooter')
})
})
describe('DialogTitle Component', () => {
it('renders correctly', () => {
render(Dialog Title )
const title = screen.getByTestId('title')
expect(title).toBeInTheDocument()
expect(title).toHaveTextContent('Dialog Title')
expect(title).toHaveClass('text-xl font-semibold leading-snug tracking-tight dark:text-white')
})
it('applies custom className', () => {
render(
Title
)
const title = screen.getByTestId('title')
expect(title).toHaveClass('custom-title')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(Title )
const title = screen.getByTestId('title-ref')
expect(title).toBeInTheDocument()
})
it('passes through HTML attributes', () => {
render(
Title
)
const title = screen.getByTestId('title')
expect(title).toHaveAttribute('id', 'title-1')
})
})
describe('DialogDescription Component', () => {
it('renders correctly', () => {
render(Dialog Description )
const description = screen.getByTestId('description')
expect(description).toBeInTheDocument()
expect(description).toHaveTextContent('Dialog Description')
expect(description).toHaveClass('text-sm text-muted-foreground leading-normal dark:text-gray-400')
})
it('applies custom className', () => {
render(
Description
)
const description = screen.getByTestId('description')
expect(description).toHaveClass('custom-description')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(Description )
const description = screen.getByTestId('desc-ref')
expect(description).toBeInTheDocument()
})
it('passes through HTML attributes', () => {
render(
Description
)
const description = screen.getByTestId('description')
expect(description).toHaveAttribute('id', 'desc-1')
})
})
describe('DialogClose Component', () => {
it('renders correctly', () => {
render(Close )
const close = screen.getByTestId('close')
expect(close).toBeInTheDocument()
expect(close).toHaveTextContent('Close')
})
it('handles click events', () => {
const onClick = jest.fn()
render(Close )
const close = screen.getByTestId('dialog-close')
fireEvent.click(close)
expect(onClick).toHaveBeenCalledTimes(1)
})
})
describe('DialogForm Component', () => {
it('renders correctly', () => {
render(Form Content )
const form = screen.getByTestId('dialog-form')
expect(form).toBeInTheDocument()
expect(form).toHaveTextContent('Form Content')
expect(form).toHaveClass('flex flex-col gap-4')
})
it('applies custom className', () => {
render(
Form
)
const form = screen.getByTestId('dialog-form')
expect(form).toHaveClass('custom-form')
})
it('forwards ref correctly', () => {
const ref = React.createRef()
render(Form )
expect(ref.current).toBeInstanceOf(HTMLFormElement)
})
it('handles form submission', () => {
const onSubmit = jest.fn((e) => e.preventDefault())
render(
Submit
)
const form = screen.getByTestId('dialog-form')
fireEvent.submit(form)
expect(onSubmit).toHaveBeenCalledTimes(1)
})
it('passes through HTML attributes', () => {
render(
Form
)
const form = screen.getByTestId('dialog-form')
expect(form).toHaveAttribute('id', 'form-1')
})
it('maintains displayName', () => {
expect(DialogForm.displayName).toBe('DialogForm')
})
})
describe('Complex Dialog Combinations', () => {
it('renders complete dialog with all components', () => {
render(
Open Dialog
Complete Dialog
This is a complete dialog example
Main content goes here
Cancel
Confirm
)
expect(screen.getByTestId('dialog-trigger')).toBeInTheDocument()
expect(screen.getByTestId('dialog-content')).toBeInTheDocument()
expect(screen.getByTestId('dialog-title')).toHaveTextContent('Complete Dialog')
expect(screen.getByTestId('dialog-description')).toHaveTextContent('This is a complete dialog example')
expect(screen.getByText('Main content goes here')).toBeInTheDocument()
expect(screen.getByText('Cancel')).toBeInTheDocument()
expect(screen.getByText('Confirm')).toBeInTheDocument()
})
it('renders dialog with form integration', () => {
render(
Form Dialog
Submit
)
expect(screen.getByTestId('dialog-form')).toBeInTheDocument()
expect(screen.getByPlaceholderText('Enter text')).toBeInTheDocument()
expect(screen.getByText('Submit')).toBeInTheDocument()
})
it('handles all variants and props together', () => {
render(
Advanced content
)
const content = screen.getByTestId('dialog-content')
expect(content).toHaveClass('custom-dialog')
expect(content).toHaveClass('border-primary/20 dark:border-primary/30')
expect(content).toHaveClass('max-w-2xl p-7')
expect(content).toHaveClass('rounded-2xl')
expect(content).toHaveClass('top-[5%]')
expect(screen.getByTestId('dialog-title')).toHaveTextContent('Advanced Dialog')
expect(screen.getByTestId('dialog-description')).toHaveTextContent('With all features')
})
})
describe('Edge Cases', () => {
it('handles empty dialog content', () => {
render(
)
const content = screen.getByTestId('content')
expect(content).toBeInTheDocument()
// Content should only contain the overlay and content wrapper
expect(content.children.length).toBeGreaterThanOrEqual(0)
})
it('handles null and undefined children', () => {
render(
{null}
{undefined}
Valid content
)
expect(screen.getByText('Valid content')).toBeInTheDocument()
})
it('handles dialog without trigger', () => {
render(
Content without trigger
)
expect(screen.getByTestId('dialog-content')).toBeInTheDocument()
})
it('handles multiple dialogs', () => {
render(
Dialog 1
Dialog 2
)
expect(screen.getByTestId('dialog-1')).toBeInTheDocument()
expect(screen.getByTestId('dialog-2')).toBeInTheDocument()
})
it('handles loading and success states together', () => {
render(
Content
)
// Both icons should be present as they are rendered conditionally
expect(screen.getByTestId('loader-icon')).toBeInTheDocument()
expect(screen.getByTestId('check-icon')).toBeInTheDocument()
})
})
})