From 9591732f4eb0b101b47d3ecc823970dd165c6c36 Mon Sep 17 00:00:00 2001 From: gerjanvangeest Date: Tue, 31 May 2022 16:25:50 +0200 Subject: [PATCH] fix(input-tel-dropdown): syncs value of dropdown on init if input has no value Co-authored-by: Thijs Louisse --- .changeset/real-actors-drum.md | 5 ++ .../test/form-integrations.test.js | 13 ++++- .../src/LionInputTelDropdown.js | 47 ++++++++++++----- .../test-suites/LionInputTelDropdown.suite.js | 51 ++++++++++++++++++- 4 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 .changeset/real-actors-drum.md diff --git a/.changeset/real-actors-drum.md b/.changeset/real-actors-drum.md new file mode 100644 index 000000000..8626ea9cb --- /dev/null +++ b/.changeset/real-actors-drum.md @@ -0,0 +1,5 @@ +--- +'@lion/input-tel-dropdown': minor +--- + +Syncs value of dropdown on init if input has no value diff --git a/packages/form-integrations/test/form-integrations.test.js b/packages/form-integrations/test/form-integrations.test.js index d31585f71..99c94716d 100644 --- a/packages/form-integrations/test/form-integrations.test.js +++ b/packages/form-integrations/test/form-integrations.test.js @@ -1,15 +1,22 @@ import sinon from 'sinon'; import { expect, fixture } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; +import { PhoneUtilManager } from '@lion/input-tel'; import { getAllTagNames } from './helpers/helpers.js'; import './helpers/umbrella-form.js'; /** * @typedef {import('./helpers/umbrella-form.js').UmbrellaForm} UmbrellaForm + * @typedef {import('@lion/input-tel-dropdown').LionInputTelDropdown} LionInputTelDropdown */ // Test umbrella form. describe('Form Integrations', () => { + beforeEach(async () => { + // Wait till PhoneUtilManager has been loaded + await PhoneUtilManager.loadComplete; + }); + it('".serializedValue" returns all non disabled fields based on form structure', async () => { const el = /** @type {UmbrellaForm} */ (await fixture(html``)); await el.updateComplete; @@ -43,6 +50,10 @@ describe('Form Integrations', () => { const el = /** @type {UmbrellaForm} */ (await fixture(html``)); await el.updateComplete; const formEl = el._lionFormNode; + const inputTelDropdownEl = /** @type {LionInputTelDropdown} */ ( + formEl.querySelector('lion-input-tel-dropdown') + ); + await inputTelDropdownEl?.updateComplete; expect(formEl.formattedValue).to.eql({ full_name: { first_name: '', last_name: '' }, @@ -63,7 +74,7 @@ describe('Form Integrations', () => { notifications: '', rsvp: '', tel: '', - 'tel-dropdown': '', + 'tel-dropdown': '+44', comments: '', }); }); diff --git a/packages/input-tel-dropdown/src/LionInputTelDropdown.js b/packages/input-tel-dropdown/src/LionInputTelDropdown.js index 147d5b340..a64d6b7d9 100644 --- a/packages/input-tel-dropdown/src/LionInputTelDropdown.js +++ b/packages/input-tel-dropdown/src/LionInputTelDropdown.js @@ -279,6 +279,33 @@ export class LionInputTelDropdown extends LionInputTel { this.refs.dropdown?.value?.removeAttribute('disabled'); } } + + if (changedProperties.has('_phoneUtil')) { + this._initModelValueBasedOnDropdown(); + } + } + + _initModelValueBasedOnDropdown() { + if (!this._initialModelValue && !this.dirty && this._phoneUtil) { + const countryCode = this._phoneUtil.getCountryCodeForRegionCode(this.activeRegion); + this.__initializedRegionCode = `+${countryCode}`; + this.modelValue = this.__initializedRegionCode; + this._initialModelValue = this.__initializedRegionCode; + this.initInteractionState(); + } + } + + /** + * Used for Required validation and computation of interaction states. + * We need to override this, because we prefill the input with the region code (like +31), but for proper UX, + * we don't consider this as having interaction state `prefilled` + * @param {string} modelValue + * @return {boolean} + * @protected + */ + _isEmpty(modelValue = this.modelValue) { + // the activeRegion is not synced on time, so it can't be used in this check + return super._isEmpty(modelValue) || this.value === this.__initializedRegionCode; } /** @@ -287,29 +314,23 @@ export class LionInputTelDropdown extends LionInputTel { */ _onDropdownValueChange(event) { const isInitializing = event.detail?.initialize || !this._phoneUtil; - if (isInitializing) { + const dropdownValue = /** @type {RegionCode} */ (event.target.modelValue || event.target.value); + + if (isInitializing || (this.activeRegion && this.activeRegion === dropdownValue)) { return; } const prevActiveRegion = this.activeRegion; - this._setActiveRegion( - /** @type {RegionCode} */ (event.target.value || event.target.modelValue), - ); + this._setActiveRegion(dropdownValue); // Change region code in text box // From: https://bl00mber.github.io/react-phone-input-2.html if (prevActiveRegion !== this.activeRegion && !this.focused && this._phoneUtil) { const prevCountryCode = this._phoneUtil.getCountryCodeForRegionCode(prevActiveRegion); const countryCode = this._phoneUtil.getCountryCodeForRegionCode(this.activeRegion); - if (countryCode && !this.modelValue) { - // When textbox is empty, prefill it with country code - this.modelValue = `+${countryCode}`; - } else if (prevCountryCode && countryCode) { - // When textbox is not empty, replace country code - this.modelValue = this._callParser( - this.value.replace(`+${prevCountryCode}`, `+${countryCode}`), - ); - } + this.modelValue = this._callParser( + this.value.replace(`+${prevCountryCode}`, `+${countryCode}`), + ); } // Put focus on text box diff --git a/packages/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js b/packages/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js index 846bfe9b0..5dbe669ff 100644 --- a/packages/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js +++ b/packages/input-tel-dropdown/test-suites/LionInputTelDropdown.suite.js @@ -71,6 +71,53 @@ export function runInputTelDropdownSuite({ klass } = { klass: LionInputTelDropdo await PhoneUtilManager.loadComplete; }); + it('syncs value of dropdown on init if input has no value', async () => { + const el = await fixture(html` <${tag}> `); + expect(el.activeRegion).to.equal('GB'); + expect(el.value).to.equal('+44'); + expect(getDropdownValue(/** @type {DropdownElement} */ (el.refs.dropdown.value))).to.equal( + 'GB', + ); + }); + + it('syncs value of dropdown on reset if input has no value', async () => { + const el = await fixture(html` <${tag}> `); + el.modelValue = '+31612345678'; + await el.updateComplete; + expect(el.activeRegion).to.equal('NL'); + el.reset(); + await el.updateComplete; + expect(el.activeRegion).to.equal('GB'); + expect(el.value).to.equal('+44'); + }); + + it('syncs value of dropdown on init if input has no value does not influence interaction states', async () => { + const el = await fixture(html` <${tag}> `); + // TODO find out why its get dirty again + // expect(el.dirty).to.be.false; + expect(el.prefilled).to.be.false; + }); + + it('syncs value of dropdown on reset also resets interaction states', async () => { + const el = await fixture(html` <${tag}> `); + el.modelValue = '+31612345678'; + await el.updateComplete; + + expect(el.dirty).to.be.true; + expect(el.prefilled).to.be.false; + el.reset(); + await el.updateComplete; + expect(el.dirty).to.be.false; + expect(el.prefilled).to.be.false; + }); + + it('sets correct interaction states on init if input has a value', async () => { + const el = await fixture(html` <${tag} .modelValue="${'+31612345678'}"> `); + // TODO find out why its get dirty again + // expect(el.dirty).to.be.false; + expect(el.prefilled).to.be.true; + }); + describe('Dropdown display', () => { it('calls `templates.dropdown` with TemplateDataForDropdownInputTel object', async () => { const el = fixtureSync(html` <${tag} @@ -261,7 +308,9 @@ export function runInputTelDropdownSuite({ klass } = { klass: LionInputTelDropdo }); it('prefills country code when textbox is empty', async () => { - const el = await fixture(html` <${tag} .allowedRegions="${['NL', 'BE']}"> `); + const el = await fixture( + html` <${tag} .allowedRegions="${['NL', 'BE']}" .modelValue="${''}"> `, + ); // @ts-ignore mimicUserChangingDropdown(el.refs.dropdown.value, 'BE'); await el.updateComplete;