Nychthemeron/packages/library/tests/use-roving-focus.spec.ts
2026-07-15 19:28:29 -04:00

71 lines
2.3 KiB
TypeScript

import { describe, it, expect, afterEach } from 'vitest'
import { ref } from 'vue'
import { useRovingFocus } from '../src/lib/use-roving-focus'
function makeItems(count: number) {
const items = Array.from({ length: count }, (_, i) => {
const el = document.createElement('button')
el.textContent = `item-${i}`
document.body.append(el)
return el
})
return items
}
function arrowDown() {
return new KeyboardEvent('keydown', { key: 'ArrowDown', cancelable: true })
}
function arrowUp() {
return new KeyboardEvent('keydown', { key: 'ArrowUp', cancelable: true })
}
describe('useRovingFocus', () => {
afterEach(() => {
document.body.replaceChildren()
})
it('focusIndex focuses the item at that index', () => {
const items = makeItems(3)
const { focusIndex } = useRovingFocus(ref(items))
focusIndex(1)
expect(document.activeElement).toBe(items[1])
})
it('focusIndex wraps by default when loop is not set', () => {
const items = makeItems(3)
const { focusIndex } = useRovingFocus(ref(items))
focusIndex(3)
expect(document.activeElement).toBe(items[0])
focusIndex(-1)
expect(document.activeElement).toBe(items[2])
})
it('focusIndex clamps instead of wrapping when loop is false', () => {
const items = makeItems(3)
const { focusIndex } = useRovingFocus(ref(items), { loop: false })
focusIndex(5)
expect(document.activeElement).toBe(items[2])
focusIndex(-5)
expect(document.activeElement).toBe(items[0])
})
it('handleKeydown moves focus forward/backward on ArrowDown/ArrowUp for vertical orientation', () => {
const items = makeItems(3)
const { handleKeydown } = useRovingFocus(ref(items))
items[0]!.focus()
handleKeydown(arrowDown())
expect(document.activeElement).toBe(items[1])
handleKeydown(arrowUp())
expect(document.activeElement).toBe(items[0])
})
it('handleKeydown jumps to first/last on Home/End', () => {
const items = makeItems(3)
const { handleKeydown } = useRovingFocus(ref(items))
items[1]!.focus()
handleKeydown(new KeyboardEvent('keydown', { key: 'End', cancelable: true }))
expect(document.activeElement).toBe(items[2])
handleKeydown(new KeyboardEvent('keydown', { key: 'Home', cancelable: true }))
expect(document.activeElement).toBe(items[0])
})
})