fix(combobox): displayoverlay on keydown event
Co-authored-by: Thijs Louisse <Thijs.Louisse@ing.com>
This commit is contained in:
parent
3aa4783326
commit
63e05c36e3
4 changed files with 221 additions and 83 deletions
5
.changeset/four-days-pull.md
Normal file
5
.changeset/four-days-pull.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@lion/combobox': patch
|
||||
---
|
||||
|
||||
Combobox evaluates show condition after keyup(instead of keydown), so textbox value is updated
|
||||
|
|
@ -390,8 +390,12 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
_showOverlayCondition({ lastKey }) {
|
||||
const doNotOpenOn = ['Tab', 'Esc', 'Enter'];
|
||||
return lastKey && !doNotOpenOn.includes(lastKey);
|
||||
// when no keyboard action involved (on focused change), return current opened state
|
||||
if (!lastKey) {
|
||||
return this.opened;
|
||||
}
|
||||
const doNotShowOn = ['Tab', 'Esc', 'Enter'];
|
||||
return !doNotShowOn.includes(lastKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -699,7 +703,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
*/
|
||||
_setupOpenCloseListeners() {
|
||||
super._setupOpenCloseListeners();
|
||||
this._inputNode.addEventListener('keydown', this.__requestShowOverlay);
|
||||
this._inputNode.addEventListener('keyup', this.__requestShowOverlay);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -707,7 +711,7 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
*/
|
||||
_teardownOpenCloseListeners() {
|
||||
super._teardownOpenCloseListeners();
|
||||
this._inputNode.removeEventListener('keydown', this.__requestShowOverlay);
|
||||
this._inputNode.removeEventListener('keyup', this.__requestShowOverlay);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -806,13 +810,9 @@ export class LionCombobox extends OverlayMixin(LionListbox) {
|
|||
* @param {KeyboardEvent} [ev]
|
||||
*/
|
||||
__requestShowOverlay(ev) {
|
||||
if (
|
||||
this._showOverlayCondition({
|
||||
lastKey: ev && ev.key,
|
||||
currentValue: this._inputNode.value,
|
||||
})
|
||||
) {
|
||||
this.opened = true;
|
||||
}
|
||||
this.opened = this._showOverlayCondition({
|
||||
lastKey: ev && ev.key,
|
||||
currentValue: this._inputNode.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,19 @@ function mimicUserTyping(el, value) {
|
|||
// eslint-disable-next-line no-param-reassign
|
||||
el._inputNode.value = value;
|
||||
el._inputNode.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keyup', { key: value }));
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: value }));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLInputElement} el
|
||||
* @param {string} key
|
||||
*/
|
||||
function mimicKeyPress(el, key) {
|
||||
el.dispatchEvent(new KeyboardEvent('keydown', { key }));
|
||||
el.dispatchEvent(new KeyboardEvent('keyup', { key }));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LionCombobox} el
|
||||
* @param {string[]} values
|
||||
|
|
@ -53,7 +63,7 @@ async function mimicUserTypingAdvanced(el, values) {
|
|||
inputNode.value += key;
|
||||
}
|
||||
|
||||
inputNode.dispatchEvent(new KeyboardEvent('keydown', { key }));
|
||||
mimicKeyPress(inputNode, key);
|
||||
el._inputNode.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
||||
|
||||
el.updateComplete.then(() => {
|
||||
|
|
@ -264,7 +274,7 @@ describe('lion-combobox', () => {
|
|||
expect(el.opened).to.equal(false);
|
||||
});
|
||||
|
||||
it('shows overlay again after select and char keydown', async () => {
|
||||
it('shows overlay again after select and char keyup', async () => {
|
||||
/**
|
||||
* Scenario:
|
||||
* [1] user focuses textbox: overlay hidden
|
||||
|
|
@ -348,7 +358,7 @@ describe('lion-combobox', () => {
|
|||
expect(el.opened).to.equal(true);
|
||||
expect(el._inputNode.value).to.equal('Artichoke');
|
||||
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Tab' }));
|
||||
mimicKeyPress(el._inputNode, 'Tab');
|
||||
expect(el.opened).to.equal(false);
|
||||
expect(el._inputNode.value).to.equal('Artichoke');
|
||||
});
|
||||
|
|
@ -405,6 +415,79 @@ describe('lion-combobox', () => {
|
|||
await el.updateComplete;
|
||||
expect(el.opened).to.equal(true);
|
||||
});
|
||||
|
||||
it('allows to control overlay visibility via "_showOverlayCondition": should not display overlay if currentValue length condition is not fulfilled', async () => {
|
||||
class ShowOverlayConditionCombobox extends LionCombobox {
|
||||
/** @param {{ currentValue: string, lastKey:string }} options */
|
||||
_showOverlayCondition(options) {
|
||||
return options.currentValue.length > 3 && super._showOverlayCondition(options);
|
||||
}
|
||||
}
|
||||
const tagName = defineCE(ShowOverlayConditionCombobox);
|
||||
const tag = unsafeStatic(tagName);
|
||||
|
||||
const el = /** @type {LionCombobox} */ (await fixture(html`
|
||||
<${tag} name="foo">
|
||||
<lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
|
||||
<lion-option .choiceValue="${'Chard'}">Chard</lion-option>
|
||||
<lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
|
||||
<lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
|
||||
</${tag}>
|
||||
`));
|
||||
|
||||
mimicUserTyping(el, 'aaa');
|
||||
expect(el.opened).to.be.false;
|
||||
});
|
||||
|
||||
it('allows to control overlay visibility via "_showOverlayCondition": should display overlay if currentValue length condition is fulfilled', async () => {
|
||||
class ShowOverlayConditionCombobox extends LionCombobox {
|
||||
/** @param {{ currentValue: string, lastKey:string }} options */
|
||||
_showOverlayCondition(options) {
|
||||
return options.currentValue.length > 3 && super._showOverlayCondition(options);
|
||||
}
|
||||
}
|
||||
const tagName = defineCE(ShowOverlayConditionCombobox);
|
||||
const tag = unsafeStatic(tagName);
|
||||
|
||||
const el = /** @type {LionCombobox} */ (await fixture(html`
|
||||
<${tag} name="foo">
|
||||
<lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
|
||||
<lion-option .choiceValue="${'Chard'}">Chard</lion-option>
|
||||
<lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
|
||||
<lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
|
||||
</${tag}>
|
||||
`));
|
||||
|
||||
mimicUserTyping(el, 'aaaa');
|
||||
expect(el.opened).to.be.true;
|
||||
});
|
||||
|
||||
it('allows to control overlay visibility via "_showOverlayCondition": should not display overlay if currentValue length condition is not fulfilled after once fulfilled', async () => {
|
||||
class ShowOverlayConditionCombobox extends LionCombobox {
|
||||
/** @param {{ currentValue: string, lastKey:string }} options */
|
||||
_showOverlayCondition(options) {
|
||||
return options.currentValue.length > 3 && super._showOverlayCondition(options);
|
||||
}
|
||||
}
|
||||
const tagName = defineCE(ShowOverlayConditionCombobox);
|
||||
const tag = unsafeStatic(tagName);
|
||||
|
||||
const el = /** @type {LionCombobox} */ (await fixture(html`
|
||||
<${tag} name="foo">
|
||||
<lion-option .choiceValue="${'Artichoke'}">Artichoke</lion-option>
|
||||
<lion-option .choiceValue="${'Chard'}">Chard</lion-option>
|
||||
<lion-option .choiceValue="${'Chicory'}">Chicory</lion-option>
|
||||
<lion-option .choiceValue="${'Victoria Plum'}">Victoria Plum</lion-option>
|
||||
</${tag}>
|
||||
`));
|
||||
|
||||
mimicUserTyping(el, 'aaaa');
|
||||
expect(el.opened).to.be.true;
|
||||
|
||||
mimicUserTyping(el, 'aaa');
|
||||
await el.updateComplete;
|
||||
expect(el.opened).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Accessibility', () => {
|
||||
|
|
@ -488,7 +571,7 @@ describe('lion-combobox', () => {
|
|||
expect(el.checkedIndex).to.equal(0);
|
||||
|
||||
// Simulate backspace deleting the char at the end of the string
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace' }));
|
||||
mimicKeyPress(el._inputNode, 'Backspace');
|
||||
el._inputNode.dispatchEvent(new Event('input'));
|
||||
const arr = el._inputNode.value.split('');
|
||||
arr.splice(el._inputNode.value.length - 1, 1);
|
||||
|
|
@ -1123,11 +1206,11 @@ describe('lion-combobox', () => {
|
|||
expect(el.activeIndex).to.equal(1);
|
||||
});
|
||||
|
||||
it('changes whether active index is set to the closest match automatically depending on autocomplete', async () => {
|
||||
it('changes whether activeIndex is set to the closest match automatically depending on autocomplete', async () => {
|
||||
/**
|
||||
* Automatic selection (setting activeIndex to closest matching option) in lion is set for inline & both autocomplete,
|
||||
* because it is unavoidable there
|
||||
* For list & none autocomplete, it is turned off and manual selection is required.
|
||||
* Automatic selection (setting activeIndex to closest matching option) in lion is set for
|
||||
* 'inline' & 'both' autocomplete, because it is unavoidable there
|
||||
* For 'list' & 'none' autocomplete, it is turned off and manual selection is required.
|
||||
* TODO: Make this configurable for list & none autocomplete?
|
||||
*/
|
||||
const el = /** @type {LionCombobox} */ (await fixture(html`
|
||||
|
|
@ -1139,49 +1222,64 @@ describe('lion-combobox', () => {
|
|||
</lion-combobox>
|
||||
`));
|
||||
|
||||
/** @param {LionCombobox} elm */
|
||||
function reset(elm) {
|
||||
/**
|
||||
* @param {LionCombobox} elm
|
||||
* @param {'none'|'list'|'inline'|'both'} autocomplete
|
||||
*/
|
||||
async function setup(elm, autocomplete) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
elm.autocomplete = autocomplete;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
elm.activeIndex = -1;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
elm.checkedIndex = -1;
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
elm.opened = true;
|
||||
await elm.updateComplete;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
|
||||
// Example 1. List Autocomplete with Manual Selection:
|
||||
// does not set active at all until user selects
|
||||
reset(el);
|
||||
el.autocomplete = 'none';
|
||||
await setup(el, 'none');
|
||||
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), 'cha');
|
||||
await el.updateComplete;
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
expect(el.activeIndex).to.equal(-1);
|
||||
expect(el.opened).to.be.true;
|
||||
|
||||
mimicKeyPress(el._inputNode, 'Enter');
|
||||
expect(el.opened).to.be.false;
|
||||
expect(el.activeIndex).to.equal(-1);
|
||||
|
||||
// https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
|
||||
// Example 2. List Autocomplete with Automatic Selection:
|
||||
// does not set active at all until user selects
|
||||
reset(el);
|
||||
el.autocomplete = 'list';
|
||||
await setup(el, 'list');
|
||||
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), 'cha');
|
||||
await el.updateComplete;
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
expect(el.activeIndex).to.equal(-1);
|
||||
expect(el.opened).to.be.true;
|
||||
expect(el.activeIndex).to.equal(-1);
|
||||
|
||||
mimicKeyPress(el._inputNode, 'Enter');
|
||||
expect(el.activeIndex).to.equal(-1);
|
||||
expect(el.opened).to.be.false;
|
||||
|
||||
// https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
|
||||
// Example 3. List with Inline Autocomplete (mostly, but with aria-autocomplete="inline")
|
||||
reset(el);
|
||||
el.autocomplete = 'inline';
|
||||
await setup(el, 'inline');
|
||||
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), '');
|
||||
await el.updateComplete;
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), 'cha');
|
||||
await el.updateComplete;
|
||||
await el.updateComplete;
|
||||
expect(el.opened).to.be.true;
|
||||
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(el._inputNode, 'Enter');
|
||||
await el.updateComplete;
|
||||
await el.updateComplete;
|
||||
|
||||
|
|
@ -1190,13 +1288,12 @@ describe('lion-combobox', () => {
|
|||
|
||||
// https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
|
||||
// Example 3. List with Inline Autocomplete
|
||||
reset(el);
|
||||
el.autocomplete = 'both';
|
||||
await setup(el, 'both');
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), '');
|
||||
await el.updateComplete;
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), 'cha');
|
||||
await el.updateComplete;
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(el._inputNode, 'Enter');
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
expect(el.opened).to.be.false;
|
||||
});
|
||||
|
|
@ -1225,7 +1322,7 @@ describe('lion-combobox', () => {
|
|||
// select artichoke
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), 'artichoke');
|
||||
await el.updateComplete;
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(el._inputNode, 'Enter');
|
||||
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), '');
|
||||
await el.updateComplete;
|
||||
|
|
@ -1247,10 +1344,10 @@ describe('lion-combobox', () => {
|
|||
// Select something
|
||||
mimicUserTyping(/** @type {LionCombobox} */ (el), 'cha');
|
||||
await el.updateComplete;
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(el._inputNode, 'Enter');
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
||||
mimicKeyPress(el._inputNode, 'Escape');
|
||||
await el.updateComplete;
|
||||
expect(el._inputNode.textContent).to.equal('');
|
||||
|
||||
|
|
@ -1293,7 +1390,8 @@ describe('lion-combobox', () => {
|
|||
mimicUserTyping(/** @type {LionCombobox} */ (el), 'ch');
|
||||
await el.updateComplete;
|
||||
expect(el._activeDescendantOwnerNode.getAttribute('aria-activedescendant')).to.equal(null);
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
// el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(el._inputNode, 'ArrowDown');
|
||||
expect(el._activeDescendantOwnerNode.getAttribute('aria-activedescendant')).to.equal(
|
||||
'artichoke-option',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,15 @@ import sinon from 'sinon';
|
|||
|
||||
const fixture = /** @type {(arg: TemplateResult) => Promise<LionListbox>} */ (_fixture);
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} el
|
||||
* @param {string} key
|
||||
*/
|
||||
function mimicKeyPress(el, key) {
|
||||
el.dispatchEvent(new KeyboardEvent('keydown', { key }));
|
||||
el.dispatchEvent(new KeyboardEvent('keyup', { key }));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LionListbox} lionListboxEl
|
||||
*/
|
||||
|
|
@ -382,7 +391,8 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
el.activeIndex = 0;
|
||||
await el.updateComplete;
|
||||
expect(activeDescendantOwner.getAttribute('aria-activedescendant')).to.equal('first');
|
||||
activeDescendantOwner.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(activeDescendantOwner, 'ArrowDown');
|
||||
// activeDescendantOwner.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
await el.updateComplete;
|
||||
expect(activeDescendantOwner.getAttribute('aria-activedescendant')).to.equal('second');
|
||||
});
|
||||
|
|
@ -537,12 +547,17 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
// Normalize
|
||||
el.activeIndex = 0;
|
||||
const options = el.formElements;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
||||
// mimicKeyPress(listbox, 'ArrowUp');
|
||||
|
||||
mimicKeyPress(listbox, 'ArrowUp');
|
||||
|
||||
expect(options[0].active).to.be.true;
|
||||
expect(options[1].active).to.be.false;
|
||||
expect(options[2].active).to.be.false;
|
||||
el.activeIndex = 2;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
// mimicKeyPress(listbox, 'ArrowDown');
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
|
||||
expect(options[0].active).to.be.false;
|
||||
expect(options[1].active).to.be.false;
|
||||
expect(options[2].active).to.be.true;
|
||||
|
|
@ -563,14 +578,20 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
el.activeIndex = 0;
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
||||
// el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
||||
mimicKeyPress(el._inputNode, 'ArrowUp');
|
||||
|
||||
await el.updateComplete;
|
||||
expect(el.activeIndex).to.equal(2);
|
||||
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
// el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(el._inputNode, 'ArrowDown');
|
||||
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
// Extra check: regular navigation
|
||||
el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
// el._inputNode.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(el._inputNode, 'ArrowDown');
|
||||
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
|
@ -590,8 +611,8 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
el.activeIndex = 0;
|
||||
const options = el.formElements;
|
||||
el.checkedIndex = 0;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
mimicKeyPress(listbox, 'Enter');
|
||||
expect(options[1].checked).to.be.true;
|
||||
});
|
||||
});
|
||||
|
|
@ -613,14 +634,16 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
el.activeIndex = 0;
|
||||
const options = el.formElements;
|
||||
el.checkedIndex = 0;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
mimicKeyPress(listbox, ' ');
|
||||
|
||||
expect(options[1].checked).to.be.true;
|
||||
el.checkedIndex = 0;
|
||||
// @ts-ignore allow protected member access in test
|
||||
el._listboxReceivesNoFocus = true;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
mimicKeyPress(listbox, ' ');
|
||||
|
||||
expect(options[1].checked).to.be.false;
|
||||
});
|
||||
});
|
||||
|
|
@ -668,9 +691,9 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
}
|
||||
|
||||
el.activeIndex = 2;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' }));
|
||||
mimicKeyPress(listbox, 'Home');
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' }));
|
||||
mimicKeyPress(listbox, 'End');
|
||||
expect(el.activeIndex).to.equal(3);
|
||||
});
|
||||
it('navigates through open lists with [ArrowDown] [ArrowUp] keys activates the option', async () => {
|
||||
|
|
@ -690,10 +713,11 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
el.selectionFollowsFocus = false;
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
expect(el.checkedIndex).to.equal(-1);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
expect(el.checkedIndex).to.equal(-1);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
||||
mimicKeyPress(listbox, 'ArrowUp');
|
||||
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
expect(el.checkedIndex).to.equal(-1);
|
||||
});
|
||||
|
|
@ -718,21 +742,24 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
expect(options[0].active).to.be.true;
|
||||
expect(options[1].active).to.be.false;
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(options[0].active).to.be.false;
|
||||
expect(options[1].active).to.be.true;
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
||||
mimicKeyPress(listbox, 'ArrowUp');
|
||||
|
||||
expect(options[0].active).to.be.true;
|
||||
expect(options[1].active).to.be.false;
|
||||
|
||||
// No response to horizontal arrows...
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }));
|
||||
mimicKeyPress(listbox, 'ArrowRight');
|
||||
|
||||
expect(options[0].active).to.be.true;
|
||||
expect(options[1].active).to.be.false;
|
||||
|
||||
el.activeIndex = 1;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }));
|
||||
mimicKeyPress(listbox, 'ArrowLeft');
|
||||
|
||||
expect(options[0].active).to.be.false;
|
||||
expect(options[1].active).to.be.true;
|
||||
});
|
||||
|
|
@ -753,18 +780,21 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
|
||||
await el.updateComplete;
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }));
|
||||
mimicKeyPress(listbox, 'ArrowRight');
|
||||
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }));
|
||||
mimicKeyPress(listbox, 'ArrowLeft');
|
||||
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
|
||||
// No response to vertical arrows...
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
|
||||
el.activeIndex = 1;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
||||
mimicKeyPress(listbox, 'ArrowUp');
|
||||
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
});
|
||||
|
||||
|
|
@ -834,13 +864,13 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
|
||||
// Enter
|
||||
el.activeIndex = 0;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(listbox, 'Enter');
|
||||
el.activeIndex = 1;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(listbox, 'Enter');
|
||||
expect(options[0].checked).to.equal(true);
|
||||
expect(el.modelValue).to.eql(['Artichoke', 'Chard']);
|
||||
// also deselect
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(listbox, 'Enter');
|
||||
expect(options[0].checked).to.equal(true);
|
||||
expect(el.modelValue).to.eql(['Artichoke']);
|
||||
|
||||
|
|
@ -855,13 +885,16 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
|
||||
// Space
|
||||
el.activeIndex = 0;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
||||
mimicKeyPress(listbox, ' ');
|
||||
|
||||
el.activeIndex = 1;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
||||
mimicKeyPress(listbox, ' ');
|
||||
|
||||
expect(options[0].checked).to.equal(true);
|
||||
expect(el.modelValue).to.eql(['Artichoke', 'Chard']);
|
||||
// also deselect
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' }));
|
||||
mimicKeyPress(listbox, ' ');
|
||||
|
||||
expect(options[0].checked).to.equal(true);
|
||||
expect(el.modelValue).to.eql(['Artichoke']);
|
||||
});
|
||||
|
|
@ -925,11 +958,12 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
expect(el.activeIndex).to.equal(0);
|
||||
expect(el.checkedIndex).to.equal(0);
|
||||
expectOnlyGivenOneOptionToBeChecked(options, 0);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
expect(el.checkedIndex).to.equal(1);
|
||||
expectOnlyGivenOneOptionToBeChecked(options, 1);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
|
||||
mimicKeyPress(listbox, 'ArrowUp');
|
||||
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
expect(el.checkedIndex).to.equal(0);
|
||||
expectOnlyGivenOneOptionToBeChecked(options, 0);
|
||||
|
|
@ -964,11 +998,13 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
expect(el.activeIndex).to.equal(0);
|
||||
expect(el.checkedIndex).to.equal(0);
|
||||
expectOnlyGivenOneOptionToBeChecked(options, 0);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }));
|
||||
mimicKeyPress(listbox, 'ArrowRight');
|
||||
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
expect(el.checkedIndex).to.equal(1);
|
||||
expectOnlyGivenOneOptionToBeChecked(options, 1);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }));
|
||||
mimicKeyPress(listbox, 'ArrowLeft');
|
||||
|
||||
expect(el.activeIndex).to.equal(0);
|
||||
expect(el.checkedIndex).to.equal(0);
|
||||
expectOnlyGivenOneOptionToBeChecked(options, 0);
|
||||
|
|
@ -990,9 +1026,9 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
}
|
||||
|
||||
expect(el.modelValue).to.equal('30');
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Home' }));
|
||||
mimicKeyPress(listbox, 'Home');
|
||||
expect(el.modelValue).to.equal('10');
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'End' }));
|
||||
mimicKeyPress(listbox, 'End');
|
||||
expect(el.modelValue).to.equal('40');
|
||||
});
|
||||
});
|
||||
|
|
@ -1009,7 +1045,7 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
|
||||
await el.updateComplete;
|
||||
const { checkedIndex } = el;
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(el.checkedIndex).to.equal(checkedIndex);
|
||||
});
|
||||
|
||||
|
|
@ -1065,11 +1101,11 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
// Normalize activeIndex across multiple implementers of ListboxMixinSuite
|
||||
el.activeIndex = 0;
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
|
||||
expect(el.checkedIndex).to.equal(0);
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(listbox, 'Enter');
|
||||
// Checked index stays where it was
|
||||
expect(el.checkedIndex).to.equal(0);
|
||||
});
|
||||
|
|
@ -1087,11 +1123,11 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
// Normalize activeIndex across multiple implementers of ListboxMixinSuite
|
||||
el.activeIndex = 0;
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(el.activeIndex).to.equal(1);
|
||||
expect(el.checkedIndex).to.equal(-1);
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }));
|
||||
mimicKeyPress(listbox, 'ArrowDown');
|
||||
expect(el.activeIndex).to.equal(2);
|
||||
expect(el.checkedIndex).to.equal(2);
|
||||
});
|
||||
|
|
@ -1318,8 +1354,7 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
// Allow options that behave like anchors (think of Google Search) to trigger the anchor behavior
|
||||
const activeOption = el.formElements[1];
|
||||
const clickSpy = sinon.spy(activeOption, 'click');
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(listbox, 'Enter');
|
||||
|
||||
expect(clickSpy).to.have.been.calledOnce;
|
||||
});
|
||||
|
|
@ -1339,7 +1374,7 @@ export function runListboxMixinSuite(customConfig = {}) {
|
|||
const activeOption = el.formElements[0];
|
||||
const clickSpy = sinon.spy(activeOption, 'click');
|
||||
|
||||
listbox.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
|
||||
mimicKeyPress(listbox, 'Enter');
|
||||
|
||||
expect(clickSpy).to.not.have.been.called;
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue