fix(combobox): submit form on [Enter] (#2005)
fix(combobox): submit form on [Enter]
This commit is contained in:
parent
ccc762d876
commit
dbc3fc2db6
5 changed files with 94 additions and 4 deletions
5
.changeset/spotty-pumpkins-matter.md
Normal file
5
.changeset/spotty-pumpkins-matter.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/ui': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
[combobox] submits form on [Enter]
|
||||||
|
|
@ -406,7 +406,6 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(LionListbox)) {
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
this._ariaVersion = browserDetection.isChromium ? '1.1' : '1.0';
|
this._ariaVersion = browserDetection.isChromium ? '1.1' : '1.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @configure ListboxMixin
|
* @configure ListboxMixin
|
||||||
* @protected
|
* @protected
|
||||||
|
|
@ -1072,6 +1071,9 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(LionListbox)) {
|
||||||
this._setTextboxValue('');
|
this._setTextboxValue('');
|
||||||
break;
|
break;
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
|
if (this.multipleChoice && this.opened) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
if (!this.formElements[this.activeIndex]) {
|
if (!this.formElements[this.activeIndex]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import '@lion/ui/define/lion-listbox.js';
|
||||||
import '@lion/ui/define/lion-option.js';
|
import '@lion/ui/define/lion-option.js';
|
||||||
import { Required, Unparseable } from '@lion/ui/form-core.js';
|
import { Required, Unparseable } from '@lion/ui/form-core.js';
|
||||||
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
|
||||||
|
import { sendKeys } from '@web/test-runner-commands';
|
||||||
import { LitElement } from 'lit';
|
import { LitElement } from 'lit';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
|
@ -2678,6 +2679,57 @@ describe('lion-combobox', () => {
|
||||||
mimicKeyPress(visibleOptions[1], 'Enter');
|
mimicKeyPress(visibleOptions[1], 'Enter');
|
||||||
expect(el.opened).to.equal(true);
|
expect(el.opened).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('submits form on [Enter] when listbox is closed', async () => {
|
||||||
|
const submitSpy = sinon.spy(e => e.preventDefault());
|
||||||
|
const el = /** @type {HTMLFormElement} */ (
|
||||||
|
await fixture(html`
|
||||||
|
<form @submit=${submitSpy}>
|
||||||
|
<lion-combobox name="foo" multiple-choice>
|
||||||
|
<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>
|
||||||
|
</lion-combobox>
|
||||||
|
<button type="submit">submit</button>
|
||||||
|
</form>
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
const combobox = /** @type {LionCombobox} */ (el.querySelector('[name="foo"]'));
|
||||||
|
const { _inputNode } = getComboboxMembers(combobox);
|
||||||
|
await combobox.updateComplete;
|
||||||
|
_inputNode.focus();
|
||||||
|
await sendKeys({
|
||||||
|
press: 'Enter',
|
||||||
|
});
|
||||||
|
expect(submitSpy.callCount).to.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not submit form on [Enter] when listbox is opened', async () => {
|
||||||
|
const submitSpy = sinon.spy(e => e.preventDefault());
|
||||||
|
const el = /** @type {HTMLFormElement} */ (
|
||||||
|
await fixture(html`
|
||||||
|
<form @submit=${submitSpy}>
|
||||||
|
<lion-combobox name="foo" multiple-choice>
|
||||||
|
<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>
|
||||||
|
</lion-combobox>
|
||||||
|
<button type="submit">submit</button>
|
||||||
|
</form>
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
const combobox = /** @type {LionCombobox} */ (el.querySelector('[name="foo"]'));
|
||||||
|
const { _inputNode } = getComboboxMembers(combobox);
|
||||||
|
combobox.opened = true;
|
||||||
|
await combobox.updateComplete;
|
||||||
|
_inputNode.focus();
|
||||||
|
await sendKeys({
|
||||||
|
press: 'Enter',
|
||||||
|
});
|
||||||
|
expect(submitSpy.callCount).to.equal(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Match Mode', () => {
|
describe('Match Mode', () => {
|
||||||
|
|
|
||||||
|
|
@ -294,7 +294,6 @@ const ListboxMixinImplementation = superclass =>
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
this._repropagationRole = 'choice-group'; // configures FormControlMixin
|
this._repropagationRole = 'choice-group'; // configures FormControlMixin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When listbox is coupled to a textbox (in case we are dealing with a combobox),
|
* When listbox is coupled to a textbox (in case we are dealing with a combobox),
|
||||||
* spaces should not select an element (they need to be put in the textbox)
|
* spaces should not select an element (they need to be put in the textbox)
|
||||||
|
|
@ -612,7 +611,9 @@ const ListboxMixinImplementation = superclass =>
|
||||||
if (key === ' ' && this._listboxReceivesNoFocus) {
|
if (key === ' ' && this._listboxReceivesNoFocus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ev.preventDefault();
|
if (key === ' ') {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
if (!this.formElements[this.activeIndex]) {
|
if (!this.formElements[this.activeIndex]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
nextFrame,
|
nextFrame,
|
||||||
unsafeStatic,
|
unsafeStatic,
|
||||||
} from '@open-wc/testing';
|
} from '@open-wc/testing';
|
||||||
|
import { sendKeys } from '@web/test-runner-commands';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import { getListboxMembers } from '../../../exports/listbox-test-helpers.js';
|
import { getListboxMembers } from '../../../exports/listbox-test-helpers.js';
|
||||||
|
|
||||||
|
|
@ -673,12 +674,41 @@ export function runListboxMixinSuite(customConfig = {}) {
|
||||||
mimicKeyPress(_listboxNode, 'Enter');
|
mimicKeyPress(_listboxNode, 'Enter');
|
||||||
expect(options[1].checked).to.be.true;
|
expect(options[1].checked).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('submits form on [Enter] when inputNode is an instance of HTMLInputNode', async () => {
|
||||||
|
const submitSpy = sinon.spy(e => e.preventDefault());
|
||||||
|
const el = /** @type {HTMLFormElement} */ (
|
||||||
|
await _fixture(html`
|
||||||
|
<form @submit=${submitSpy}>
|
||||||
|
<${tag} name="foo">
|
||||||
|
<${optionTag} .choiceValue="${'Artichoke'}">Artichoke</${optionTag}>
|
||||||
|
<${optionTag} .choiceValue="${'Bla'}">Bla</${optionTag}>
|
||||||
|
<${optionTag} .choiceValue="${'Chard'}">Chard</${optionTag}>
|
||||||
|
</${tag}>
|
||||||
|
<button type="submit">submit</button>
|
||||||
|
</form>
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
const listbox = /** @type {LionListbox} */ (el.querySelector('[name="foo"]'));
|
||||||
|
const { _inputNode } = getListboxMembers(listbox);
|
||||||
|
if (!(_inputNode instanceof HTMLInputElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await listbox.updateComplete;
|
||||||
|
// @ts-ignore allow protected members in tests
|
||||||
|
|
||||||
|
_inputNode.focus();
|
||||||
|
await sendKeys({
|
||||||
|
press: 'Enter',
|
||||||
|
});
|
||||||
|
expect(submitSpy.callCount).to.equal(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Space', () => {
|
describe('Space', () => {
|
||||||
it('selects active option when "_listboxReceivesNoFocus" is true', async () => {
|
it('selects active option when "_listboxReceivesNoFocus" is true', async () => {
|
||||||
// When listbox is not focusable (in case of a combobox), the user should be allowed
|
// When listbox is not focusable (in case of a combobox), the user should be allowed
|
||||||
// to enter a space in the focusable element (texbox)
|
// to enter a space in the focusable element (textbox)
|
||||||
const el = /** @type {LionListbox} */ (
|
const el = /** @type {LionListbox} */ (
|
||||||
await fixture(html`
|
await fixture(html`
|
||||||
<${tag} opened name="foo" ._listboxReceivesNoFocus="${false}" autocomplete="none" show-all-on-empty>
|
<${tag} opened name="foo" ._listboxReceivesNoFocus="${false}" autocomplete="none" show-all-on-empty>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue