test(03-12): RED — WR-07 focus trap Tab/Shift+Tab cycle tests
Add two failing tests for the focus trap: - Tab from last focusable element must wrap to first inside dialog - Shift+Tab from first focusable element must wrap to last inside dialog Both fail today because EventForm only calls .focus() once on open; Tab escapes the modal to background content.
This commit is contained in:
@@ -616,4 +616,58 @@ describe('EventForm — Plan 03-12 gap closures', () => {
|
||||
// Should return a YYYY-MM-DD string
|
||||
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/)
|
||||
})
|
||||
|
||||
// ── WR-07: Tab/Shift+Tab focus trap ──────────────────────────────────────
|
||||
//
|
||||
// The dialog must trap Tab focus: pressing Tab from the last focusable element
|
||||
// wraps to the first, and Shift+Tab from the first wraps to the last.
|
||||
// Today EventForm only calls .focus() once on open — Tab escapes the modal.
|
||||
|
||||
it('WR-07: Tab from last focusable element wraps focus to first inside dialog', () => {
|
||||
renderForm({ mode: 'create' })
|
||||
|
||||
const dialog = screen.getByRole('dialog')
|
||||
const focusable = Array.from(
|
||||
dialog.querySelectorAll<HTMLElement>(
|
||||
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
|
||||
),
|
||||
)
|
||||
expect(focusable.length).toBeGreaterThan(1)
|
||||
|
||||
const lastElement = focusable[focusable.length - 1]
|
||||
const firstElement = focusable[0]
|
||||
|
||||
// Focus the last element, then dispatch Tab
|
||||
lastElement.focus()
|
||||
expect(document.activeElement).toBe(lastElement)
|
||||
|
||||
fireEvent.keyDown(dialog, { key: 'Tab', shiftKey: false })
|
||||
|
||||
// WR-07 fix: focus should have wrapped to first element inside dialog
|
||||
expect(document.activeElement).toBe(firstElement)
|
||||
})
|
||||
|
||||
it('WR-07: Shift+Tab from first focusable element wraps focus to last inside dialog', () => {
|
||||
renderForm({ mode: 'create' })
|
||||
|
||||
const dialog = screen.getByRole('dialog')
|
||||
const focusable = Array.from(
|
||||
dialog.querySelectorAll<HTMLElement>(
|
||||
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
|
||||
),
|
||||
)
|
||||
expect(focusable.length).toBeGreaterThan(1)
|
||||
|
||||
const firstElement = focusable[0]
|
||||
const lastElement = focusable[focusable.length - 1]
|
||||
|
||||
// Focus the first element, then dispatch Shift+Tab
|
||||
firstElement.focus()
|
||||
expect(document.activeElement).toBe(firstElement)
|
||||
|
||||
fireEvent.keyDown(dialog, { key: 'Tab', shiftKey: true })
|
||||
|
||||
// WR-07 fix: focus should have wrapped to last element inside dialog
|
||||
expect(document.activeElement).toBe(lastElement)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user