chore: fix all form integrations tech debt

This commit is contained in:
Thijs Louisse 2022-12-06 09:41:12 +01:00 committed by Thijs Louisse
parent f3191e8993
commit 5e15942386
8 changed files with 78 additions and 34 deletions

View file

@ -7,7 +7,9 @@ For usage and installation please see the appropriate packages.
import { html } from '@mdjs/mdjs-preview'; import { html } from '@mdjs/mdjs-preview';
import '@lion/ui/define/lion-button.js'; import '@lion/ui/define/lion-button.js';
import '@lion/ui/define/lion-checkbox-group.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-combobox.js';
import '@lion/ui/define/lion-option.js';
import '@lion/ui/define/lion-fieldset.js'; import '@lion/ui/define/lion-fieldset.js';
import '@lion/ui/define/lion-form.js'; import '@lion/ui/define/lion-form.js';
import '@lion/ui/define/lion-input-amount.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-datepicker.js';
import '@lion/ui/define/lion-input-email.js'; import '@lion/ui/define/lion-input-email.js';
import '@lion/ui/define/lion-input-tel.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-iban.js';
import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-input-range.js';
import '@lion/ui/define/lion-input-stepper.js'; import '@lion/ui/define/lion-input-stepper.js';
import '@lion/ui/define/lion-input.js'; import '@lion/ui/define/lion-input.js';
import '@lion/ui/define/lion-listbox.js'; import '@lion/ui/define/lion-listbox.js';
import '@lion/ui/define/lion-radio-group.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.js';
import '@lion/ui/define/lion-select-rich.js'; import '@lion/ui/define/lion-select-rich.js';
import '@lion/ui/define/lion-switch.js'; import '@lion/ui/define/lion-switch.js';

View file

@ -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-amount.js';
import '@lion/ui/define/lion-input-iban.js'; import '@lion/ui/define/lion-input-iban.js';
import '@lion/ui/define/lion-input-email.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-group.js';
import '@lion/ui/define/lion-checkbox.js';
import '@lion/ui/define/lion-radio-group.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.js';
import '@lion/ui/define/lion-select-rich.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-input-range.js';
import '@lion/ui/define/lion-textarea.js'; import '@lion/ui/define/lion-textarea.js';
import '@lion/ui/define/lion-button.js'; import '@lion/ui/define/lion-button.js';
import '@lion/ui/define/lion-input-tel.js'; import '@lion/ui/define/lion-switch.js';
import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-input-stepper.js';
export class UmbrellaForm extends LitElement { export class UmbrellaForm extends LitElement {
get _lionFormNode() { get _lionFormNode() {

View file

@ -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 () => { it('pressing submit button of a form should make submitted true for all fields', async () => {
const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`)); const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`));
await el.updateComplete; await el.updateComplete;
await el.waitForAllChildrenUpdates();
const formEl = el._lionFormNode; const formEl = el._lionFormNode;
const allElements = getAllFieldsAndFormGroups(formEl); const allElements = getAllFieldsAndFormGroups(formEl);
allElements.forEach((/** @type {LionField} */ field) => { 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' // TODO: prefer submitted 'false' over 'undefined'
expect(Boolean(field.submitted)).to.be.false; expect(Boolean(field.submitted)).to.be.false;
}); });
@ -122,6 +120,8 @@ describe(`Submitting/Resetting/Clearing Form`, async () => {
html`<umbrella-form .serializedValue="${fullyPrefilledSerializedValue}"></umbrella-form>`, html`<umbrella-form .serializedValue="${fullyPrefilledSerializedValue}"></umbrella-form>`,
) )
); );
await el.waitForAllChildrenUpdates();
await el.updateComplete; await el.updateComplete;
const formEl = el._lionFormNode; const formEl = el._lionFormNode;
@ -168,6 +168,8 @@ describe(`Submitting/Resetting/Clearing Form`, async () => {
html`<umbrella-form .serializedValue="${fullyPrefilledSerializedValue}"></umbrella-form>`, html`<umbrella-form .serializedValue="${fullyPrefilledSerializedValue}"></umbrella-form>`,
) )
); );
await el.waitForAllChildrenUpdates();
await el.updateComplete; await el.updateComplete;
const formEl = el._lionFormNode; const formEl = el._lionFormNode;

View file

@ -25,6 +25,7 @@ describe('Form Integrations', () => {
it('".serializedValue" returns all non disabled fields based on form structure', async () => { it('".serializedValue" returns all non disabled fields based on form structure', async () => {
const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`)); const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`));
await el.updateComplete; await el.updateComplete;
await el.waitForAllChildrenUpdates();
const formEl = el._lionFormNode; const formEl = el._lionFormNode;
expect(formEl.serializedValue).to.eql({ expect(formEl.serializedValue).to.eql({
@ -46,7 +47,7 @@ describe('Form Integrations', () => {
notifications: { value: '', checked: false }, notifications: { value: '', checked: false },
rsvp: '', rsvp: '',
tel: '', tel: '',
'tel-dropdown': '', 'tel-dropdown': '+44',
comments: '', comments: '',
}); });
}); });
@ -110,6 +111,7 @@ describe('Form Integrations', () => {
></umbrella-form>`, ></umbrella-form>`,
) )
); );
await el.waitForAllChildrenUpdates();
await el._lionFormNode.initComplete; await el._lionFormNode.initComplete;
expect(el._lionFormNode.dirty).to.be.false; expect(el._lionFormNode.dirty).to.be.false;
@ -162,9 +164,10 @@ describe('Form Integrations', () => {
]; ];
it('successfully registers all form components', async () => { it('successfully registers all form components', async () => {
const el = /** @type {UmbrellaForm} */ await fixture(html`<umbrella-form></umbrella-form>`); const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`));
// @ts-ignore await el.waitForAllChildrenUpdates();
const formEl = /** @type {LionForm} */ (el._lionFormNode);
const formEl = el._lionFormNode;
await formEl.registrationComplete; await formEl.registrationComplete;
const registeredEls = getAllTagNames(formEl); const registeredEls = getAllTagNames(formEl);
@ -172,8 +175,10 @@ describe('Form Integrations', () => {
}); });
it('successfully unregisters all form components', async () => { it('successfully unregisters all form components', async () => {
const el = /** @type {UmbrellaForm} */ await fixture(html`<umbrella-form></umbrella-form>`); const el = /** @type {UmbrellaForm} */ (await fixture(html`<umbrella-form></umbrella-form>`));
const offlineContainer = document.createElement('div'); const offlineContainer = document.createElement('div');
await el.waitForAllChildrenUpdates();
// @ts-ignore // @ts-ignore
const formEl = /** @type {LionForm} */ (el._lionFormNode); const formEl = /** @type {LionForm} */ (el._lionFormNode);
await formEl.registrationComplete; await formEl.registrationComplete;

View file

@ -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.js';
import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-input-tel-dropdown.js';
import '@lion/ui/define/lion-checkbox-group.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-group.js';
import '@lion/ui/define/lion-radio.js';
import '@lion/ui/define/lion-select.js'; import '@lion/ui/define/lion-select.js';
import '@lion/ui/define/lion-select-rich.js'; import '@lion/ui/define/lion-select-rich.js';
import '@lion/ui/define/lion-listbox.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-combobox.js';
import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-input-range.js';
import '@lion/ui/define/lion-textarea.js'; import '@lion/ui/define/lion-textarea.js';
@ -36,6 +39,13 @@ export class UmbrellaForm extends LitElement {
this.__serializedValue = v; this.__serializedValue = v;
} }
/**
* Prevents errors outside test from being thrown
*/
async waitForAllChildrenUpdates() {
return Promise.all(this._lionFormNode.formElements.map(child => child.updateComplete));
}
render() { render() {
return html` return html`
<lion-form .serializedValue="${this.__serializedValue}"> <lion-form .serializedValue="${this.__serializedValue}">

View file

@ -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-iban.js';
import '@lion/ui/define/lion-input-range.js'; import '@lion/ui/define/lion-input-range.js';
import '@lion/ui/define/lion-input-stepper.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-textarea.js';
import '@lion/ui/define/lion-checkbox.js'; import '@lion/ui/define/lion-checkbox.js';
import '@lion/ui/define/lion-checkbox-group.js'; import '@lion/ui/define/lion-checkbox-group.js';
import '@lion/ui/define/lion-radio.js'; import '@lion/ui/define/lion-radio.js';
import '@lion/ui/define/lion-radio-group.js'; import '@lion/ui/define/lion-radio-group.js';
import '@lion/ui/define/lion-switch.js'; import '@lion/ui/define/lion-switch.js';
import '@lion/ui/define/lion-select.js'; import '@lion/ui/define/lion-select.js';
import '@lion/ui/define/lion-option.js'; import '@lion/ui/define/lion-option.js';
import '@lion/ui/define/lion-combobox.js'; import '@lion/ui/define/lion-combobox.js';
import '@lion/ui/define/lion-listbox.js'; import '@lion/ui/define/lion-listbox.js';
import '@lion/ui/define/lion-select-rich.js'; import '@lion/ui/define/lion-select-rich.js';
import '@lion/ui/define/lion-fieldset.js'; import '@lion/ui/define/lion-fieldset.js';
import '@lion/ui/define/lion-form.js'; import '@lion/ui/define/lion-form.js';
import '@lion/ui/define/lion-field.js'; import '@lion/ui/define/lion-field.js';
@ -344,7 +340,6 @@ describe('lion-fieldset', () => {
describe('detail.isTriggeredByUser', () => { describe('detail.isTriggeredByUser', () => {
const allFormControls = [ const allFormControls = [
'switch', // TODO: move back below when scoped-elements (polyfill) fixed side effects
// 1) Fields // 1) Fields
'field', 'field',
// 1a) Input Fields // 1a) Input Fields
@ -356,12 +351,14 @@ describe('detail.isTriggeredByUser', () => {
'input-iban', 'input-iban',
'input-range', 'input-range',
'input-stepper', 'input-stepper',
'input-tel',
'input-tel-dropdown',
'textarea', 'textarea',
// 1b) Choice Fields // 1b) Choice Fields
'option', 'option',
'checkbox', 'checkbox',
'radio', 'radio',
// 'switch', 'switch',
// 1c) Choice Group Fields // 1c) Choice Group Fields
'select', 'select',
'listbox', 'listbox',
@ -423,9 +420,10 @@ describe('detail.isTriggeredByUser', () => {
/** /**
* @param {FormControl & {value: string;}} el * @param {FormControl & {value: string;}} el
* @param {string} newViewValue * @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 { _inputNode, _labelNode } = getFormControlMembers(el);
const type = detectType(el); const type = detectType(el);
let userInputEv; let userInputEv;
@ -434,15 +432,15 @@ describe('detail.isTriggeredByUser', () => {
el.value = newViewValue; // eslint-disable-line no-param-reassign el.value = newViewValue; // eslint-disable-line no-param-reassign
_inputNode.dispatchEvent(new Event(userInputEv, { bubbles: true })); _inputNode.dispatchEvent(new Event(userInputEv, { bubbles: true }));
} else if (type === 'ChoiceField') { } else if (type === 'ChoiceField') {
if (optionals?.labelInsteadOfInput) { if (options?.labelInsteadOfInput) {
_labelNode.click(); _labelNode.click();
} else { } else {
_inputNode.click(); _inputNode.click();
} }
} else if (type === 'OptionChoiceField') { } else if (type === 'OptionChoiceField') {
if (!optionals?.triggerType) { if (!options?.triggerType) {
el.dispatchEvent(new Event('click', { bubbles: true })); 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('keydown', { key: 'ArrowDown', bubbles: true }));
el.dispatchEvent(new KeyboardEvent('keyup', { 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('keydown', { key: 'Enter', bubbles: true }));
@ -503,9 +501,12 @@ describe('detail.isTriggeredByUser', () => {
/** /**
* @param {FormControl & {value: string;}} formControl * @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); const type = detectType(formControl);
await resetChoiceFieldToForceRepropagation(formControl); await resetChoiceFieldToForceRepropagation(formControl);
@ -526,14 +527,19 @@ describe('detail.isTriggeredByUser', () => {
formControl.modelValue = { value: 'programmaticValue', checked: false }; formControl.modelValue = { value: 'programmaticValue', checked: false };
expect(spy.secondCall.args[0].detail.isTriggeredByUser).to.be.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); await resetChoiceFieldToForceRepropagation(formControl);
mimicUserInput(formControl, 'userValue', { triggerType: 'keypress' }); mimicUserInput(formControl, 'userValue', { triggerType: 'keypress', parent });
// TODO: get rid of try/catch (?)...
try { try {
expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true; expect(spy.firstCall.args[0].detail.isTriggeredByUser).to.be.true;
} catch (e) { } 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); el.appendChild(childrenEls);
await el.registrationComplete; await el.registrationComplete;
await expectCorrectEventMetaChoiceField(el.formElements[0], true); await expectCorrectEventMetaChoiceField(el.formElements[0], {
testKeyboardBehavior: true,
parent: el,
});
} else if (type === 'FormOrFieldset') { } else if (type === 'FormOrFieldset') {
const childrenEls = await fixture( const childrenEls = await fixture(
html`<div><lion-input name="one"></lion-input><lion-input name="two"></lion-input></div>`, html`<div><lion-input name="one"></lion-input><lion-input name="two"></lion-input></div>`,

View file

@ -25,7 +25,7 @@ export function parsePhoneNumber(viewValue, { regionCode }) {
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} catch (_) {} } catch (_) {}
if (pn) { if (pn?.number) {
return pn.number.e164; return pn.number.e164;
} }

View file

@ -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-amount.js';
import '@lion/ui/define/lion-input-iban.js'; import '@lion/ui/define/lion-input-iban.js';
import '@lion/ui/define/lion-input-email.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-group.js';
import '@lion/ui/define/lion-checkbox.js';
import '@lion/ui/define/lion-radio-group.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.js';
import '@lion/ui/define/lion-select-rich.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-input-range.js';
import '@lion/ui/define/lion-textarea.js'; import '@lion/ui/define/lion-textarea.js';
import '@lion/ui/define/lion-button.js'; import '@lion/ui/define/lion-button.js';
import '@lion/ui/define/lion-input-tel.js'; import '@lion/ui/define/lion-switch.js';
import '@lion/ui/define/lion-input-tel-dropdown.js'; import '@lion/ui/define/lion-input-stepper.js';
export class UmbrellaForm extends LitElement { export class UmbrellaForm extends LitElement {
get _lionFormNode() { get _lionFormNode() {