diff --git a/packages/components/src/confirm-dialog/test/index.js b/packages/components/src/confirm-dialog/test/index.js index 4aecd43f570861..cbcdf032eb95d4 100644 --- a/packages/components/src/confirm-dialog/test/index.js +++ b/packages/components/src/confirm-dialog/test/index.js @@ -4,7 +4,6 @@ import { render, screen, - fireEvent, waitForElementToBeRemoved, waitFor, } from '@testing-library/react'; @@ -18,6 +17,18 @@ import { ConfirmDialog } from '..'; const noop = () => {}; describe( 'Confirm', () => { + let mockedDocumentHasFocus; + + beforeEach( () => { + mockedDocumentHasFocus = jest + .spyOn( document, 'hasFocus' ) + .mockImplementation( () => true ); + } ); + + afterEach( () => { + mockedDocumentHasFocus.mockRestore(); + } ); + describe( 'Confirm component', () => { describe( 'Structure', () => { it( 'should render correctly', () => { @@ -137,24 +148,27 @@ describe( 'Confirm', () => { } ); it( 'should not render if dialog is closed by clicking the overlay, and the `onCancel` callback should be called', async () => { + const user = userEvent.setup(); const onCancel = jest.fn().mockName( 'onCancel()' ); render( - - Are you sure? - +
+ + Are you sure? + +
); const confirmDialog = screen.getByRole( 'dialog' ); - //The overlay click is handled by detecting an onBlur from the modal frame. - // TODO: replace with `@testing-library/user-event` - fireEvent.blur( confirmDialog ); + await user.click( + screen.getByLabelText( 'Under the overlay' ) + ); await waitForElementToBeRemoved( confirmDialog ); expect( confirmDialog ).not.toBeInTheDocument(); - expect( onCancel ).toHaveBeenCalled(); + await waitFor( () => expect( onCancel ).toHaveBeenCalled() ); } ); it( 'should not render if dialog is closed by pressing `Escape`, and the `onCancel` callback should be called', async () => { @@ -315,23 +329,22 @@ describe( 'Confirm', () => { } ); it( 'should call the `onCancel` callback if the overlay is clicked', async () => { + const user = userEvent.setup(); const onCancel = jest.fn().mockName( 'onCancel()' ); render( - - Are you sure? - +
+ + Are you sure? + +
); - const confirmDialog = screen.getByRole( 'dialog' ); - - //The overlay click is handled by detecting an onBlur from the modal frame. - // TODO: replace with `@testing-library/user-event` - fireEvent.blur( confirmDialog ); + await user.click( screen.getByLabelText( 'Under the overlay' ) ); // Wait for a DOM side effect here, so that the `queueBlurCheck` in the // `useFocusOutside` hook properly executes its timeout task. diff --git a/packages/components/src/modal/stories/index.tsx b/packages/components/src/modal/stories/index.tsx index 7f414d47c2d11c..89ac8c7e126bbd 100644 --- a/packages/components/src/modal/stories/index.tsx +++ b/packages/components/src/modal/stories/index.tsx @@ -78,6 +78,15 @@ const Template: ComponentStory< typeof Modal > = ( { + + + +const FocusOutsideComponent = ( { onFocusOutside: callback } ) => { + const focusOutsideProps = useFocusOutside( callback ); + + return ( +
+ { /* Wrapper */ } +
+ + + +
+ +
- - - -); + ); +}; describe( 'useFocusOutside', () => { + let mockedDocumentHasFocus; + + beforeEach( () => { + mockedDocumentHasFocus = jest + .spyOn( document, 'hasFocus' ) + .mockImplementation( () => true ); + } ); + + afterEach( () => { + mockedDocumentHasFocus.mockRestore(); + } ); + it( 'should not call handler if focus shifts to element within component', async () => { const mockOnFocusOutside = jest.fn(); const user = userEvent.setup(); @@ -72,24 +88,30 @@ describe( 'useFocusOutside', () => { ); // Click and focus button inside the wrapper - await user.click( - screen.getByRole( 'button', { name: 'Button inside the wrapper' } ) - ); + const buttonInside = screen.getByRole( 'button', { + name: 'Button inside the wrapper', + } ); + await user.click( buttonInside ); - expect( mockOnFocusOutside ).not.toHaveBeenCalled(); + // TODO: find a way to guarantee that the callback does not fire + await new Promise( ( r ) => setTimeout( r, 200 ) ); + expect( mockOnFocusOutside ).not.toHaveBeenCalled(); // Click and focus button outside the wrapper - await user.click( - screen.getByRole( 'button', { name: 'Button outside the wrapper' } ) - ); + const buttonOutside = screen.getByRole( 'button', { + name: 'Button outside the wrapper', + } ); + await user.click( buttonOutside ); - expect( mockOnFocusOutside ).toHaveBeenCalled(); + await waitFor( () => + expect( mockOnFocusOutside ).toHaveBeenCalledTimes( 1 ) + ); } ); it( 'should not call handler if focus shifts outside the component when the document does not have focus', async () => { // Force document.hasFocus() to return false to simulate the window/document losing focus // See https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus. - const mockedDocumentHasFocus = jest + mockedDocumentHasFocus = jest .spyOn( document, 'hasFocus' ) .mockImplementation( () => false ); const mockOnFocusOutside = jest.fn();