From ca15374a12436888fe5ee720753d5989bc496b9f Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Thu, 25 Mar 2021 09:59:50 +0100 Subject: [PATCH] fix: 'isTriggeredByUser' support for listbox (descendants) --- .changeset/wise-moles-teach.md | 5 +++ .../test/model-value-consistency.test.js | 33 +++++++++++++++---- packages/listbox/src/ListboxMixin.js | 17 +++++++++- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 .changeset/wise-moles-teach.md diff --git a/.changeset/wise-moles-teach.md b/.changeset/wise-moles-teach.md new file mode 100644 index 000000000..cf9f61713 --- /dev/null +++ b/.changeset/wise-moles-teach.md @@ -0,0 +1,5 @@ +--- +'@lion/listbox': patch +--- + +listbox (descendants) add `isTriggeredByUser` meta data to `model-value-changed` event on keyboard interaction diff --git a/packages/form-integrations/test/model-value-consistency.test.js b/packages/form-integrations/test/model-value-consistency.test.js index f8a39cee8..2cfeb0d05 100644 --- a/packages/form-integrations/test/model-value-consistency.test.js +++ b/packages/form-integrations/test/model-value-consistency.test.js @@ -396,10 +396,11 @@ describe('detail.isTriggeredByUser', () => { } /** - * @param {FormControl & {value: string}} el + * @param {FormControl & {value: string;}} el * @param {string} newViewValue + * @param {string | undefined} [triggerType] */ - function mimicUserInput(el, newViewValue) { + function mimicUserInput(el, newViewValue, triggerType) { const type = detectType(el); let userInputEv; if (type === 'RegularField') { @@ -409,7 +410,14 @@ describe('detail.isTriggeredByUser', () => { } else if (type === 'ChoiceField') { el._inputNode.dispatchEvent(new Event('change', { bubbles: true })); } else if (type === 'OptionChoiceField') { - el.dispatchEvent(new Event('click', { bubbles: true })); + if (!triggerType) { + el.dispatchEvent(new Event('click', { bubbles: true })); + } else if (triggerType === 'keypress') { + el.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })); + el.dispatchEvent(new KeyboardEvent('keyup', { key: 'ArrowDown', bubbles: true })); + el.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); + el.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter', bubbles: true })); + } } } @@ -459,9 +467,12 @@ describe('detail.isTriggeredByUser', () => { } /** - * @param {FormControl & {value: string}} formControl + * @param {FormControl & {value: string;}} formControl + * @param {boolean | undefined} [testKeyboardBehavior] */ - function expectCorrectEventMetaChoiceField(formControl) { + function expectCorrectEventMetaChoiceField(formControl, testKeyboardBehavior) { + const type = detectType(formControl); + resetChoiceFieldToForceRepropagation(formControl); mimicUserInput(formControl, 'userValue'); expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true; @@ -474,6 +485,16 @@ describe('detail.isTriggeredByUser', () => { // eslint-disable-next-line no-param-reassign formControl.modelValue = { value: 'programmaticValue', checked: false }; expect(spy.secondCall.args[0].detail.isTriggeredByUser).to.be.false; + + if (type === 'OptionChoiceField' && testKeyboardBehavior) { + resetChoiceFieldToForceRepropagation(formControl); + mimicUserInput(formControl, 'userValue', 'keypress'); + try { + expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true; + } catch (e) { + console.log(formControl); + } + } } // 1. Derive the type of field we're dealing with @@ -494,7 +515,7 @@ describe('detail.isTriggeredByUser', () => { ); el.appendChild(childrenEls); await el.registrationComplete; - expectCorrectEventMetaChoiceField(el.formElements[0]); + expectCorrectEventMetaChoiceField(el.formElements[0], true); } else if (type === 'FormOrFieldset') { const childrenEls = await fixture( html`
`, diff --git a/packages/listbox/src/ListboxMixin.js b/packages/listbox/src/ListboxMixin.js index 3450af3dd..61972bdc2 100644 --- a/packages/listbox/src/ListboxMixin.js +++ b/packages/listbox/src/ListboxMixin.js @@ -486,6 +486,13 @@ const ListboxMixinImplementation = superclass => return; } + this.__isHandlingUserInput = true; + setTimeout(() => { + // Since we can't control when subclasses are done handling keyboard input, we + // schedule a timeout to reset __isHandlingUserInput + this.__isHandlingUserInput = false; + }); + const { key } = ev; switch (key) { @@ -586,6 +593,14 @@ const ListboxMixinImplementation = superclass => if (this.disabled) { return; } + + this.__isHandlingUserInput = true; + setTimeout(() => { + // Since we can't control when subclasses are done handling keyboard input, we + // schedule a timeout to reset __isHandlingUserInput + this.__isHandlingUserInput = false; + }); + const { key } = ev; // eslint-disable-next-line default-case switch (key) { @@ -702,7 +717,7 @@ const ListboxMixinImplementation = superclass => new CustomEvent('model-value-changed', { detail: /** @type {ModelValueEventDetails} */ ({ formPath: ev.detail.formPath, - isTriggeredByUser: ev.detail.isTriggeredByUser, + isTriggeredByUser: ev.detail.isTriggeredByUser || this.__isHandlingUserInput, element: ev.target, }), }),