diff --git a/docs/fundamentals/systems/form/use-cases.md b/docs/fundamentals/systems/form/use-cases.md index 3c985e5d2..0d8b218f1 100644 --- a/docs/fundamentals/systems/form/use-cases.md +++ b/docs/fundamentals/systems/form/use-cases.md @@ -7,7 +7,9 @@ For usage and installation please see the appropriate packages. import { html } from '@mdjs/mdjs-preview'; import '@lion/ui/define/lion-button.js'; import '@lion/ui/define/lion-checkbox-group.js'; +import '@lion/ui/define/lion-checkbox.js'; import '@lion/ui/define/lion-combobox.js'; +import '@lion/ui/define/lion-option.js'; import '@lion/ui/define/lion-fieldset.js'; import '@lion/ui/define/lion-form.js'; import '@lion/ui/define/lion-input-amount.js'; @@ -15,12 +17,14 @@ import '@lion/ui/define/lion-input-date.js'; import '@lion/ui/define/lion-input-datepicker.js'; import '@lion/ui/define/lion-input-email.js'; import '@lion/ui/define/lion-input-tel.js'; +import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-input-iban.js'; import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-input-stepper.js'; import '@lion/ui/define/lion-input.js'; import '@lion/ui/define/lion-listbox.js'; import '@lion/ui/define/lion-radio-group.js'; +import '@lion/ui/define/lion-radio.js'; import '@lion/ui/define/lion-select.js'; import '@lion/ui/define/lion-select-rich.js'; import '@lion/ui/define/lion-switch.js'; diff --git a/docs/fundamentals/systems/overlays/assets/umbrella-form.js b/docs/fundamentals/systems/overlays/assets/umbrella-form.js index 58d5f7e13..db35ab01c 100644 --- a/docs/fundamentals/systems/overlays/assets/umbrella-form.js +++ b/docs/fundamentals/systems/overlays/assets/umbrella-form.js @@ -8,15 +8,22 @@ import '@lion/ui/define/lion-input-datepicker.js'; import '@lion/ui/define/lion-input-amount.js'; import '@lion/ui/define/lion-input-iban.js'; import '@lion/ui/define/lion-input-email.js'; +import '@lion/ui/define/lion-input-tel.js'; +import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-checkbox-group.js'; +import '@lion/ui/define/lion-checkbox.js'; import '@lion/ui/define/lion-radio-group.js'; +import '@lion/ui/define/lion-radio.js'; import '@lion/ui/define/lion-select.js'; import '@lion/ui/define/lion-select-rich.js'; +import '@lion/ui/define/lion-listbox.js'; +import '@lion/ui/define/lion-option.js'; +import '@lion/ui/define/lion-combobox.js'; import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-textarea.js'; import '@lion/ui/define/lion-button.js'; -import '@lion/ui/define/lion-input-tel.js'; -import '@lion/ui/define/lion-input-tel-dropdown.js'; +import '@lion/ui/define/lion-switch.js'; +import '@lion/ui/define/lion-input-stepper.js'; export class UmbrellaForm extends LitElement { get _lionFormNode() { diff --git a/packages/ui/components/form-integrations/test/form-group-methods.test.js b/packages/ui/components/form-integrations/test/form-group-methods.test.js index a1eb233aa..567799c7f 100644 --- a/packages/ui/components/form-integrations/test/form-group-methods.test.js +++ b/packages/ui/components/form-integrations/test/form-group-methods.test.js @@ -96,15 +96,13 @@ describe(`Submitting/Resetting/Clearing Form`, async () => { it('pressing submit button of a form should make submitted true for all fields', async () => { const el = /** @type {UmbrellaForm} */ (await fixture(html``)); await el.updateComplete; + await el.waitForAllChildrenUpdates(); + const formEl = el._lionFormNode; const allElements = getAllFieldsAndFormGroups(formEl); allElements.forEach((/** @type {LionField} */ field) => { - if (field.tagName === 'LION-SWITCH') { - // TODO: remove this when this is fixed: https://github.com/ing-bank/lion/issues/1204 - return; - } // TODO: prefer submitted 'false' over 'undefined' expect(Boolean(field.submitted)).to.be.false; }); @@ -122,6 +120,8 @@ describe(`Submitting/Resetting/Clearing Form`, async () => { html``, ) ); + await el.waitForAllChildrenUpdates(); + await el.updateComplete; const formEl = el._lionFormNode; @@ -168,6 +168,8 @@ describe(`Submitting/Resetting/Clearing Form`, async () => { html``, ) ); + await el.waitForAllChildrenUpdates(); + await el.updateComplete; const formEl = el._lionFormNode; diff --git a/packages/ui/components/form-integrations/test/form-integrations.test.js b/packages/ui/components/form-integrations/test/form-integrations.test.js index 48d711ef2..6e404be84 100644 --- a/packages/ui/components/form-integrations/test/form-integrations.test.js +++ b/packages/ui/components/form-integrations/test/form-integrations.test.js @@ -25,6 +25,7 @@ describe('Form Integrations', () => { it('".serializedValue" returns all non disabled fields based on form structure', async () => { const el = /** @type {UmbrellaForm} */ (await fixture(html``)); await el.updateComplete; + await el.waitForAllChildrenUpdates(); const formEl = el._lionFormNode; expect(formEl.serializedValue).to.eql({ @@ -46,7 +47,7 @@ describe('Form Integrations', () => { notifications: { value: '', checked: false }, rsvp: '', tel: '', - 'tel-dropdown': '', + 'tel-dropdown': '+44', comments: '', }); }); @@ -110,6 +111,7 @@ describe('Form Integrations', () => { >`, ) ); + await el.waitForAllChildrenUpdates(); await el._lionFormNode.initComplete; expect(el._lionFormNode.dirty).to.be.false; @@ -162,9 +164,10 @@ describe('Form Integrations', () => { ]; it('successfully registers all form components', async () => { - const el = /** @type {UmbrellaForm} */ await fixture(html``); - // @ts-ignore - const formEl = /** @type {LionForm} */ (el._lionFormNode); + const el = /** @type {UmbrellaForm} */ (await fixture(html``)); + await el.waitForAllChildrenUpdates(); + + const formEl = el._lionFormNode; await formEl.registrationComplete; const registeredEls = getAllTagNames(formEl); @@ -172,8 +175,10 @@ describe('Form Integrations', () => { }); it('successfully unregisters all form components', async () => { - const el = /** @type {UmbrellaForm} */ await fixture(html``); + const el = /** @type {UmbrellaForm} */ (await fixture(html``)); const offlineContainer = document.createElement('div'); + await el.waitForAllChildrenUpdates(); + // @ts-ignore const formEl = /** @type {LionForm} */ (el._lionFormNode); await formEl.registrationComplete; diff --git a/packages/ui/components/form-integrations/test/helpers/umbrella-form.js b/packages/ui/components/form-integrations/test/helpers/umbrella-form.js index 2873c4c33..c8413a24d 100644 --- a/packages/ui/components/form-integrations/test/helpers/umbrella-form.js +++ b/packages/ui/components/form-integrations/test/helpers/umbrella-form.js @@ -11,10 +11,13 @@ import '@lion/ui/define/lion-input-email.js'; import '@lion/ui/define/lion-input-tel.js'; import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-checkbox-group.js'; +import '@lion/ui/define/lion-checkbox.js'; import '@lion/ui/define/lion-radio-group.js'; +import '@lion/ui/define/lion-radio.js'; import '@lion/ui/define/lion-select.js'; import '@lion/ui/define/lion-select-rich.js'; import '@lion/ui/define/lion-listbox.js'; +import '@lion/ui/define/lion-option.js'; import '@lion/ui/define/lion-combobox.js'; import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-textarea.js'; @@ -36,6 +39,13 @@ export class UmbrellaForm extends LitElement { this.__serializedValue = v; } + /** + * Prevents errors outside test from being thrown + */ + async waitForAllChildrenUpdates() { + return Promise.all(this._lionFormNode.formElements.map(child => child.updateComplete)); + } + render() { return html` diff --git a/packages/ui/components/form-integrations/test/model-value-consistency.test.js b/packages/ui/components/form-integrations/test/model-value-consistency.test.js index dec0398bf..184e216b6 100644 --- a/packages/ui/components/form-integrations/test/model-value-consistency.test.js +++ b/packages/ui/components/form-integrations/test/model-value-consistency.test.js @@ -11,23 +11,19 @@ import '@lion/ui/define/lion-input-email.js'; import '@lion/ui/define/lion-input-iban.js'; import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-input-stepper.js'; - +import '@lion/ui/define/lion-input-tel.js'; +import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-textarea.js'; - import '@lion/ui/define/lion-checkbox.js'; import '@lion/ui/define/lion-checkbox-group.js'; - import '@lion/ui/define/lion-radio.js'; import '@lion/ui/define/lion-radio-group.js'; import '@lion/ui/define/lion-switch.js'; - import '@lion/ui/define/lion-select.js'; import '@lion/ui/define/lion-option.js'; - import '@lion/ui/define/lion-combobox.js'; import '@lion/ui/define/lion-listbox.js'; import '@lion/ui/define/lion-select-rich.js'; - import '@lion/ui/define/lion-fieldset.js'; import '@lion/ui/define/lion-form.js'; import '@lion/ui/define/lion-field.js'; @@ -344,7 +340,6 @@ describe('lion-fieldset', () => { describe('detail.isTriggeredByUser', () => { const allFormControls = [ - 'switch', // TODO: move back below when scoped-elements (polyfill) fixed side effects // 1) Fields 'field', // 1a) Input Fields @@ -356,12 +351,14 @@ describe('detail.isTriggeredByUser', () => { 'input-iban', 'input-range', 'input-stepper', + 'input-tel', + 'input-tel-dropdown', 'textarea', // 1b) Choice Fields 'option', 'checkbox', 'radio', - // 'switch', + 'switch', // 1c) Choice Group Fields 'select', 'listbox', @@ -423,9 +420,10 @@ describe('detail.isTriggeredByUser', () => { /** * @param {FormControl & {value: string;}} el * @param {string} newViewValue - * @param {{ triggerType?: string, labelInsteadOfInput?: boolean }} [optionals] + * @param {{ triggerType?: string, labelInsteadOfInput?: boolean, parent?: HTMLElement }} options */ - function mimicUserInput(el, newViewValue, optionals) { + function mimicUserInput(el, newViewValue, options = {}) { + // @ts-ignore const { _inputNode, _labelNode } = getFormControlMembers(el); const type = detectType(el); let userInputEv; @@ -434,15 +432,15 @@ describe('detail.isTriggeredByUser', () => { el.value = newViewValue; // eslint-disable-line no-param-reassign _inputNode.dispatchEvent(new Event(userInputEv, { bubbles: true })); } else if (type === 'ChoiceField') { - if (optionals?.labelInsteadOfInput) { + if (options?.labelInsteadOfInput) { _labelNode.click(); } else { _inputNode.click(); } } else if (type === 'OptionChoiceField') { - if (!optionals?.triggerType) { + if (!options?.triggerType) { el.dispatchEvent(new Event('click', { bubbles: true })); - } else if (optionals?.triggerType === 'keypress') { + } else if (options?.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 })); @@ -503,9 +501,12 @@ describe('detail.isTriggeredByUser', () => { /** * @param {FormControl & {value: string;}} formControl - * @param {boolean | undefined} [testKeyboardBehavior] + * @param {{parent?: FormControl; testKeyboardBehavior?: boolean}} moreOpts */ - async function expectCorrectEventMetaChoiceField(formControl, testKeyboardBehavior) { + async function expectCorrectEventMetaChoiceField( + formControl, + { parent, testKeyboardBehavior } = {}, + ) { const type = detectType(formControl); await resetChoiceFieldToForceRepropagation(formControl); @@ -526,14 +527,19 @@ describe('detail.isTriggeredByUser', () => { formControl.modelValue = { value: 'programmaticValue', checked: false }; expect(spy.secondCall.args[0].detail.isTriggeredByUser).to.be.false; - if (type === 'OptionChoiceField' && testKeyboardBehavior) { + if ( + type === 'OptionChoiceField' && + testKeyboardBehavior && + parent?.constructor.name !== 'LionCombobox' // modelValue only changeable via click or textbox + ) { await resetChoiceFieldToForceRepropagation(formControl); - mimicUserInput(formControl, 'userValue', { triggerType: 'keypress' }); - // TODO: get rid of try/catch (?)... + mimicUserInput(formControl, 'userValue', { triggerType: 'keypress', parent }); try { expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true; } catch (e) { - console.log(formControl); + throw new Error( + `model-value-changed of ${formControl.constructor.name} with parent ${parent?.constructor.name} not caught`, + ); } } } @@ -556,7 +562,10 @@ describe('detail.isTriggeredByUser', () => { ); el.appendChild(childrenEls); await el.registrationComplete; - await expectCorrectEventMetaChoiceField(el.formElements[0], true); + await expectCorrectEventMetaChoiceField(el.formElements[0], { + testKeyboardBehavior: true, + parent: el, + }); } else if (type === 'FormOrFieldset') { const childrenEls = await fixture( html`
`, diff --git a/packages/ui/components/input-tel/src/parsers.js b/packages/ui/components/input-tel/src/parsers.js index a9e887b0e..ed5f85373 100644 --- a/packages/ui/components/input-tel/src/parsers.js +++ b/packages/ui/components/input-tel/src/parsers.js @@ -25,7 +25,7 @@ export function parsePhoneNumber(viewValue, { regionCode }) { // eslint-disable-next-line no-empty } catch (_) {} - if (pn) { + if (pn?.number) { return pn.number.e164; } diff --git a/packages/ui/docs/fundamentals/systems/overlays/assets/umbrella-form.js b/packages/ui/docs/fundamentals/systems/overlays/assets/umbrella-form.js index 58d5f7e13..db35ab01c 100644 --- a/packages/ui/docs/fundamentals/systems/overlays/assets/umbrella-form.js +++ b/packages/ui/docs/fundamentals/systems/overlays/assets/umbrella-form.js @@ -8,15 +8,22 @@ import '@lion/ui/define/lion-input-datepicker.js'; import '@lion/ui/define/lion-input-amount.js'; import '@lion/ui/define/lion-input-iban.js'; import '@lion/ui/define/lion-input-email.js'; +import '@lion/ui/define/lion-input-tel.js'; +import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-checkbox-group.js'; +import '@lion/ui/define/lion-checkbox.js'; import '@lion/ui/define/lion-radio-group.js'; +import '@lion/ui/define/lion-radio.js'; import '@lion/ui/define/lion-select.js'; import '@lion/ui/define/lion-select-rich.js'; +import '@lion/ui/define/lion-listbox.js'; +import '@lion/ui/define/lion-option.js'; +import '@lion/ui/define/lion-combobox.js'; import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-textarea.js'; import '@lion/ui/define/lion-button.js'; -import '@lion/ui/define/lion-input-tel.js'; -import '@lion/ui/define/lion-input-tel-dropdown.js'; +import '@lion/ui/define/lion-switch.js'; +import '@lion/ui/define/lion-input-stepper.js'; export class UmbrellaForm extends LitElement { get _lionFormNode() {