import { Fragment } from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import Dropdown from './Dropdown'; import { DOWN_ARROW, ENTER, ESCAPE, TAB, UP_ARROW } from './keyCodes'; describe('Dropdown', () => { afterEach(() => { jest.restoreAllMocks(); }); it('renders a menu of 6 items', () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); const { baseElement } = render( , ); expect(baseElement).toMatchSnapshot(); }); it('renders a menu of 3 items conditionally', () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); const renderOptions = true; const { baseElement } = render( {renderOptions && ( {[1, 2, 3, 4, 5, 6].map((x: any) => ( ))} {[7, 8, 9, 10, 11, 12].map((x: any) => ( ))} )} , ); expect(baseElement).toMatchSnapshot(); }); it('renders dropdown sections', () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); render( , ); const sectionLabels = screen.getAllByRole('presentation'); expect(sectionLabels).toHaveLength(2); }); it('renders a custom header', () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); render( This is my custom header

} id="ex-3" onDismiss={mockOnDismiss} >
, ); expect(screen.getByText('This is my custom header')).toBeVisible(); }); it('closes when esc key is pressed', () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); render( , ); fireEvent.keyDown(window.document, { keyCode: ESCAPE, }); expect(mockOnDismiss).toHaveBeenCalled(); }); it('closes when tab key is pressed', () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); render( , ); fireEvent.keyDown(window.document, { keyCode: TAB, }); expect(mockOnDismiss).toHaveBeenCalledTimes(1); }); it('changes active descendant when arrow keys are pressed', async () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); render( , ); // Wait for state change in Dropdown so that the first item has focus await waitFor(() => expect(screen.getByTestId('item-1')).toHaveFocus()); fireEvent.keyDown(window.document, { keyCode: DOWN_ARROW, }); expect(screen.getByTestId('item-2')).toHaveFocus(); fireEvent.keyDown(window.document, { keyCode: DOWN_ARROW, }); // eslint-disable-next-line testing-library/no-node-access -- No way to pass test id for the link and no dedicated method for finding active element by testing-library expect(document.activeElement).toHaveAttribute('href', 'https://pinterest.com'); fireEvent.keyDown(window.document, { keyCode: UP_ARROW, }); expect(screen.getByTestId('item-2')).toHaveFocus(); }); it('should call item onSelect when enter key is pressed', async () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); render( , ); // Wait for state change in Dropdown so that the first item has focus await waitFor(() => expect(screen.getByTestId('item-1')).toHaveFocus()); fireEvent.keyDown(window.document, { keyCode: DOWN_ARROW, }); expect(screen.getByTestId('item-2')).toHaveFocus(); fireEvent.keyDown(window.document, { keyCode: ENTER, }); expect(onSelectMock).toHaveBeenCalledTimes(1); }); it('should call link onClick when enter key is pressed', async () => { // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. const mockOnDismiss = jest.fn<[], undefined>(); const onSelectMock = jest.fn< [ { event: React.ChangeEvent; item: { label: string; subtext?: string; value: string; }; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const onClickMock = jest.fn< [ { dangerouslyDisableOnNavigation: () => void; event: React.MouseEvent | React.KeyboardEvent; mobileOnDismissStart: () => void; }, ], // @ts-expect-error - TS2344 - Type 'undefined' does not satisfy the constraint 'any[]'. undefined >(); const element = document.createElement('button'); render( , ); // Wait for state change in Dropdown so that the first item has focus await waitFor(() => expect(screen.getByTestId('item-1')).toHaveFocus()); fireEvent.keyDown(window.document, { keyCode: DOWN_ARROW, }); fireEvent.keyDown(window.document, { keyCode: ENTER, }); // eslint-disable-next-line testing-library/no-node-access -- Please fix the next time this file is touched! expect(document.activeElement).toHaveAttribute('href', 'https://pinterest.com/today'); // NOTE(rjames): I suspect this may be an RTL bug. This behavior works fine // when testing manually, and this test passes if using the click event below. // But for some reason the keyDown event isn't triggering the handler, // even though the activeElement is correct. // fireEvent.keyDown(window.document, { // keyCode: ENTER, // }); // fireEvent.click(screen.getByText(/External Item 3/)); // expect(onClickMock).toHaveBeenCalledTimes(1); }); });