import type {ComponentProps} from 'react'; import { render, screen, userEvent, waitForElementToBeRemoved, } from 'sentry-test/reactTestingLibrary'; import DeprecatedDropdownMenu from 'sentry/components/deprecatedDropdownMenu'; jest.useFakeTimers(); describe('dropdownMenuDeprecated', function () { const DeprecatedDropdownImplementation = ( props: Partial<ComponentProps<typeof DeprecatedDropdownMenu>> = {} ) => { return ( <DeprecatedDropdownMenu {...props}> {({getRootProps, getActorProps, getMenuProps, isOpen}) => ( <span {...getRootProps({})}> <button {...getActorProps({})}>Open Dropdown</button> {isOpen && ( <ul {...getMenuProps({})}> <li>Dropdown Menu Item 1</li> </ul> )} </span> )} </DeprecatedDropdownMenu> ); }; it('renders', function () { const {container} = render(<DeprecatedDropdownImplementation />); expect(container).toSnapshot(); }); it('can toggle dropdown menu with actor', function () { render(<DeprecatedDropdownImplementation />); userEvent.click(screen.getByRole('button')); expect(screen.getByRole('listbox')).toBeInTheDocument(); userEvent.click(screen.getByRole('button')); expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); }); it('closes dropdown when clicking on anything in menu', function () { render(<DeprecatedDropdownImplementation />); userEvent.click(screen.getByRole('button')); userEvent.click(screen.getByRole('listitem')); expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); }); it('closes dropdown when clicking outside of menu', async function () { render( <div data-test-id="outside-element"> <DeprecatedDropdownImplementation /> </div> ); userEvent.click(screen.getByRole('button')); userEvent.click(screen.getByTestId('outside-element')); await waitForElementToBeRemoved(() => screen.queryByRole('listbox')); }); it('closes dropdown when pressing escape', function () { render(<DeprecatedDropdownImplementation />); userEvent.click(screen.getByRole('button')); userEvent.keyboard('{Escape}'); expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); }); it('ignores "Escape" key if `closeOnEscape` is false', function () { render(<DeprecatedDropdownImplementation closeOnEscape={false} />); userEvent.click(screen.getByRole('button')); userEvent.keyboard('{Escape}'); expect(screen.getByRole('listbox')).toBeInTheDocument(); }); it('keeps dropdown open when clicking on anything in menu with `keepMenuOpen` prop', function () { render(<DeprecatedDropdownImplementation keepMenuOpen />); userEvent.click(screen.getByRole('button')); userEvent.click(screen.getByRole('listitem')); expect(screen.getByRole('listbox')).toBeInTheDocument(); }); it('render prop getters all extend props and call original onClick handlers', function () { const rootClick = jest.fn(); const actorClick = jest.fn(); const menuClick = jest.fn(); render( <DeprecatedDropdownMenu keepMenuOpen> {({getRootProps, getActorProps, getMenuProps, isOpen}) => ( <span {...getRootProps({onClick: rootClick})} data-test-id="root"> <button {...getActorProps({onClick: actorClick})} data-test-id="actor"> Open Dropdown </button> {isOpen && ( <ul {...getMenuProps({onClick: menuClick})} data-test-id="menu"> <li>Dropdown Menu Item 1</li> </ul> )} </span> )} </DeprecatedDropdownMenu> ); expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); userEvent.click(screen.getByTestId('root')); expect(rootClick).toHaveBeenCalled(); userEvent.click(screen.getByTestId('actor')); expect(actorClick).toHaveBeenCalled(); userEvent.click(screen.getByTestId('menu')); expect(menuClick).toHaveBeenCalled(); expect(screen.queryByRole('listbox')).toBeInTheDocument(); }); it('always rendered menus should attach document event listeners only when opened', function () { const addSpy = jest.spyOn(document, 'addEventListener'); const removeSpy = jest.spyOn(document, 'removeEventListener'); render( <DeprecatedDropdownMenu alwaysRenderMenu> {({getRootProps, getActorProps, getMenuProps}) => ( <span {...getRootProps({className: 'root'})}> <button {...getActorProps({className: 'actor'})}>Open Dropdown</button> <ul {...getMenuProps({className: 'menu'})}> <li>Dropdown Menu Item 1</li> </ul> </span> )} </DeprecatedDropdownMenu> ); // Make sure this is only called when menu is open expect(addSpy).not.toHaveBeenCalled(); userEvent.click(screen.getByRole('button')); expect(addSpy).toHaveBeenCalled(); expect(removeSpy).not.toHaveBeenCalled(); userEvent.click(screen.getByRole('button')); expect(removeSpy).toHaveBeenCalled(); addSpy.mockRestore(); removeSpy.mockRestore(); }); it('does not close nested dropdown on actor clicks', function () { render( <DeprecatedDropdownMenu isNestedDropdown> {({getRootProps, getActorProps, getMenuProps}) => ( <span {...getRootProps({})}> <button {...getActorProps({})}>Open Dropdown</button> { <ul {...getMenuProps({})}> <li data-test-id="menu-item">Dropdown Menu Item 1</li> </ul> } </span> )} </DeprecatedDropdownMenu> ); userEvent.hover(screen.getByRole('button')); expect(screen.getByTestId('menu-item')).toBeInTheDocument(); userEvent.click(screen.getByRole('button')); // Should still be visible. expect(screen.getByTestId('menu-item')).toBeInTheDocument(); }); });