diff --git a/packages/aria-voyager/src/navigation-patterns/active-descendent-strategy.ts b/packages/aria-voyager/src/navigation-patterns/active-descendent-strategy.ts index 9bdd21d1..d7cd3f90 100644 --- a/packages/aria-voyager/src/navigation-patterns/active-descendent-strategy.ts +++ b/packages/aria-voyager/src/navigation-patterns/active-descendent-strategy.ts @@ -33,11 +33,11 @@ export class ActiveDescendentStrategy implements NavigationPattern, FocusStrateg } handleFocus() { - const multiSelection = - this.control.capabilities.multiSelection && this.control.options.multiple; + // const multiSelection = + // this.control.capabilities.multiSelection && this.control.options.multiple; const selectionPresent = this.control.selection.length > 0; - if (multiSelection && selectionPresent) { + if (selectionPresent) { this.activateItem(this.control.selection[0]); } else { this.activateItem(this.control.items[0]); diff --git a/packages/aria-voyager/tests/listbox/multi-selection.test.ts b/packages/aria-voyager/tests/listbox/multi-selection.test.ts deleted file mode 100644 index 493acc26..00000000 --- a/packages/aria-voyager/tests/listbox/multi-selection.test.ts +++ /dev/null @@ -1,272 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { Listbox } from '../../src'; -import { createMultiSelectListWithFruits } from './-shared'; - -describe('Listbox Multi Selection', () => { - describe('When Focus', () => { - it('select no items', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - - list.dispatchEvent(new FocusEvent('focusin')); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - }); - }); - - describe('With Pointer', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - - it('select second item', () => { - secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('select third item with `Meta` key', () => { - thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, metaKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - - it('deselect second item with `Meta` key', () => { - secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, metaKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - - it('select third to first item with `Shift` key', () => { - firstItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - }); - - describe('With Keyboard', () => { - describe('select with `Home`', () => { - it('select from third to first item with `Home` and `Shift` key', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - - thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - }); - - describe('select with `End`', () => { - it('select from third to first item with `Home` and `Shift` key', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - - thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - }); - - describe('select NEXT', () => { - describe('select with `ArrowDown` and `Shift`', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - // activate first item - it('focus the list to activate first item', () => { - list.dispatchEvent(new FocusEvent('focusin')); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowDown` and `Shift` key to select from first to second item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowDown` and `Shift` key to select from first to third item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - }); - - describe('select with `ArrowDown` and release `Shift`', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - // activate first item - it('focus the list to activate first item', () => { - list.dispatchEvent(new FocusEvent('focusin')); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowDown` and `Shift` key to select from first to second item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('Release shift', () => { - list.dispatchEvent(new KeyboardEvent('keyup')); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - }); - }); - - describe('select with PREV', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - it('use `End` key to activate last item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowUp` and `Shift` key to select third and second item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - - it('use `ArrowUp` and `Shift` key to select thirdt to first item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - }); - - describe('toggle selection with `Space` key', () => { - const list = createMultiSelectListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - - list.dispatchEvent(new FocusEvent('focusin')); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - - it('use `Space` to select active item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - }); - - it('use `Space` to deselect active item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - }); - }); - - describe('select all', () => { - const list = createMultiSelectListWithFruits(); - - const listbox = new Listbox(list); - - expect( - listbox.items.map((item) => item.getAttribute('aria-selected')).every(Boolean) - ).toBeFalsy(); - - it('use `Meta` + `A` key to select all items', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'KeyA', metaKey: true })); - - expect( - listbox.items.map((item) => item.getAttribute('aria-selected')).every(Boolean) - ).toBeTruthy(); - }); - }); - }); -}); diff --git a/packages/aria-voyager/tests/listbox/navigation.test.ts b/packages/aria-voyager/tests/listbox/navigation.test.ts deleted file mode 100644 index 4e346556..00000000 --- a/packages/aria-voyager/tests/listbox/navigation.test.ts +++ /dev/null @@ -1,431 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { Listbox } from '../../src'; -import { createListWithFruits, createMultiSelectListWithFruits } from './-shared'; - -describe('Listbox Navigation', () => { - describe('When Focus', () => { - it('focus activates the first item', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - - list.dispatchEvent(new FocusEvent('focusin')); - - expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); - expect(firstItem.getAttribute('aria-current')).toBe('true'); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - }); - - it('focus activates first item of selection (Multi Select)', () => { - const list = createMultiSelectListWithFruits(); - const listbox = new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, shiftKey: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - - expect( - listbox.items.map((item) => item.getAttribute('aria-current')).every(Boolean) - ).toBeFalsy(); - - list.dispatchEvent(new FocusEvent('focusin')); - - expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBe('true'); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - }); - - describe('With Pointer', () => { - // describe('propagation is stopped for items', () => { - // let propagationStopped = false; - - // const event = new PointerEvent('pointerup', { bubbles: true }); - - // // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // // @ts-ignore - // event.__TEST__ = true; - - // const nativeStopPropagation = event.stopPropagation; - - // event.stopPropagation = function () { - // propagationStopped = true; - // nativeStopPropagation.call(this); - // }; - - // const list = createListWithFruits(); - - // new Listbox(list); - - // it('propagation stops on item', () => { - // propagationStopped = false; - // list.children[1].dispatchEvent(event); - // expect(propagationStopped).toBeTruthy(); - // }); - - // it('propagation continues when not on item', () => { - // propagationStopped = false; - // list.dispatchEvent(event); - // expect(propagationStopped).toBeFalsy(); - // }); - // }); - - describe('use pointer to activate items', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - it('select not an item does nothing', () => { - list.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - - it('select second item', () => { - secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBe('true'); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - - it('select third item', () => { - thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBe('true'); - }); - }); - }); - - describe('With Keyboard', () => { - describe('navigates with HOME', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); - - it('moves aria-activedescendant', () => { - expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); - }); - - it('marks items with aria-current', () => { - expect(firstItem.getAttribute('aria-current')).toBe('true'); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - }); - - describe('navigates with HOME, skip disabled item', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - firstItem.setAttribute('aria-disabled', 'true'); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); - - it('moves aria-activedescendant', () => { - expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); - }); - - it('marks items with aria-current', () => { - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBe('true'); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - }); - - describe('navigates with END', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); - - it('moves aria-activedescendant', () => { - expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); - }); - - it('marks items with aria-current', () => { - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBe('true'); - }); - }); - - describe('navigates with END, skip disabled item', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - thirdItem.setAttribute('aria-disabled', 'true'); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); - - it('moves aria-activedescendant', () => { - expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); - }); - - it('marks items with aria-current', () => { - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBe('true'); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - }); - - describe('navigate with NEXT', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - it('use `ArrowDown` key when there is no active item does nothing', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - - it('use `Home` key to activate first item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowDown` key to activate second item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBe('true'); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - - it('use `ArrowDown` key to activate third item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBe('true'); - }); - - it('use `ArrowDown` key, but keep third item activated (hit end of list)', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBe('true'); - }); - }); - - describe('navigate with NEXT, skip disabled item', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - secondItem.setAttribute('aria-disabled', 'true'); - - it('use `Home` key to activate first item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowDown` key to activate third item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBe('true'); - }); - }); - - describe('navigate with PREV', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - it('use `ArrowUp` key when there is no active item does nothing', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - - it('use `End` key to activate last item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBe('true'); - }); - - it('use `ArrowUp` key to activate second item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBe('true'); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - - it('use `ArrowUp` key to activate first item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); - expect(firstItem.getAttribute('aria-current')).toBe('true'); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - - it('use `ArrowUp` key to, but keep first item activated (hit beginning of list)', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); - expect(firstItem.getAttribute('aria-current')).toBe('true'); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - }); - }); - - describe('navigate with PREV, skip disabled item', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(list.getAttribute('aria-activedescendant')).toBeNull(); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - - secondItem.setAttribute('aria-disabled', 'true'); - - it('use `End` key to activate last item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); - expect(firstItem.getAttribute('aria-current')).toBeNull(); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBe('true'); - }); - - it('use `ArrowUp` key to activate first item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); - expect(firstItem.getAttribute('aria-current')).toBe('true'); - expect(secondItem.getAttribute('aria-current')).toBeNull(); - expect(thirdItem.getAttribute('aria-current')).toBeNull(); - }); - }); -}); diff --git a/packages/aria-voyager/tests/listbox/navigation/focus/no-selection.test.ts b/packages/aria-voyager/tests/listbox/navigation/focus/no-selection.test.ts new file mode 100644 index 00000000..65e56dd8 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/focus/no-selection.test.ts @@ -0,0 +1,19 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createListWithFruits } from '../../-shared'; + +test('focus activates the first item', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); + expect(firstItem.getAttribute('aria-current')).toBe('true'); + expect(secondItem.getAttribute('aria-current')).toBeNull(); +}); diff --git a/packages/aria-voyager/tests/listbox/navigation/focus/with-multi-selection.test.ts b/packages/aria-voyager/tests/listbox/navigation/focus/with-multi-selection.test.ts new file mode 100644 index 00000000..e3a88c00 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/focus/with-multi-selection.test.ts @@ -0,0 +1,29 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createMultiSelectListWithFruits } from '../../-shared'; + +test('focus activates first item of selection (Multi Select)', () => { + const list = createMultiSelectListWithFruits(); + const listbox = new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + + expect(listbox.items.map((item) => item.getAttribute('aria-current')).every(Boolean)).toBeFalsy(); + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBe('true'); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); +}); diff --git a/packages/aria-voyager/tests/listbox/navigation/focus/with-single-selection.test.ts b/packages/aria-voyager/tests/listbox/navigation/focus/with-single-selection.test.ts new file mode 100644 index 00000000..b661df42 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/focus/with-single-selection.test.ts @@ -0,0 +1,21 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createListWithFruits } from '../../-shared'; + +test('focus activates the first selection item', () => { + const list = createListWithFruits(); + const listbox = new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + + secondItem.setAttribute('aria-selected', 'true'); + listbox.readSelection(); + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBe('true'); +}); diff --git a/packages/aria-voyager/tests/listbox/navigation/keyboard/arrow-down.test.ts b/packages/aria-voyager/tests/listbox/navigation/keyboard/arrow-down.test.ts new file mode 100644 index 00000000..2e821391 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/keyboard/arrow-down.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createListWithFruits } from '../../-shared'; + +describe('navigate with `ArrowDown`', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + it('use `ArrowDown` key when there is no active item does nothing', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); + + it('use `Home` key to activate first item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowDown` key to activate second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBe('true'); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); + + it('use `ArrowDown` key to activate third item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBe('true'); + }); + + it('use `ArrowDown` key, but keep third item activated (hit end of list)', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBe('true'); + }); +}); + +describe('navigate with `ArrowDown`, skip disabled item', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + secondItem.setAttribute('aria-disabled', 'true'); + + it('use `Home` key to activate first item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowDown` key to activate third item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBe('true'); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/navigation/keyboard/arrow-up.test.ts b/packages/aria-voyager/tests/listbox/navigation/keyboard/arrow-up.test.ts new file mode 100644 index 00000000..d348e620 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/keyboard/arrow-up.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createListWithFruits } from '../../-shared'; + +describe('navigate with `ArrowUp`', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + it('use `ArrowUp` key when there is no active item does nothing', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); + + it('use `End` key to activate last item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBe('true'); + }); + + it('use `ArrowUp` key to activate second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBe('true'); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); + + it('use `ArrowUp` key to activate first item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); + expect(firstItem.getAttribute('aria-current')).toBe('true'); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); + + it('use `ArrowUp` key to, but keep first item activated (hit beginning of list)', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); + expect(firstItem.getAttribute('aria-current')).toBe('true'); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); +}); + +describe('navigate with `ArrowUp`, skip disabled item', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + secondItem.setAttribute('aria-disabled', 'true'); + + it('use `End` key to activate last item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBe('true'); + }); + + it('use `ArrowUp` key to activate first item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); + expect(firstItem.getAttribute('aria-current')).toBe('true'); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/navigation/keyboard/end.test.ts b/packages/aria-voyager/tests/listbox/navigation/keyboard/end.test.ts new file mode 100644 index 00000000..60eaa346 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/keyboard/end.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createListWithFruits } from '../../-shared'; + +describe('navigates with `End`', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + it('moves aria-activedescendant', () => { + expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); + }); + + it('marks items with aria-current', () => { + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBe('true'); + }); +}); + +describe('navigates with `End`, skip disabled item', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + thirdItem.setAttribute('aria-disabled', 'true'); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + it('moves aria-activedescendant', () => { + expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); + }); + + it('marks items with aria-current', () => { + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBe('true'); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/navigation/keyboard/home.test.ts b/packages/aria-voyager/tests/listbox/navigation/keyboard/home.test.ts new file mode 100644 index 00000000..f24e945c --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/keyboard/home.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createListWithFruits } from '../../-shared'; + +describe('navigates with `Home`', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); + + it('moves aria-activedescendant', () => { + expect(list.getAttribute('aria-activedescendant')).toBe(firstItem.id); + }); + + it('marks items with aria-current', () => { + expect(firstItem.getAttribute('aria-current')).toBe('true'); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); +}); + +describe('navigates with `Home`, skip disabled item', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + firstItem.setAttribute('aria-disabled', 'true'); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); + + it('moves aria-activedescendant', () => { + expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); + }); + + it('marks items with aria-current', () => { + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBe('true'); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/navigation/pointer.test.ts b/packages/aria-voyager/tests/listbox/navigation/pointer.test.ts new file mode 100644 index 00000000..9788019c --- /dev/null +++ b/packages/aria-voyager/tests/listbox/navigation/pointer.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../src'; +import { createListWithFruits } from '../-shared'; + +// describe('propagation is stopped for items', () => { +// let propagationStopped = false; + +// const event = new PointerEvent('pointerup', { bubbles: true }); + +// // eslint-disable-next-line @typescript-eslint/ban-ts-comment +// // @ts-ignore +// event.__TEST__ = true; + +// const nativeStopPropagation = event.stopPropagation; + +// event.stopPropagation = function () { +// propagationStopped = true; +// nativeStopPropagation.call(this); +// }; + +// const list = createListWithFruits(); + +// new Listbox(list); + +// it('propagation stops on item', () => { +// propagationStopped = false; +// list.children[1].dispatchEvent(event); +// expect(propagationStopped).toBeTruthy(); +// }); + +// it('propagation continues when not on item', () => { +// propagationStopped = false; +// list.dispatchEvent(event); +// expect(propagationStopped).toBeFalsy(); +// }); +// }); + +describe('use pointer to activate items', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + + it('select not an item does nothing', () => { + list.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(list.getAttribute('aria-activedescendant')).toBeNull(); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); + + it('select second item', () => { + secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(list.getAttribute('aria-activedescendant')).toBe(secondItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBe('true'); + expect(thirdItem.getAttribute('aria-current')).toBeNull(); + }); + + it('select third item', () => { + thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(list.getAttribute('aria-activedescendant')).toBe(thirdItem.id); + expect(firstItem.getAttribute('aria-current')).toBeNull(); + expect(secondItem.getAttribute('aria-current')).toBeNull(); + expect(thirdItem.getAttribute('aria-current')).toBe('true'); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/focus/no-selection.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/focus/no-selection.test.ts new file mode 100644 index 00000000..d26c049b --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/focus/no-selection.test.ts @@ -0,0 +1,18 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +test('select no items on focus', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/focus/with-selection.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/focus/with-selection.test.ts new file mode 100644 index 00000000..aae2c9c0 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/focus/with-selection.test.ts @@ -0,0 +1,23 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +test('select first selection item when focus', () => { + const list = createMultiSelectListWithFruits(); + const listbox = new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + secondItem.setAttribute('aria-selected', 'true'); + thirdItem.setAttribute('aria-selected', 'true'); + listbox.readSelection(); + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/keyboard/arrow-down.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/arrow-down.test.ts new file mode 100644 index 00000000..7f0f6413 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/arrow-down.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +describe('select with `ArrowDown` and `Shift`', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + // activate first item + it('focus the list to activate first item', () => { + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowDown` and `Shift` key to select from first to second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowDown` and `Shift` key to select from first to third item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); +}); + +describe('select with `ArrowDown` and release `Shift`', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + // activate first item + it('focus the list to activate first item', () => { + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowDown` and `Shift` key to select from first to second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('Release shift', () => { + list.dispatchEvent(new KeyboardEvent('keyup')); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/keyboard/arrow-up.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/arrow-up.test.ts new file mode 100644 index 00000000..ca787124 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/arrow-up.test.ts @@ -0,0 +1,72 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +describe('select with `ArrowUp` and `Shift`', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + it('use `End` key to activate last item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowUp` and `Shift` key to select third and second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); + + it('use `ArrowUp` and `Shift` key to select thirdt to first item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); +}); + +describe('select with `ArrowUp` and release `Shift`', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + it('use `End` key to activate last item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowUp` and `Shift` key to select third and second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); + + it('Release shift', () => { + list.dispatchEvent(new KeyboardEvent('keyup')); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/keyboard/end.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/end.test.ts new file mode 100644 index 00000000..052a08c6 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/end.test.ts @@ -0,0 +1,30 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +test('select from first to third item with `End` and `Shift` key', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + + firstItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/keyboard/home.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/home.test.ts new file mode 100644 index 00000000..c16b2412 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/home.test.ts @@ -0,0 +1,30 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +test('select from third to first item with `Home` and `Shift` key', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + + thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home', shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/keyboard/select-all.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/select-all.test.ts new file mode 100644 index 00000000..e18a1a41 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/select-all.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +describe('select all', () => { + const list = createMultiSelectListWithFruits(); + + const listbox = new Listbox(list); + + expect( + listbox.items.map((item) => item.getAttribute('aria-selected')).every(Boolean) + ).toBeFalsy(); + + it('use `Meta` + `A` key to select all items', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'KeyA', metaKey: true })); + + expect( + listbox.items.map((item) => item.getAttribute('aria-selected')).every(Boolean) + ).toBeTruthy(); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/keyboard/space.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/space.test.ts new file mode 100644 index 00000000..bdb02fb7 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/keyboard/space.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createMultiSelectListWithFruits } from '../../../-shared'; + +describe('toggle selection with `Space` key', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + + it('use `Space` to select active item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + }); + + it('use `Space` to deselect active item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/multi/multi-selection.test.ts b/packages/aria-voyager/tests/listbox/selection/multi/multi-selection.test.ts new file mode 100644 index 00000000..36ab8360 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/multi/multi-selection.test.ts @@ -0,0 +1,50 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createMultiSelectListWithFruits } from '../../-shared'; + +describe('Select with Pointer', () => { + const list = createMultiSelectListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + + it('select second item', () => { + secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('select third item with `Meta` key', () => { + thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, metaKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); + + it('deselect second item with `Meta` key', () => { + secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, metaKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); + + it('select third to first item with `Shift` key', () => { + firstItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, shiftKey: true })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/single/focus/no-selection.test.ts b/packages/aria-voyager/tests/listbox/selection/single/focus/no-selection.test.ts new file mode 100644 index 00000000..293d4003 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/single/focus/no-selection.test.ts @@ -0,0 +1,18 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createListWithFruits } from '../../../-shared'; + +test('select first item when focus', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/single/focus/with-selection.test.ts b/packages/aria-voyager/tests/listbox/selection/single/focus/with-selection.test.ts new file mode 100644 index 00000000..375985a5 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/single/focus/with-selection.test.ts @@ -0,0 +1,20 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createListWithFruits } from '../../../-shared'; + +test('select first selection item when focus', () => { + const list = createListWithFruits(); + const listbox = new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + + secondItem.setAttribute('aria-selected', 'true'); + listbox.readSelection(); + + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/single/keyboard/arrow-down.test.ts b/packages/aria-voyager/tests/listbox/selection/single/keyboard/arrow-down.test.ts new file mode 100644 index 00000000..3dda8089 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/single/keyboard/arrow-down.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createListWithFruits } from '../../../-shared'; + +describe('select with `ArrowDown`', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + it('focus list to select first item', () => { + list.dispatchEvent(new FocusEvent('focusin')); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowDown` key to select second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowDown` key to select third item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); + + it('use `ArrowDown` key, but keep third item selected (hit end of list)', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/single/keyboard/arrow-up.test.ts b/packages/aria-voyager/tests/listbox/selection/single/keyboard/arrow-up.test.ts new file mode 100644 index 00000000..89e242be --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/single/keyboard/arrow-up.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createListWithFruits } from '../../../-shared'; + +describe('select with `ArrowUp`', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + it('use `End` key to select last item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); + + it('use `ArrowUp` key to select second item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowUp` key to select first item', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('use `ArrowUp` key to, but keep first item selected (hit beginning of list)', () => { + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/single/keyboard/end.test.ts b/packages/aria-voyager/tests/listbox/selection/single/keyboard/end.test.ts new file mode 100644 index 00000000..d3337796 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/single/keyboard/end.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createListWithFruits } from '../../../-shared'; + +test('select last item with `End` key', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/single/keyboard/home.test.ts b/packages/aria-voyager/tests/listbox/selection/single/keyboard/home.test.ts new file mode 100644 index 00000000..ff72975d --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/single/keyboard/home.test.ts @@ -0,0 +1,24 @@ +import { expect, test } from 'vitest'; + +import { Listbox } from '../../../../../src'; +import { createListWithFruits } from '../../../-shared'; + +test('select first item with `Home` key', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + + list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); + + expect(firstItem.getAttribute('aria-selected')).toBe('true'); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); +}); diff --git a/packages/aria-voyager/tests/listbox/selection/single/pointer.test.ts b/packages/aria-voyager/tests/listbox/selection/single/pointer.test.ts new file mode 100644 index 00000000..ccd26703 --- /dev/null +++ b/packages/aria-voyager/tests/listbox/selection/single/pointer.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from 'vitest'; + +import { Listbox } from '../../../../src'; +import { createListWithFruits } from '../../-shared'; + +describe('With Pointer', () => { + const list = createListWithFruits(); + + new Listbox(list); + + const firstItem = list.children[0]; + const secondItem = list.children[1]; + const thirdItem = list.children[2]; + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + + it('select second item', () => { + secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBe('true'); + expect(thirdItem.getAttribute('aria-selected')).toBeNull(); + }); + + it('select third item', () => { + thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); + + expect(firstItem.getAttribute('aria-selected')).toBeNull(); + expect(secondItem.getAttribute('aria-selected')).toBeNull(); + expect(thirdItem.getAttribute('aria-selected')).toBe('true'); + }); +}); diff --git a/packages/aria-voyager/tests/listbox/single-selection.test.ts b/packages/aria-voyager/tests/listbox/single-selection.test.ts deleted file mode 100644 index 5d74c2be..00000000 --- a/packages/aria-voyager/tests/listbox/single-selection.test.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { Listbox } from '../../src'; -import { createListWithFruits } from './-shared'; - -describe('Listbox Single Selection', () => { - describe('When Focus', () => { - it('select first item with FOCUS', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - - list.dispatchEvent(new FocusEvent('focusin')); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - }); - }); - - describe('With Pointer', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - - it('select second item', () => { - secondItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('select third item', () => { - thirdItem.dispatchEvent(new PointerEvent('pointerup', { bubbles: true })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - }); - - describe('With Keyboard', () => { - it('select first item with `Home` key', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('select last item with `End` key', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - - describe('select with NEXT', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - it('focus list to select first item', () => { - list.dispatchEvent(new FocusEvent('focusin')); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowDown` key to select second item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowDown` key to select third item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - - it('use `ArrowDown` key, but keep third item selected (hit end of list)', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - }); - - describe('select with PREV', () => { - const list = createListWithFruits(); - - new Listbox(list); - - const firstItem = list.children[0]; - const secondItem = list.children[1]; - const thirdItem = list.children[2]; - - it('use End key to select last item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBe('true'); - }); - - it('use `ArrowUp` key to select second item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(firstItem.getAttribute('aria-selected')).toBeNull(); - expect(secondItem.getAttribute('aria-selected')).toBe('true'); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowUp` key to select first item', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - - it('use `ArrowUp` key to, but keep first item selected (hit beginning of list)', () => { - list.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' })); - - expect(firstItem.getAttribute('aria-selected')).toBe('true'); - expect(secondItem.getAttribute('aria-selected')).toBeNull(); - expect(thirdItem.getAttribute('aria-selected')).toBeNull(); - }); - }); - }); -});