fix(combobox): submit form on [Enter] (#2005)

fix(combobox): submit form on [Enter]
This commit is contained in:
gerjanvangeest 2023-06-08 18:14:14 +02:00 committed by GitHub
parent ccc762d876
commit dbc3fc2db6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 4 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/ui': patch
---
[combobox] submits form on [Enter]

View file

@ -406,7 +406,6 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(LionListbox)) {
* @protected
*/
this._ariaVersion = browserDetection.isChromium ? '1.1' : '1.0';
/**
* @configure ListboxMixin
* @protected
@ -1072,6 +1071,9 @@ export class LionCombobox extends LocalizeMixin(OverlayMixin(LionListbox)) {
this._setTextboxValue('');
break;
case 'Enter':
if (this.multipleChoice && this.opened) {
ev.preventDefault();
}
if (!this.formElements[this.activeIndex]) {
return;
}

View file

@ -12,6 +12,7 @@ import '@lion/ui/define/lion-listbox.js';
import '@lion/ui/define/lion-option.js';
import { Required, Unparseable } from '@lion/ui/form-core.js';
import { defineCE, expect, fixture, html, unsafeStatic } from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import { LitElement } from 'lit';
import sinon from 'sinon';
@ -2678,6 +2679,57 @@ describe('lion-combobox', () => {
mimicKeyPress(visibleOptions[1], 'Enter');
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', () => {

View file

@ -294,7 +294,6 @@ const ListboxMixinImplementation = superclass =>
* @protected
*/
this._repropagationRole = 'choice-group'; // configures FormControlMixin
/**
* 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)
@ -612,7 +611,9 @@ const ListboxMixinImplementation = superclass =>
if (key === ' ' && this._listboxReceivesNoFocus) {
return;
}
ev.preventDefault();
if (key === ' ') {
ev.preventDefault();
}
if (!this.formElements[this.activeIndex]) {
return;
}

View file

@ -13,6 +13,7 @@ import {
nextFrame,
unsafeStatic,
} from '@open-wc/testing';
import { sendKeys } from '@web/test-runner-commands';
import sinon from 'sinon';
import { getListboxMembers } from '../../../exports/listbox-test-helpers.js';
@ -673,12 +674,41 @@ export function runListboxMixinSuite(customConfig = {}) {
mimicKeyPress(_listboxNode, 'Enter');
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', () => {
it('selects active option when "_listboxReceivesNoFocus" is true', async () => {
// 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} */ (
await fixture(html`
<${tag} opened name="foo" ._listboxReceivesNoFocus="${false}" autocomplete="none" show-all-on-empty>