From 1a6e0faffe9026cf25604a104dc40d44c506e752 Mon Sep 17 00:00:00 2001 From: Luis Takahashi Date: Tue, 20 Oct 2020 20:59:23 -0300 Subject: [PATCH 1/5] Added option role into allOptions selector --- src/select-options.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/select-options.js b/src/select-options.js index a63cd238..5ef82330 100644 --- a/src/select-options.js +++ b/src/select-options.js @@ -10,7 +10,9 @@ function selectOptionsBase(newValue, select, values, init) { ) } const valArray = Array.isArray(values) ? values : [values] - const allOptions = Array.from(select.querySelectorAll('option')) + const allOptions = Array.from( + select.querySelectorAll('option, [role="option"]'), + ) const selectedOptions = valArray .map(val => { if (allOptions.includes(val)) { From 57433aa5783f83542b5e44a5b57e6f0e7b59a44b Mon Sep 17 00:00:00 2001 From: Luis Takahashi Date: Wed, 21 Oct 2020 09:22:03 -0300 Subject: [PATCH 2/5] Added listbox select behaviors when using the role option --- src/select-options.js | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/select-options.js b/src/select-options.js index 5ef82330..b110d846 100644 --- a/src/select-options.js +++ b/src/select-options.js @@ -1,6 +1,7 @@ import {createEvent, getConfig, fireEvent} from '@testing-library/dom' import {click} from './click' import {focus} from './focus' +import {hover, unhover} from './hover' function selectOptionsBase(newValue, select, values, init) { if (!newValue && !select.multiple) { @@ -18,7 +19,9 @@ function selectOptionsBase(newValue, select, values, init) { if (allOptions.includes(val)) { return val } else { - const matchingOption = allOptions.find(o => o.value === val) + const matchingOption = allOptions.find( + o => o.value === val || o.innerHTML === val, + ) if (matchingOption) { return matchingOption } else { @@ -61,17 +64,24 @@ function selectOptionsBase(newValue, select, values, init) { } function selectOption(option) { - option.selected = newValue - fireEvent( - select, - createEvent('input', select, { - bubbles: true, - cancelable: false, - composed: true, - ...init, - }), - ) - fireEvent.change(select, init) + if (option.getAttribute('role') === 'option') { + option['aria-selected'] = newValue + hover(option, init) + click(option, init) + unhover(option, init) + } else { + option.selected = newValue + fireEvent( + select, + createEvent('input', select, { + bubbles: true, + cancelable: false, + composed: true, + ...init, + }), + ) + fireEvent.change(select, init) + } } } From f6e6d13913eae3af83015727552a41ec160b0544 Mon Sep 17 00:00:00 2001 From: Luis Takahashi Date: Wed, 21 Oct 2020 10:29:05 -0300 Subject: [PATCH 3/5] Added initial listbox tests and helpers --- src/__tests__/helpers/utils.js | 28 +++++++++++++++- src/__tests__/select-options.js | 58 ++++++++++++++++++++++++++++++++- src/select-options.js | 3 +- 3 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/__tests__/helpers/utils.js b/src/__tests__/helpers/utils.js index 5c773788..0a666643 100644 --- a/src/__tests__/helpers/utils.js +++ b/src/__tests__/helpers/utils.js @@ -55,6 +55,32 @@ function setupSelect({ } } +function setupListbox() { + const wrapper = document.createElement('div') + wrapper.innerHTML = ` + +
    + + + +
+ ` + document.body.append(wrapper) + const listbox = wrapper.querySelector('[role="listbox"]') + const options = Array.from(wrapper.querySelectorAll('[role="option"]')) + return { + ...addListeners(listbox), + listbox, + options, + } +} + const eventLabelGetters = { KeyboardEvent(event) { return [ @@ -278,4 +304,4 @@ afterEach(() => { document.body.innerHTML = '' }) -export {setup, setupSelect, addEventListener, addListeners} +export {setup, setupSelect, setupListbox, addEventListener, addListeners} diff --git a/src/__tests__/select-options.js b/src/__tests__/select-options.js index c28c27de..662944fb 100644 --- a/src/__tests__/select-options.js +++ b/src/__tests__/select-options.js @@ -1,5 +1,5 @@ import userEvent from '../' -import {setupSelect, addListeners} from './helpers/utils' +import {setupSelect, addListeners, setupListbox} from './helpers/utils' test('fires correct events', () => { const {select, options, getEventSnapshot} = setupSelect() @@ -29,6 +29,53 @@ test('fires correct events', () => { expect(o3.selected).toBe(false) }) +test('fires correct events on listBox select', () => { + const {listbox, options, getEventSnapshot} = setupListbox() + userEvent.selectOptions(listbox, '2') + expect(getEventSnapshot()).toMatchInlineSnapshot(` + Events fired on: ul + + ul - pointerover + ul - pointerenter + ul - mouseover: Left (0) + ul - mouseenter: Left (0) + ul - pointermove + ul - mousemove: Left (0) + ul - pointerdown + ul - mousedown: Left (0) + ul - pointerup + ul - mouseup: Left (0) + ul - click: Left (0) + option[value="2"][selected=false] - pointerover + ul - pointerenter + option[value="2"][selected=false] - mouseover: Left (0) + ul - mouseenter: Left (0) + option[value="2"][selected=false] - pointermove + option[value="2"][selected=false] - mousemove: Left (0) + option[value="2"][selected=false] - pointerover + ul - pointerenter + option[value="2"][selected=false] - mouseover: Left (0) + ul - mouseenter: Left (0) + option[value="2"][selected=false] - pointermove + option[value="2"][selected=false] - mousemove: Left (0) + option[value="2"][selected=false] - pointerdown + option[value="2"][selected=false] - mousedown: Left (0) + option[value="2"][selected=false] - pointerup + option[value="2"][selected=false] - mouseup: Left (0) + option[value="2"][selected=false] - click: Left (0) + option[value="2"][selected=false] - pointermove + option[value="2"][selected=false] - mousemove: Left (0) + option[value="2"][selected=false] - pointerout + ul - pointerleave + option[value="2"][selected=false] - mouseout: Left (0) + ul - mouseleave: Left (0) + `) + const [o1, o2, o3] = options + expect(o1).toHaveAttribute('aria-selected', 'false') + expect(o2).toHaveAttribute('aria-selected', 'true') + expect(o3).toHaveAttribute('aria-selected', 'false') +}) + test('fires correct events on multi-selects', () => { const {select, options, getEventSnapshot} = setupSelect({multiple: true}) userEvent.selectOptions(select, ['1', '3']) @@ -79,6 +126,15 @@ test('sets the selected prop on the selected option using option html elements', expect(o3.selected).toBe(false) }) +test('sets the selected prop on the selected listbox option using option html elements', () => { + const {listbox, options} = setupListbox() + const [o1, o2, o3] = options + userEvent.selectOptions(listbox, o1) + expect(o1).toHaveAttribute('aria-selected', 'true') + expect(o2).toHaveAttribute('aria-selected', 'false') + expect(o3).toHaveAttribute('aria-selected', 'false') +}) + test('a previously focused input gets blurred', () => { const button = document.createElement('button') document.body.append(button) diff --git a/src/select-options.js b/src/select-options.js index b110d846..967a34b3 100644 --- a/src/select-options.js +++ b/src/select-options.js @@ -65,7 +65,8 @@ function selectOptionsBase(newValue, select, values, init) { function selectOption(option) { if (option.getAttribute('role') === 'option') { - option['aria-selected'] = newValue + option?.setAttribute?.('aria-selected', newValue) + hover(option, init) click(option, init) unhover(option, init) From 6fd309a82834c4811bbee4a2fdf9f350c2b43fac Mon Sep 17 00:00:00 2001 From: Luis Takahashi Date: Wed, 21 Oct 2020 10:37:29 -0300 Subject: [PATCH 4/5] Fixed stupListBox typo switching option to li --- src/__tests__/helpers/utils.js | 6 +++--- src/__tests__/select-options.js | 34 ++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/__tests__/helpers/utils.js b/src/__tests__/helpers/utils.js index 0a666643..8e7d8cf7 100644 --- a/src/__tests__/helpers/utils.js +++ b/src/__tests__/helpers/utils.js @@ -66,9 +66,9 @@ function setupListbox() { name="listbox" aria-labelledby="button" > - - - +
  • 1
  • +
  • 2
  • +
  • 3
  • ` document.body.append(wrapper) diff --git a/src/__tests__/select-options.js b/src/__tests__/select-options.js index 662944fb..afddc271 100644 --- a/src/__tests__/select-options.js +++ b/src/__tests__/select-options.js @@ -46,28 +46,28 @@ test('fires correct events on listBox select', () => { ul - pointerup ul - mouseup: Left (0) ul - click: Left (0) - option[value="2"][selected=false] - pointerover + li[value=0] - pointerover ul - pointerenter - option[value="2"][selected=false] - mouseover: Left (0) + li[value=0] - mouseover: Left (0) ul - mouseenter: Left (0) - option[value="2"][selected=false] - pointermove - option[value="2"][selected=false] - mousemove: Left (0) - option[value="2"][selected=false] - pointerover + li[value=0] - pointermove + li[value=0] - mousemove: Left (0) + li[value=0] - pointerover ul - pointerenter - option[value="2"][selected=false] - mouseover: Left (0) + li[value=0] - mouseover: Left (0) ul - mouseenter: Left (0) - option[value="2"][selected=false] - pointermove - option[value="2"][selected=false] - mousemove: Left (0) - option[value="2"][selected=false] - pointerdown - option[value="2"][selected=false] - mousedown: Left (0) - option[value="2"][selected=false] - pointerup - option[value="2"][selected=false] - mouseup: Left (0) - option[value="2"][selected=false] - click: Left (0) - option[value="2"][selected=false] - pointermove - option[value="2"][selected=false] - mousemove: Left (0) - option[value="2"][selected=false] - pointerout + li[value=0] - pointermove + li[value=0] - mousemove: Left (0) + li[value=0] - pointerdown + li[value=0] - mousedown: Left (0) + li[value=0] - pointerup + li[value=0] - mouseup: Left (0) + li[value=0] - click: Left (0) + li[value=0] - pointermove + li[value=0] - mousemove: Left (0) + li[value=0] - pointerout ul - pointerleave - option[value="2"][selected=false] - mouseout: Left (0) + li[value=0] - mouseout: Left (0) ul - mouseleave: Left (0) `) const [o1, o2, o3] = options From fae37788d96e6e97560a88b7d5ce285f28748f62 Mon Sep 17 00:00:00 2001 From: Luis Takahashi Date: Wed, 21 Oct 2020 22:23:54 -0300 Subject: [PATCH 5/5] Added listbox and option right behavior on test-utils --- src/__tests__/helpers/utils.js | 15 ++++++++--- src/__tests__/select-options.js | 48 ++++++++++++++++----------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/__tests__/helpers/utils.js b/src/__tests__/helpers/utils.js index 8e7d8cf7..9d5e7c59 100644 --- a/src/__tests__/helpers/utils.js +++ b/src/__tests__/helpers/utils.js @@ -66,9 +66,9 @@ function setupListbox() { name="listbox" aria-labelledby="button" > -
  • 1
  • -
  • 2
  • -
  • 3
  • +
  • 1
  • +
  • 2
  • +
  • 3
  • ` document.body.append(wrapper) @@ -117,6 +117,12 @@ function addEventListener(el, type, listener, options) { function getElementValue(element) { if (element.tagName === 'SELECT' && element.multiple) { return JSON.stringify(Array.from(element.selectedOptions).map(o => o.value)) + } else if (element.getAttribute('role') === 'listbox') { + return JSON.stringify( + element.querySelector('[aria-selected="true"]')?.innerHTML, + ) + } else if (element.getAttribute('role') === 'option') { + return JSON.stringify(element.innerHTML) } else if ( element.type === 'checkbox' || element.type === 'radio' || @@ -140,6 +146,9 @@ function getElementDisplayName(element) { value ? `[value=${value}]` : null, hasChecked ? `[checked=${element.checked}]` : null, element.tagName === 'OPTION' ? `[selected=${element.selected}]` : null, + element.getAttribute('role') === 'option' + ? `[aria-selected=${element.getAttribute('aria-selected')}]` + : null, ] .filter(Boolean) .join('') diff --git a/src/__tests__/select-options.js b/src/__tests__/select-options.js index afddc271..24259ba0 100644 --- a/src/__tests__/select-options.js +++ b/src/__tests__/select-options.js @@ -33,7 +33,7 @@ test('fires correct events on listBox select', () => { const {listbox, options, getEventSnapshot} = setupListbox() userEvent.selectOptions(listbox, '2') expect(getEventSnapshot()).toMatchInlineSnapshot(` - Events fired on: ul + Events fired on: ul[value="2"] ul - pointerover ul - pointerenter @@ -46,29 +46,29 @@ test('fires correct events on listBox select', () => { ul - pointerup ul - mouseup: Left (0) ul - click: Left (0) - li[value=0] - pointerover - ul - pointerenter - li[value=0] - mouseover: Left (0) - ul - mouseenter: Left (0) - li[value=0] - pointermove - li[value=0] - mousemove: Left (0) - li[value=0] - pointerover - ul - pointerenter - li[value=0] - mouseover: Left (0) - ul - mouseenter: Left (0) - li[value=0] - pointermove - li[value=0] - mousemove: Left (0) - li[value=0] - pointerdown - li[value=0] - mousedown: Left (0) - li[value=0] - pointerup - li[value=0] - mouseup: Left (0) - li[value=0] - click: Left (0) - li[value=0] - pointermove - li[value=0] - mousemove: Left (0) - li[value=0] - pointerout - ul - pointerleave - li[value=0] - mouseout: Left (0) - ul - mouseleave: Left (0) + li#2[value="2"][aria-selected=true] - pointerover + ul[value="2"] - pointerenter + li#2[value="2"][aria-selected=true] - mouseover: Left (0) + ul[value="2"] - mouseenter: Left (0) + li#2[value="2"][aria-selected=true] - pointermove + li#2[value="2"][aria-selected=true] - mousemove: Left (0) + li#2[value="2"][aria-selected=true] - pointerover + ul[value="2"] - pointerenter + li#2[value="2"][aria-selected=true] - mouseover: Left (0) + ul[value="2"] - mouseenter: Left (0) + li#2[value="2"][aria-selected=true] - pointermove + li#2[value="2"][aria-selected=true] - mousemove: Left (0) + li#2[value="2"][aria-selected=true] - pointerdown + li#2[value="2"][aria-selected=true] - mousedown: Left (0) + li#2[value="2"][aria-selected=true] - pointerup + li#2[value="2"][aria-selected=true] - mouseup: Left (0) + li#2[value="2"][aria-selected=true] - click: Left (0) + li#2[value="2"][aria-selected=true] - pointermove + li#2[value="2"][aria-selected=true] - mousemove: Left (0) + li#2[value="2"][aria-selected=true] - pointerout + ul[value="2"] - pointerleave + li#2[value="2"][aria-selected=true] - mouseout: Left (0) + ul[value="2"] - mouseleave: Left (0) `) const [o1, o2, o3] = options expect(o1).toHaveAttribute('aria-selected', 'false')