UNPKG

@vertisanpro/flowbite-react

Version:

Non-Official React components built for Flowbite and Tailwind CSS

118 lines (117 loc) 6.74 kB
import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React, { createRef, useState } from 'react'; import { describe, expect, it } from 'vitest'; import { Button } from '../Button'; import { TextInput } from '../TextInput'; import { Modal } from './Modal'; describe('Components / Modal', () => { it('should be closed by clicking outside if the "dismissible" prop is passed.', async () => { const user = userEvent.setup(); render(React.createElement(TestModal, { dismissible: true })); await user.click(triggerButton()); const modal = dialog(); expect(modal).toBeInTheDocument(); await user.click(dialogOverlay()); expect(modal).not.toBeInTheDocument(); }); it('should append to root element when root prop is provided', async () => { const root = document.createElement('div'); const user = userEvent.setup(); render(React.createElement(TestModal, { root: root })); const openButton = triggerButton(); await user.click(openButton); expect(within(root).getByRole('dialog')).toBeTruthy(); }); describe('A11y', () => { it('should have `role="dialog"`', async () => { const user = userEvent.setup(); render(React.createElement(TestModal, null)); const openButton = triggerButton(); await user.click(openButton); expect(dialog()).toBeDefined(); }); it('should have `aria-labelledby` equals to modal header id', async () => { const user = userEvent.setup(); render(React.createElement(TestModal, null)); const openButton = triggerButton(); await user.click(openButton); expect(dialog()).toHaveAttribute('aria-labelledby', 'test-dialog-header'); }); }); describe('Keyboard interactions', () => { it('should open `Modal` when `Space` is pressed on its toggle button', async () => { const user = userEvent.setup(); render(React.createElement(TestModal, null)); const openButton = triggerButton(); await user.click(openButton); const modal = dialog(); expect(modal).toBeInTheDocument(); }); it('should close `Modal` when `Space` is pressed on any of its buttons', async () => { const user = userEvent.setup(); render(React.createElement(TestModal, null)); const openButton = triggerButton(); await user.click(openButton); const modal = dialog(); const closeButton = within(modal).getAllByRole('button')[0]; expect(modal).toBeInTheDocument(); await user.click(closeButton); expect(modal).not.toBeInTheDocument(); }); it('should be closed by Esc key press.', async () => { const user = userEvent.setup(); render(React.createElement(TestModal, { dismissible: true })); await user.click(triggerButton()); const modal = dialog(); expect(modal).toBeInTheDocument(); await user.keyboard('[Escape]'); expect(modal).not.toBeInTheDocument(); }); it('should initially focus element provided by ref when `initialFocus={elementRef}`', async () => { const user = userEvent.setup(); const inputRef = createRef(); render(React.createElement(TestModal, { dismissible: true, inputRef: inputRef })); await user.click(triggerButton()); const modal = dialog(); expect(modal).toBeInTheDocument(); await waitFor(() => expect(document.activeElement).toEqual(input())); }); it('should focus back to button toggle when closing Modal', async () => { const user = userEvent.setup(); render(React.createElement("div", null, React.createElement(TestModal, { dismissible: true }), React.createElement("button", null, "Second button"))); await user.click(triggerButton()); const modal = dialog(); expect(modal).toBeInTheDocument(); await waitFor(() => expect(document.activeElement).toEqual(closeButton())); await user.click(dialogOverlay()); expect(modal).not.toBeInTheDocument(); // The following element is only focusable in the testing environment expect(document.activeElement).toEqual(document.body); await user.tab(); expect(document.activeElement).toEqual(triggerButton()); }); }); }); const TestModal = ({ root, dismissible = false, inputRef, }) => { const [open, setOpen] = useState(false); return (React.createElement(React.Fragment, null, React.createElement(Button, { onClick: () => setOpen(true) }, "Toggle modal"), React.createElement(Modal, { dismissible: dismissible, root: root, show: open, onClose: () => setOpen(false), initialFocus: inputRef }, React.createElement(Modal.Header, { id: "test-dialog-header" }, "Terms of Service"), React.createElement(Modal.Body, null, React.createElement("div", { className: "space-y-6" }, React.createElement("p", { className: "text-base leading-relaxed text-gray-500 dark:text-gray-400" }, "With less than a month to go before the European Union enacts new consumer privacy laws for its citizens, companies around the world are updating their terms of service agreements to comply."), React.createElement("p", { className: "text-base leading-relaxed text-gray-500 dark:text-gray-400" }, "The European Union\u2019s General Data Protection Regulation (G.D.P.R.) goes into effect on May 25 and is meant to ensure a common set of data rights in the European Union. It requires organizations to notify users as soon as possible of high-risk data breaches that could personally affect them.")), React.createElement(TextInput, { ref: inputRef, "data-testid": "text-input" })), React.createElement(Modal.Footer, null, React.createElement(Button, { onClick: () => setOpen(false) }, "I accept"), React.createElement(Button, { color: "gray", onClick: () => setOpen(false) }, "Decline"))))); }; const dialog = () => screen.getByRole('dialog'); const dialogOverlay = () => screen.getByTestId('modal-overlay'); const triggerButton = () => screen.getByRole('button', { name: 'Toggle modal' }); const input = () => screen.getByTestId('text-input'); const closeButton = () => screen.getByLabelText('Close');