|
| 1 | +import { describe, expect, it } from 'vitest'; |
| 2 | +import { page, userEvent } from 'vitest/browser'; |
| 3 | +import { render } from 'vitest-browser-react'; |
| 4 | +import { useState } from '@wordpress/element'; |
| 5 | +import { Menu } from '..'; |
| 6 | +import Modal from '../../modal'; |
| 7 | +// Load the body scroll-lock rule that WordPress enqueues for Modal. |
| 8 | +// eslint-disable-next-line @wordpress/no-non-module-stylesheet-imports |
| 9 | +import '../../modal/style.scss'; |
| 10 | + |
| 11 | +const MenuWithModal = () => { |
| 12 | + const [ isModalOpen, setIsModalOpen ] = useState( false ); |
| 13 | + |
| 14 | + return ( |
| 15 | + <> |
| 16 | + <Menu> |
| 17 | + <Menu.TriggerButton>Open dropdown</Menu.TriggerButton> |
| 18 | + <Menu.Popover> |
| 19 | + <Menu.Item onClick={ () => setIsModalOpen( true ) }> |
| 20 | + Open modal |
| 21 | + </Menu.Item> |
| 22 | + </Menu.Popover> |
| 23 | + </Menu> |
| 24 | + { isModalOpen && ( |
| 25 | + <Modal |
| 26 | + title="Modal" |
| 27 | + onRequestClose={ () => setIsModalOpen( false ) } |
| 28 | + > |
| 29 | + <button onClick={ () => setIsModalOpen( false ) }> |
| 30 | + Close modal |
| 31 | + </button> |
| 32 | + </Modal> |
| 33 | + ) } |
| 34 | + </> |
| 35 | + ); |
| 36 | +}; |
| 37 | + |
| 38 | +describe( 'Menu scroll lock', () => { |
| 39 | + it.each( [ 'pointer', 'keyboard' ] )( |
| 40 | + 'should keep body scroll locked when opening a modal with the %s and restore it after closing', |
| 41 | + async ( interaction ) => { |
| 42 | + const user = userEvent.setup(); |
| 43 | + const initialOverflow = getComputedStyle( document.body ).overflow; |
| 44 | + expect( initialOverflow ).not.toBe( 'hidden' ); |
| 45 | + await render( <MenuWithModal /> ); |
| 46 | + |
| 47 | + if ( interaction === 'pointer' ) { |
| 48 | + await user.click( |
| 49 | + page.getByRole( 'button', { name: 'Open dropdown' } ) |
| 50 | + ); |
| 51 | + } else { |
| 52 | + await user.tab(); |
| 53 | + await user.keyboard( '{ArrowDown}' ); |
| 54 | + } |
| 55 | + |
| 56 | + await expect.element( page.getByRole( 'menu' ) ).toBeVisible(); |
| 57 | + await expect |
| 58 | + .poll( () => getComputedStyle( document.body ).overflow ) |
| 59 | + .toBe( 'hidden' ); |
| 60 | + |
| 61 | + if ( interaction === 'pointer' ) { |
| 62 | + await user.click( |
| 63 | + page.getByRole( 'menuitem', { name: 'Open modal' } ) |
| 64 | + ); |
| 65 | + } else { |
| 66 | + await user.keyboard( '{Enter}' ); |
| 67 | + } |
| 68 | + |
| 69 | + await expect |
| 70 | + .element( page.getByRole( 'menu' ) ) |
| 71 | + .not.toBeInTheDocument(); |
| 72 | + await expect.element( page.getByRole( 'dialog' ) ).toBeVisible(); |
| 73 | + expect( getComputedStyle( document.body ).overflow ).toBe( |
| 74 | + 'hidden' |
| 75 | + ); |
| 76 | + |
| 77 | + if ( interaction === 'pointer' ) { |
| 78 | + await user.click( |
| 79 | + page.getByRole( 'button', { name: 'Close modal' } ) |
| 80 | + ); |
| 81 | + } else { |
| 82 | + await user.keyboard( '{Escape}' ); |
| 83 | + } |
| 84 | + |
| 85 | + await expect |
| 86 | + .element( page.getByRole( 'dialog' ) ) |
| 87 | + .not.toBeInTheDocument(); |
| 88 | + await expect |
| 89 | + .poll( () => getComputedStyle( document.body ).overflow ) |
| 90 | + .toBe( initialOverflow ); |
| 91 | + } |
| 92 | + ); |
| 93 | +} ); |
0 commit comments