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);
});
});