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
|
// Should return a YYYY-MM-DD string
|
||||||
expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/)
|
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