Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'import { afterEach, expect, it, vi } from 'vitest'import { ModalShell } from '../ModalShell'import { topmostDialog } from '../focusable'
afterEach(cleanup)
it('traps focus, closes on Escape, and restores focus to the launcher', () => { const launcher = document.createElement('button') document.body.appendChild(launcher) launcher.focus() const onClose = vi.fn() const content = (open: boolean) => <ModalShell isOpen={open} onClose={onClose} titleId="dialog-title"> <h2 id="dialog-title">Test dialog</h2><button>First</button><button>Last</button> </ModalShell> const { rerender } = render(content(true)) const dialog = screen.getByRole('dialog', { name: 'Test dialog' }) expect(dialog).toHaveFocus() launcher.focus() expect(dialog).toHaveFocus() const [first, last] = screen.getAllByRole('button').filter(button => button !== launcher) fireEvent.keyDown(document, { key: 'Tab', shiftKey: true }) expect(last).toHaveFocus() last.focus() fireEvent.keyDown(document, { key: 'Tab' }) expect(first).toHaveFocus() fireEvent.keyDown(document, { key: 'Escape' }) expect(onClose).toHaveBeenCalledOnce() rerender(content(false)) expect(launcher).toHaveFocus() launcher.remove()})
it('leaves Escape and Tab to a dialog opened above it', async () => { const onClose = vi.fn() render(<ModalShell isOpen onClose={onClose} titleId="dialog-title"><h2 id="dialog-title">Parent</h2><button>Parent button</button></ModalShell>) const nested = document.createElement('div') nested.setAttribute('role', 'dialog') nested.setAttribute('aria-modal', 'true') nested.style.position = 'fixed' nested.style.zIndex = '1100' const input = document.createElement('input') nested.appendChild(input) document.body.appendChild(nested) const parent = document.querySelector<HTMLElement>('[aria-labelledby="dialog-title"]')! await waitFor(() => { expect(parent).toHaveAttribute('aria-modal', 'false') expect(parent).toHaveAttribute('aria-hidden', 'true') }) input.focus() fireEvent.keyDown(document, { key: 'Escape' }) fireEvent.keyDown(document, { key: 'Tab' }) expect(onClose).not.toHaveBeenCalled() expect(input).toHaveFocus() input.blur() fireEvent.keyDown(document, { key: 'Escape' }) expect(onClose).not.toHaveBeenCalled() nested.remove() await waitFor(() => { expect(parent).toHaveAttribute('aria-modal', 'true') expect(parent).not.toHaveAttribute('aria-hidden') })})
it('uses visual overlay order when a lower-layer dialog portals later', () => { const account = document.createElement('div') account.setAttribute('role', 'dialog') account.style.zIndex = '1300' const picker = document.createElement('div') picker.setAttribute('role', 'dialog') picker.style.zIndex = '1100' document.body.append(account, picker) expect(topmostDialog()).toBe(account) account.remove() picker.remove()})