diff --git a/.changeset/spotty-pumpkins-matter.md b/.changeset/spotty-pumpkins-matter.md new file mode 100644 index 000000000..b62eefc81 --- /dev/null +++ b/.changeset/spotty-pumpkins-matter.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[combobox] submits form on [Enter] diff --git a/packages/ui/components/combobox/src/LionCombobox.js b/packages/ui/components/combobox/src/LionCombobox.js index 6777edf9a..a93e0922a 100644 --- a/packages/ui/components/combobox/src/LionCombobox.js +++ b/packages/ui/components/combobox/src/LionCombobox.js @@ -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; } diff --git a/packages/ui/components/combobox/test/lion-combobox.test.js b/packages/ui/components/combobox/test/lion-combobox.test.js index 5b20619da..d83e21d5e 100644 --- a/packages/ui/components/combobox/test/lion-combobox.test.js +++ b/packages/ui/components/combobox/test/lion-combobox.test.js @@ -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` +
+ `) + ); + 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` + + `) + ); + 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', () => { diff --git a/packages/ui/components/listbox/src/ListboxMixin.js b/packages/ui/components/listbox/src/ListboxMixin.js index fb49ada94..7dbae3a83 100644 --- a/packages/ui/components/listbox/src/ListboxMixin.js +++ b/packages/ui/components/listbox/src/ListboxMixin.js @@ -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; } diff --git a/packages/ui/components/listbox/test-suites/ListboxMixin.suite.js b/packages/ui/components/listbox/test-suites/ListboxMixin.suite.js index 86b705faf..58d68b2a8 100644 --- a/packages/ui/components/listbox/test-suites/ListboxMixin.suite.js +++ b/packages/ui/components/listbox/test-suites/ListboxMixin.suite.js @@ -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` + + `) + ); + 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>