2222 * SOFTWARE.
2323 */
2424
25- import { render , screen , waitFor } from '@testing-library/react'
25+ import { act , fireEvent , render , screen } from '@testing-library/react'
2626import { vi } from 'vitest'
2727import '@testing-library/jest-dom'
28+ import { useState } from 'react'
2829
2930import { Tray } from '../index'
3031import type { TrayProps } from '../props'
3132
3233describe ( '<Tray />' , async ( ) => {
33- it ( 'should render nothing and have a node with no parent when closed' , async ( ) => {
34+ beforeAll ( ( ) => {
35+ vi . useFakeTimers ( )
36+ } )
37+
38+ afterAll ( ( ) => {
39+ vi . useRealTimers ( )
40+ } )
41+
42+ it ( 'should render nothing and have a node with no parent when closed' , ( ) => {
3443 render ( < Tray label = "Tray Example" > Hello Tray</ Tray > )
3544
3645 const trayContent = screen . queryByText ( 'Hello Tray' )
3746 expect ( trayContent ) . not . toBeInTheDocument ( )
3847 } )
3948
40- it ( 'should render children and have a node with a parent when open' , async ( ) => {
49+ it ( 'should render children and have a node with a parent when open' , ( ) => {
4150 render (
4251 < Tray label = "Tray Example" open >
4352 Hello Tray
@@ -48,31 +57,30 @@ describe('<Tray />', async () => {
4857 expect ( trayContent ) . toBeInTheDocument ( )
4958 } )
5059
51- it ( 'should apply the a11y attributes' , async ( ) => {
60+ it ( 'should apply the a11y attributes' , ( ) => {
5261 render (
5362 < Tray label = "Tray Example" open >
5463 Hello Tray
5564 </ Tray >
5665 )
5766 const tray = screen . getByRole ( 'dialog' )
58-
5967 expect ( tray ) . toHaveAttribute ( 'aria-label' , 'Tray Example' )
6068 } )
6169
62- it ( 'should support onOpen prop' , async ( ) => {
70+ it ( 'should support onOpen prop' , ( ) => {
6371 const onOpen = vi . fn ( )
6472 render (
6573 < Tray label = "Tray Example" open onOpen = { onOpen } >
6674 Hello Tray
6775 </ Tray >
6876 )
69-
70- await waitFor ( ( ) => {
71- expect ( onOpen ) . toHaveBeenCalled ( )
77+ act ( ( ) => {
78+ vi . runAllTimers ( )
7279 } )
80+ expect ( onOpen ) . toHaveBeenCalled ( )
7381 } )
7482
75- it ( 'should support onClose prop' , async ( ) => {
83+ it ( 'should support onClose prop' , ( ) => {
7684 const onClose = vi . fn ( )
7785
7886 const { rerender } = render (
@@ -87,13 +95,13 @@ describe('<Tray />', async () => {
8795 Hello Tray
8896 </ Tray >
8997 )
90-
91- await waitFor ( ( ) => {
92- expect ( onClose ) . toHaveBeenCalled ( )
98+ act ( ( ) => {
99+ vi . runAllTimers ( )
93100 } )
101+ expect ( onClose ) . toHaveBeenCalled ( )
94102 } )
95103
96- it ( 'should take a prop for finding default focus' , async ( ) => {
104+ it ( 'should take a prop for finding default focus' , ( ) => {
97105 render (
98106 < Tray
99107 label = "Tray Example"
@@ -106,10 +114,10 @@ describe('<Tray />', async () => {
106114 </ Tray >
107115 )
108116 const input = screen . getByLabelText ( 'my-input-label' )
109-
110- await waitFor ( ( ) => {
111- expect ( document . activeElement ) . toBe ( input )
117+ act ( ( ) => {
118+ vi . runAllTimers ( )
112119 } )
120+ expect ( document . activeElement ) . toBe ( input )
113121 } )
114122
115123 describe ( 'transition()' , ( ) => {
@@ -139,7 +147,6 @@ describe('<Tray />', async () => {
139147 }
140148 }
141149 }
142-
143150 for ( const dir in placements ) {
144151 describe ( `when text direction is '${ dir } '` , ( ) => {
145152 for ( const placement in placements [ dir ] . enteringPlacements ) {
@@ -158,15 +165,16 @@ describe('<Tray />', async () => {
158165 Hello
159166 </ Tray >
160167 )
161- await waitFor ( ( ) => {
162- expect ( onEntered ) . toHaveBeenCalled ( )
168+ act ( ( ) => {
169+ vi . runAllTimers ( )
163170 } )
171+ expect ( onEntered ) . toHaveBeenCalled ( )
164172 } )
165173 }
166174
167175 for ( const placement in placements [ dir ] . exitingPlacements ) {
168176 const val = placements [ dir ] . exitingPlacements [ placement ]
169- it ( `returns ${ val } for ${ placement } when exiting` , async ( ) => {
177+ it ( `returns ${ val } for ${ placement } when exiting` , ( ) => {
170178 const onExited = vi . fn ( )
171179 document . documentElement . setAttribute ( 'dir' , dir )
172180
@@ -192,12 +200,79 @@ describe('<Tray />', async () => {
192200 Hello
193201 </ Tray >
194202 )
195- await waitFor ( ( ) => {
196- expect ( onExited ) . toHaveBeenCalled ( )
203+ act ( ( ) => {
204+ vi . runAllTimers ( )
197205 } )
206+ expect ( onExited ) . toHaveBeenCalled ( )
198207 } )
199208 }
200209 } )
201210 }
202211 } )
212+
213+ it ( 'should open, close via dismiss, and reopen with shouldCloseOnDocumentClick enabled' , async ( ) => {
214+ const onDismiss = vi . fn ( )
215+ const onEntered = vi . fn ( )
216+ const onExited = vi . fn ( )
217+
218+ const TrayWithButton = ( ) => {
219+ const [ isOpen , setIsOpen ] = useState ( false )
220+
221+ const handleDismiss = ( ) => {
222+ setIsOpen ( false )
223+ onDismiss ( )
224+ }
225+
226+ return (
227+ < div >
228+ < div > Outside of Tray</ div >
229+ < button onClick = { ( ) => setIsOpen ( ! isOpen ) } > Toggle Tray</ button >
230+ < Tray
231+ label = "Tray Example"
232+ shouldCloseOnDocumentClick
233+ open = { isOpen }
234+ onDismiss = { handleDismiss }
235+ onEntered = { onEntered }
236+ onExited = { onExited }
237+ >
238+ < div > Tray Content</ div >
239+ </ Tray >
240+ </ div >
241+ )
242+ }
243+ render ( < TrayWithButton /> )
244+ // 1. Open Tray
245+ expect ( screen . queryByText ( 'Tray Content' ) ) . not . toBeInTheDocument ( )
246+ const button = screen . getByText ( 'Toggle Tray' )
247+ fireEvent . click ( button , { button : 0 , detail : 1 } )
248+ act ( ( ) => {
249+ vi . runAllTimers ( )
250+ } )
251+ expect ( onEntered ) . toHaveBeenCalled ( )
252+ expect ( onDismiss ) . not . toHaveBeenCalled ( )
253+ expect ( screen . getByText ( 'Tray Content' ) ) . toBeInTheDocument ( )
254+ // 2. Close Tray be clicking outside it
255+ // event.detail and button are needed because FocusRegion.ts/handleDocumentClick
256+ fireEvent . click ( document , { button : 0 , detail : 1 } )
257+ act ( ( ) => {
258+ vi . runAllTimers ( )
259+ } )
260+ expect ( onDismiss ) . toHaveBeenCalled ( )
261+ expect ( onExited ) . toHaveBeenCalled ( )
262+ expect ( screen . queryByText ( 'Tray Content' ) ) . not . toBeInTheDocument ( )
263+
264+ onEntered . mockClear ( )
265+ onDismiss . mockClear ( )
266+ onExited . mockClear ( )
267+
268+ // 3. click Button again, Tray should reopen.
269+ fireEvent . click ( button , { button : 0 , detail : 1 } )
270+ act ( ( ) => {
271+ vi . runAllTimers ( )
272+ } )
273+ expect ( onEntered ) . toHaveBeenCalled ( )
274+ expect ( onDismiss ) . not . toHaveBeenCalled ( )
275+ expect ( onExited ) . not . toHaveBeenCalled ( )
276+ expect ( screen . getByText ( 'Tray Content' ) ) . toBeInTheDocument ( )
277+ } )
203278} )
0 commit comments