@@ -25,8 +25,155 @@ import { act, render, screen } from "@testing-library/react"
2525import userEvent from "@testing-library/user-event"
2626import type React from "react"
2727import { type ForwardedRef , type ReactNode , type RefObject , createRef , useState } from "react"
28- import { describe , expect , it } from "vitest"
28+ import { describe , expect , it , vi } from "vitest"
29+ // endregion
30+
31+ // region Mocks
32+ vi . mock ( "@chakra-ui/react" , async ( ) => {
33+ const React = await import ( "react" )
34+ const { useState, createContext, useContext } = React
35+
36+ // region Type Aliases (Copied EXACTLY from original TestSetup)
37+ type ComponentPropsWithoutRef < T extends React . ElementType > = React . ComponentPropsWithoutRef < T >
38+ type DialogRootProps = {
39+ children : ReactNode
40+ open ?: boolean
41+ onOpenChange ?: ( details : { open : boolean } ) => void
42+ } & ComponentPropsWithoutRef < "div" >
43+ type DialogTriggerProps = {
44+ children : ReactNode
45+ isOpen ?: boolean
46+ onOpen ?: ( ) => void
47+ } & ComponentPropsWithoutRef < "button" >
48+ type DialogContentProps = {
49+ children : ReactNode
50+ portalled ?: boolean
51+ backdrop ?: boolean
52+ ref ?: ForwardedRef < HTMLDivElement >
53+ } & ComponentPropsWithoutRef < "div" >
54+ type CloseButtonProps = { children ?: ReactNode } & ComponentPropsWithoutRef < "button" >
55+ type DialogCloseTriggerProps = {
56+ children ?: ReactNode
57+ asChild ?: boolean
58+ insetEnd ?: string
59+ top ?: string
60+ position ?: string
61+ ref ?: ForwardedRef < HTMLButtonElement >
62+ } & ComponentPropsWithoutRef < "button" >
63+ type DialogContextType = { onClose : ( ) => void ; isOpen : boolean }
64+ interface RefElement extends React . ReactElement {
65+ ref ?: ForwardedRef < HTMLButtonElement >
66+ }
67+ // endregion
68+
69+ const DialogContext = createContext < DialogContextType | undefined > ( undefined )
70+ const ThemeContext = createContext ( { theme : { _config : { } } } )
71+
72+ // region Mock Components (Copied EXACTLY from original TestSetup)
73+ const MockCloseButton = React . forwardRef < HTMLButtonElement , CloseButtonProps > ( ( { children, ...props } , ref ) => (
74+ < button ref = { ref } data-testid = "close-button" { ...props } >
75+ { children }
76+ </ button >
77+ ) )
78+
79+ const MockDialogCloseTrigger = React . forwardRef < HTMLButtonElement , DialogCloseTriggerProps > (
80+ ( { children, asChild, insetEnd, top, position, ...props } , ref ) => {
81+ const context = useContext ( DialogContext )
82+ const buttonProps = { ...props , position, top, insetend : insetEnd , "data-testid" : "close-button" }
83+
84+ if ( asChild && React . isValidElement ( children ) ) {
85+ return React . cloneElement ( children , {
86+ ...buttonProps ,
87+ // @ts -ignore
88+ ref,
89+ onClick : ( e : React . MouseEvent < HTMLButtonElement > ) => {
90+ if ( children . props . onClick ) children . props . onClick ( e )
91+ if ( ! e . defaultPrevented ) context ?. onClose ?.( )
92+ } ,
93+ } )
94+ }
95+ return (
96+ < MockCloseButton
97+ ref = { ref }
98+ onClick = { ( e ) => {
99+ if ( ! e . defaultPrevented ) context ?. onClose ?.( )
100+ } }
101+ { ...buttonProps }
102+ >
103+ { children }
104+ </ MockCloseButton >
105+ )
106+ } ,
107+ )
108+ // endregion
29109
110+ return {
111+ ...( await vi . importActual < typeof import ( "@chakra-ui/react" ) > ( "@chakra-ui/react" ) ) ,
112+ ChakraProvider : ( { children } : { children : ReactNode } ) : React . ReactElement => (
113+ < ThemeContext . Provider value = { { theme : { _config : { } } } } > { children } </ ThemeContext . Provider >
114+ ) ,
115+ Portal : ( { children, disabled } : { children : ReactNode ; disabled ?: boolean } ) : React . ReactElement =>
116+ disabled ? < > { children } </ > : < div data-testid = "portal" > { children } </ div > ,
117+ CloseButton : MockCloseButton ,
118+ IconButton : React . forwardRef < HTMLButtonElement , ComponentPropsWithoutRef < "button" > > (
119+ ( props , ref ) : React . ReactElement => < button data-testid = "icon-button" ref = { ref } { ...props } /> ,
120+ ) ,
121+ defineRecipe : vi . fn ( ( ) => ( { } ) ) ,
122+ Dialog : {
123+ Root : ( { children, open, onOpenChange, ...props } : DialogRootProps ) => {
124+ const [ isOpen , setIsOpen ] = useState ( open ?? false )
125+ const isControlled = open !== undefined
126+ const effectiveOpen = isControlled ? open : isOpen
127+ const handleOpenChange = ( newOpen : boolean ) => {
128+ if ( ! isControlled ) setIsOpen ( newOpen )
129+ onOpenChange ?.( { open : newOpen } )
130+ }
131+ return (
132+ < div data-testid = "dialog-root" { ...props } >
133+ < DialogContext . Provider value = { { onClose : ( ) => handleOpenChange ( false ) , isOpen : effectiveOpen } } >
134+ { React . Children . map ( children , ( child ) => {
135+ if ( ! React . isValidElement ( child ) ) return child
136+ const childType =
137+ typeof child . type !== "string" && "displayName" in child . type ? child . type . displayName : undefined
138+ if ( childType === "DialogTrigger" ) {
139+ return React . cloneElement ( child as React . ReactElement < DialogTriggerProps > , {
140+ isOpen : effectiveOpen ,
141+ onOpen : ( ) => handleOpenChange ( true ) ,
142+ } )
143+ }
144+ if ( childType === "DialogCloseTrigger" ) {
145+ const closeTriggerChild = child as RefElement
146+ return < MockDialogCloseTrigger ref = { closeTriggerChild . ref } asChild { ...closeTriggerChild . props } />
147+ }
148+ if ( childType === "DialogContent" && ! effectiveOpen ) return null
149+ return child
150+ } ) }
151+ </ DialogContext . Provider >
152+ </ div >
153+ )
154+ } ,
155+ Trigger : ( { children, isOpen, onOpen, ...props } : DialogTriggerProps ) => (
156+ < button data-testid = "dialog-trigger" onClick = { ( ) => ! isOpen && onOpen ?.( ) } { ...props } >
157+ { children }
158+ </ button >
159+ ) ,
160+ Positioner : ( { children } : { children : ReactNode } ) => < div data-testid = "dialog-positioner" > { children } </ div > ,
161+ Backdrop : ( props : ComponentPropsWithoutRef < "div" > ) => < div data-testid = "dialog-backdrop" { ...props } /> ,
162+ Content : React . forwardRef < HTMLDivElement , DialogContentProps > ( ( { portalled, backdrop, ...props } , ref ) => (
163+ < div data-testid = "dialog-content" ref = { ref } { ...props } />
164+ ) ) ,
165+ CloseTrigger : MockDialogCloseTrigger ,
166+ Footer : ( props : ComponentPropsWithoutRef < "div" > ) => < div data-testid = "dialog-footer" { ...props } /> ,
167+ Header : ( props : ComponentPropsWithoutRef < "div" > ) => < div data-testid = "dialog-header" { ...props } /> ,
168+ Body : ( props : ComponentPropsWithoutRef < "div" > ) => < div data-testid = "dialog-body" { ...props } /> ,
169+ Title : ( props : ComponentPropsWithoutRef < "h2" > ) => < h2 data-testid = "dialog-title" { ...props } /> ,
170+ Description : ( props : ComponentPropsWithoutRef < "p" > ) => < p data-testid = "dialog-description" { ...props } /> ,
171+ ActionTrigger : ( props : ComponentPropsWithoutRef < "button" > ) => (
172+ < button data-testid = "dialog-action-trigger" { ...props } />
173+ ) ,
174+ } ,
175+ }
176+ } )
30177// endregion
31178
32179// region Type Aliases
@@ -188,7 +335,9 @@ describe("Dialog", (): void => {
188335 render (
189336 < Wrapper >
190337 < DialogRoot open >
191- < DialogCloseTrigger data-testid = "close-trigger" > Close</ DialogCloseTrigger >
338+ < DialogContent >
339+ < DialogCloseTrigger data-testid = "close-trigger" > Close</ DialogCloseTrigger >
340+ </ DialogContent >
192341 </ DialogRoot >
193342 </ Wrapper > ,
194343 )
@@ -358,4 +507,5 @@ describe("Dialog", (): void => {
358507 expect ( actionTrigger ) . toHaveTextContent ( "Action" )
359508 } )
360509} )
510+
361511// endregion
0 commit comments