diff --git a/.changeset/olive-meals-explode.md b/.changeset/olive-meals-explode.md new file mode 100644 index 0000000000..fb90fa0090 --- /dev/null +++ b/.changeset/olive-meals-explode.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[form-core] Updated behavior of name attribute in FormRegisteringMixin to convert values to string type diff --git a/packages/ui/components/form-core/src/FormControlMixin.js b/packages/ui/components/form-core/src/FormControlMixin.js index 7eda6888cf..fd232e82ee 100644 --- a/packages/ui/components/form-core/src/FormControlMixin.js +++ b/packages/ui/components/form-core/src/FormControlMixin.js @@ -34,7 +34,6 @@ const FormControlMixinImplementation = superclass => /** @type {any} */ static get properties() { return { - name: { type: String, reflect: true }, readOnly: { type: Boolean, attribute: 'readonly', reflect: true }, label: String, // FIXME: { attribute: false } breaks a bunch of tests, but shouldn't... labelSrOnly: { type: Boolean, attribute: 'label-sr-only', reflect: true }, @@ -156,14 +155,6 @@ const FormControlMixinImplementation = superclass => constructor() { super(); - /** - * The name the element will be registered with to the .formElements collection - * of the parent. Also, it serves as the key of key/value pairs in - * modelValue/serializedValue objects - * @type {string} - */ - this.name = ''; - /** * A Boolean attribute which, if present, indicates that the user should not be able to edit * the value of the input. The difference between disabled and readonly is that read-only diff --git a/packages/ui/components/form-core/src/registration/FormRegisteringMixin.js b/packages/ui/components/form-core/src/registration/FormRegisteringMixin.js index c8fb4a31f8..3763ade3b1 100644 --- a/packages/ui/components/form-core/src/registration/FormRegisteringMixin.js +++ b/packages/ui/components/form-core/src/registration/FormRegisteringMixin.js @@ -22,6 +22,14 @@ const FormRegisteringMixinImplementation = superclass => class extends superclass { constructor() { super(); + /** + * The name the element will be registered with to the .formElements collection + * of the parent. Also, it serves as the key of key/value pairs in + * modelValue/serializedValue objects + * @type {string} + */ + this.name = ''; + /** * The registrar this FormControl registers to, Usually a descendant of FormGroup or * ChoiceGroup @@ -37,8 +45,28 @@ const FormRegisteringMixinImplementation = superclass => this.allowCrossRootRegistration = false; } + /** + * Name attribute for the control. + * @type {string} + */ + get name() { + return this.__name || ''; + } + + /** + * Converts values provided for the `name` attribute to string type. + * Mimics the native `input` behavior. + * @param {string} newName + */ + set name(newName) { + const oldName = this.name; + this.__name = newName.toString(); + this.requestUpdate('name', oldName); + } + static get properties() { return { + name: { type: String, reflect: true }, allowCrossRootRegistration: { type: Boolean, attribute: 'allow-cross-root-registration' }, }; } diff --git a/packages/ui/components/form-core/test-suites/FormRegistrationMixins.suite.js b/packages/ui/components/form-core/test-suites/FormRegistrationMixins.suite.js index 71864f515e..da7f169af2 100644 --- a/packages/ui/components/form-core/test-suites/FormRegistrationMixins.suite.js +++ b/packages/ui/components/form-core/test-suites/FormRegistrationMixins.suite.js @@ -310,6 +310,23 @@ export const runRegistrationSuite = customConfig => { expect(eventSpy).to.have.been.calledOnce; expect(eventSpy.getCall(0).args[0].composed).to.equal(false); }); + it('accepts a name attribute and converts the values provided to a string', async () => { + const elAttr = /** @type {RegisteringClass} */ ( + await fixture(html`<${childTag} name=${5}>`) + ); + + expect(elAttr.hasAttribute('name')).to.be.true; + expect(elAttr.name).to.be.a('string'); + expect(elAttr.name).to.equal('5', 'as an attribute'); + + const elProp = /** @type {RegisteringClass} */ ( + await fixture(html`<${childTag} .name=${5}>`) + ); + + expect(elProp.hasAttribute('name')).to.be.true; + expect(elProp.name).to.be.a('string'); + expect(elProp.name).to.equal('5', 'as a property'); + }); }); describe('FormRegistrarPortalMixin', () => { it('forwards registrations to the .registrationTarget', async () => { diff --git a/packages/ui/components/form-core/types/FormControlMixinTypes.ts b/packages/ui/components/form-core/types/FormControlMixinTypes.ts index 3b084e8d11..e1fe23c113 100644 --- a/packages/ui/components/form-core/types/FormControlMixinTypes.ts +++ b/packages/ui/components/form-core/types/FormControlMixinTypes.ts @@ -43,10 +43,6 @@ export declare interface HTMLElementWithValue extends HTMLElement { export declare class FormControlHost { static get styles(): CSSResultArray; static get properties(): { - name: { - type: StringConstructor; - reflect: boolean; - }; readOnly: { type: BooleanConstructor; attribute: string; diff --git a/packages/ui/components/form-core/types/registration/FormRegisteringMixinTypes.ts b/packages/ui/components/form-core/types/registration/FormRegisteringMixinTypes.ts index 2ec4ba313f..282fec5191 100644 --- a/packages/ui/components/form-core/types/registration/FormRegisteringMixinTypes.ts +++ b/packages/ui/components/form-core/types/registration/FormRegisteringMixinTypes.ts @@ -4,10 +4,13 @@ import { LitElement } from 'lit'; import { FormRegistrarHost } from './FormRegistrarMixinTypes.js'; export declare class FormRegisteringHost { - /** - * The name the host is registered with to a parent - */ - name: string; + static get properties(): { + name: { + type: StringConstructor; + reflect: boolean; + }; + } + /** * To encourage accessibility best practices, `form-element-register` events * do not pierce through shadow roots. This forces the developer to create form groups and fieldsets that @@ -17,12 +20,22 @@ export declare class FormRegisteringHost { */ allowCrossRootRegistration: boolean; + /** + * The name the element will be registered with to the .formElements collection + * of the parent. Also, it serves as the key of key/value pairs in + * modelValue/serializedValue objects + */ + get name(): string; + set name(arg: any); + /** * The registrar this FormControl registers to, Usually a descendant of FormGroup or * ChoiceGroup */ protected _parentFormGroup: FormRegistrarHost | undefined; + private __name: string; + private __unregisterFormElement: void; }