From 181a1d452b74cc5213db1dc47dc70a856473f9a9 Mon Sep 17 00:00:00 2001 From: Thijs Louisse Date: Tue, 6 Apr 2021 12:44:42 +0200 Subject: [PATCH] fix(form-core): choice-group initialization values + null support --- .changeset/old-starfishes-glow.md | 6 ++ .../src/choice-group/ChoiceGroupMixin.js | 12 +++- .../choice-group/ChoiceGroupMixin.suite.js | 68 ++++++++++++++++++- .../types/FormControlMixinTypes.d.ts | 2 +- 4 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 .changeset/old-starfishes-glow.md diff --git a/.changeset/old-starfishes-glow.md b/.changeset/old-starfishes-glow.md new file mode 100644 index 000000000..77a5eda8c --- /dev/null +++ b/.changeset/old-starfishes-glow.md @@ -0,0 +1,6 @@ +--- +'@lion/form-core': patch +--- + +- form-core: pending initialization values in order of execution +- form-core: choiceGroup null and undefined values support diff --git a/packages/form-core/src/choice-group/ChoiceGroupMixin.js b/packages/form-core/src/choice-group/ChoiceGroupMixin.js index be1ca374f..9e26138a9 100644 --- a/packages/form-core/src/choice-group/ChoiceGroupMixin.js +++ b/packages/form-core/src/choice-group/ChoiceGroupMixin.js @@ -53,8 +53,8 @@ const ChoiceGroupMixinImplementation = superclass => }; if (this.__isInitialModelValue) { - this.__isInitialModelValue = false; this.registrationComplete.then(() => { + this.__isInitialModelValue = false; this._setCheckedElements(value, checkCondition); this.requestUpdate('modelValue', this.__oldModelValue); }); @@ -89,8 +89,8 @@ const ChoiceGroupMixinImplementation = superclass => const checkCondition = (el, val) => el.serializedValue.value === val; if (this.__isInitialSerializedValue) { - this.__isInitialSerializedValue = false; this.registrationComplete.then(() => { + this.__isInitialSerializedValue = false; this._setCheckedElements(value, checkCondition); this.requestUpdate('serializedValue'); }); @@ -116,8 +116,8 @@ const ChoiceGroupMixinImplementation = superclass => const checkCondition = (el, val) => el.formattedValue === val; if (this.__isInitialFormattedValue) { - this.__isInitialFormattedValue = false; this.registrationComplete.then(() => { + this.__isInitialFormattedValue = false; this._setCheckedElements(value, checkCondition); }); } else { @@ -307,6 +307,12 @@ const ChoiceGroupMixinImplementation = superclass => * @protected */ _setCheckedElements(value, check) { + if (value === null || value === undefined) { + // Uncheck all + // eslint-disable-next-line no-return-assign, no-param-reassign + this.formElements.forEach(fe => (fe.checked = false)); + return; + } for (let i = 0; i < this.formElements.length; i += 1) { if (this.multipleChoice) { let valueIsIncluded = value.includes(this.formElements[i].modelValue.value); diff --git a/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js b/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js index b3e3ee749..8d6cb2533 100644 --- a/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js +++ b/packages/form-core/test-suites/choice-group/ChoiceGroupMixin.suite.js @@ -2,7 +2,7 @@ import { LitElement } from '@lion/core'; import { LionInput } from '@lion/input'; import '@lion/fieldset/define'; import { FormGroupMixin, Required } from '@lion/form-core'; -import { expect, html, fixture, unsafeStatic } from '@open-wc/testing'; +import { expect, html, fixture, fixtureSync, unsafeStatic } from '@open-wc/testing'; import { ChoiceGroupMixin } from '../../src/choice-group/ChoiceGroupMixin.js'; import { ChoiceInputMixin } from '../../src/choice-group/ChoiceInputMixin.js'; @@ -246,6 +246,72 @@ export function runChoiceGroupMixinSuite({ parentTagString, childTagString, choi expect(el.formElements[2].checked).to.be.true; }); + it('correctly handles modelValue being set before registrationComplete', async () => { + const el = /** @type {ChoiceInputGroup} */ (fixtureSync(html` + <${parentTag} name="gender[]" .modelValue=${null}> + <${childTag} .choiceValue=${'male'}> + <${childTag} .choiceValue=${'female'}> + <${childTag} .choiceValue=${'other'}> + + `)); + + if (cfg.choiceType === 'single') { + el.modelValue = 'other'; + await el.registrationComplete; + expect(el.modelValue).to.equal('other'); + } else { + el.modelValue = ['other']; + await el.registrationComplete; + expect(el.modelValue).to.deep.equal(['other']); + } + }); + + it('correctly handles serializedValue being set before registrationComplete', async () => { + const el = /** @type {ChoiceInputGroup} */ (fixtureSync(html` + <${parentTag} name="gender[]" .serializedValue=${null}> + <${childTag} .choiceValue=${'male'}> + <${childTag} .choiceValue=${'female'}> + <${childTag} .choiceValue=${'other'}> + + `)); + + if (cfg.choiceType === 'single') { + // @ts-expect-error + el.serializedValue = 'other'; + await el.registrationComplete; + expect(el.serializedValue).to.equal('other'); + } else { + // @ts-expect-error + el.serializedValue = ['other']; + await el.registrationComplete; + expect(el.serializedValue).to.deep.equal(['other']); + } + }); + + it('can handle null and undefined modelValues', async () => { + const el = /** @type {ChoiceInputGroup} */ (await fixture(html` + <${parentTag} name="gender[]" .modelValue=${null}> + <${childTag} .choiceValue=${'male'}> + <${childTag} .choiceValue=${'female'}> + <${childTag} .choiceValue=${'other'}> + + `)); + + if (cfg.choiceType === 'single') { + expect(el.modelValue).to.equal(''); + } else { + expect(el.modelValue).to.deep.equal([]); + } + + el.modelValue = undefined; + await el.updateComplete; + if (cfg.choiceType === 'single') { + expect(el.modelValue).to.equal(''); + } else { + expect(el.modelValue).to.deep.equal([]); + } + }); + it('can handle complex data via choiceValue', async () => { const date = new Date(2018, 11, 24, 10, 33, 30, 0); diff --git a/packages/form-core/types/FormControlMixinTypes.d.ts b/packages/form-core/types/FormControlMixinTypes.d.ts index 2c403ecf7..e25aa6a73 100644 --- a/packages/form-core/types/FormControlMixinTypes.d.ts +++ b/packages/form-core/types/FormControlMixinTypes.d.ts @@ -123,7 +123,7 @@ export declare class FormControlHost { * (think of an amount-input with a currency select box next to it), can set this * to true to hide private internals in the formPath. */ - _isRepropagationEndpoint: boolean; + protected _isRepropagationEndpoint: boolean; connectedCallback(): void; updated(changedProperties: import('@lion/core').PropertyValues): void;