fix(form-core): update formElements with the right name on change
This commit is contained in:
parent
247e64a3cc
commit
fd297a2832
8 changed files with 59 additions and 14 deletions
5
.changeset/hungry-files-lay.md
Normal file
5
.changeset/hungry-files-lay.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@lion/form-core': patch
|
||||
---
|
||||
|
||||
Properly update formElements when the name attribute changes, in order to get an updated serializedValue.
|
||||
|
|
@ -23,8 +23,8 @@ function uuid(prefix) {
|
|||
* @typedef {import('@lion/core').nothing} nothing
|
||||
* @typedef {import('@lion/core/types/SlotMixinTypes').SlotsMap} SlotsMap
|
||||
* @typedef {import('../types/FormControlMixinTypes.js').FormControlMixin} FormControlMixin
|
||||
* @type {FormControlMixin}
|
||||
* @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass
|
||||
* @type {FormControlMixin}
|
||||
*/
|
||||
const FormControlMixinImplementation = superclass =>
|
||||
// eslint-disable-next-line no-shadow, no-unused-vars
|
||||
|
|
@ -144,8 +144,7 @@ const FormControlMixinImplementation = superclass =>
|
|||
* @return {string}
|
||||
*/
|
||||
get fieldName() {
|
||||
// @ts-expect-error
|
||||
return this.__fieldName || this.label || this.name; // FIXME: when LionField is typed we can inherit this prop
|
||||
return this.__fieldName || this.label || this.name || '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -195,6 +194,8 @@ const FormControlMixinImplementation = superclass =>
|
|||
|
||||
constructor() {
|
||||
super();
|
||||
/** @type {string | undefined} */
|
||||
this.name = undefined;
|
||||
/** @type {string} */
|
||||
this._inputId = uuid(this.localName);
|
||||
/** @type {HTMLElement[]} */
|
||||
|
|
@ -257,6 +258,15 @@ const FormControlMixinImplementation = superclass =>
|
|||
if (changedProperties.has('helpText') && this._helpTextNode) {
|
||||
this._helpTextNode.textContent = this.helpText;
|
||||
}
|
||||
|
||||
if (changedProperties.has('name')) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('form-element-name-changed', {
|
||||
detail: { oldName: changedProperties.get('name'), newName: this.name },
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_triggerInitialModelValueChangedEvent() {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { InteractionStateMixin } from '../InteractionStateMixin.js';
|
|||
* @param {import('@open-wc/dedupe-mixin').Constructor<import('@lion/core').LitElement>} superclass
|
||||
*/
|
||||
const ChoiceGroupMixinImplementation = superclass =>
|
||||
// @ts-expect-error
|
||||
class ChoiceGroupMixin extends FormRegistrarMixin(InteractionStateMixin(superclass)) {
|
||||
static get properties() {
|
||||
return {
|
||||
|
|
@ -121,6 +120,7 @@ const ChoiceGroupMixinImplementation = superclass =>
|
|||
constructor() {
|
||||
super();
|
||||
this.multipleChoice = false;
|
||||
/** @type {'child'|'choice-group'|'fieldset'} */
|
||||
this._repropagationRole = 'choice-group'; // configures event propagation logic of FormControlMixin
|
||||
|
||||
this.__isInitialModelValue = true;
|
||||
|
|
|
|||
|
|
@ -50,11 +50,16 @@ const FormRegistrarMixinImplementation = superclass =>
|
|||
this._isFormOrFieldset = false;
|
||||
|
||||
this._onRequestToAddFormElement = this._onRequestToAddFormElement.bind(this);
|
||||
this._onRequestToChangeFormElementName = this._onRequestToChangeFormElementName.bind(this);
|
||||
|
||||
this.addEventListener(
|
||||
'form-element-register',
|
||||
/** @type {EventListenerOrEventListenerObject} */ (this._onRequestToAddFormElement),
|
||||
);
|
||||
this.addEventListener(
|
||||
'form-element-name-changed',
|
||||
/** @type {EventListenerOrEventListenerObject} */ (this._onRequestToChangeFormElementName),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -163,6 +168,17 @@ const FormRegistrarMixinImplementation = superclass =>
|
|||
this.addFormElement(child, indexToInsertAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CustomEvent} ev
|
||||
*/
|
||||
_onRequestToChangeFormElementName(ev) {
|
||||
const element = this.formElements[ev.detail.oldName];
|
||||
if (element) {
|
||||
this.formElements[ev.detail.newName] = element;
|
||||
delete this.formElements[ev.detail.oldName];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CustomEvent} ev
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -390,7 +390,7 @@ export function runValidateMixinFeedbackPart() {
|
|||
params: 4,
|
||||
modelValue: 'cat',
|
||||
formControl: el,
|
||||
fieldName: undefined,
|
||||
fieldName: '',
|
||||
type: 'x',
|
||||
name: 'MinLength',
|
||||
});
|
||||
|
|
@ -414,7 +414,7 @@ export function runValidateMixinFeedbackPart() {
|
|||
params: 4,
|
||||
modelValue: 'cat',
|
||||
formControl: el,
|
||||
fieldName: undefined,
|
||||
fieldName: '',
|
||||
type: 'error',
|
||||
name: 'MinLength',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,26 +18,27 @@ import { FormGroupMixin } from '../../src/form-group/FormGroupMixin.js';
|
|||
* @param {{ tagString?: string, childTagString?:string }} [cfg]
|
||||
*/
|
||||
export function runFormGroupMixinSuite(cfg = {}) {
|
||||
const FormChild = class extends LionField {
|
||||
class FormChild extends LionField {
|
||||
get slots() {
|
||||
return {
|
||||
...super.slots,
|
||||
input: () => document.createElement('input'),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const childTagString = cfg.childTagString || defineCE(FormChild);
|
||||
|
||||
// @ts-expect-error
|
||||
const FormGroup = class extends FormGroupMixin(LitElement) {
|
||||
// @ts-expect-error base constructors same return type
|
||||
class FormGroup extends FormGroupMixin(LitElement) {
|
||||
constructor() {
|
||||
super();
|
||||
/** @override from FormRegistrarMixin */
|
||||
this._isFormOrFieldset = true;
|
||||
/** @type {'child'|'choice-group'|'fieldset'} */
|
||||
this._repropagationRole = 'fieldset'; // configures FormControlMixin
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const tagString = cfg.tagString || defineCE(FormGroup);
|
||||
const tag = unsafeStatic(tagString);
|
||||
|
|
@ -804,6 +805,19 @@ export function runFormGroupMixinSuite(cfg = {}) {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the formElements keys when a name attribute changes', async () => {
|
||||
const fieldset = /** @type {FormGroup} */ (await fixture(html`
|
||||
<${tag}>
|
||||
<${childTag} name="foo" .modelValue=${'qux'}></${childTag}>
|
||||
</${tag}>
|
||||
`));
|
||||
expect(fieldset.serializedValue.foo).to.equal('qux');
|
||||
fieldset.formElements[0].name = 'bar';
|
||||
await fieldset.updateComplete;
|
||||
await fieldset.formElements[0].updateComplete;
|
||||
expect(fieldset.serializedValue.bar).to.equal('qux');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Reset', () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ declare interface HTMLElementWithValue extends HTMLElement {
|
|||
value: string;
|
||||
}
|
||||
|
||||
export class FormControlHost {
|
||||
export declare class FormControlHost {
|
||||
static get styles(): CSSResult | CSSResult[];
|
||||
/**
|
||||
* A Boolean attribute which, if present, indicates that the user should not be able to edit
|
||||
|
|
@ -129,7 +129,7 @@ export declare function FormControlImplementation<T extends Constructor<LitEleme
|
|||
superclass: T,
|
||||
): T &
|
||||
Constructor<FormControlHost> &
|
||||
FormControlHost &
|
||||
typeof FormControlHost &
|
||||
Constructor<FormRegisteringHost> &
|
||||
typeof FormRegisteringHost &
|
||||
Constructor<DisabledHost> &
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export declare class FormGroupHost {
|
|||
touched: boolean;
|
||||
dirty: boolean;
|
||||
submitted: boolean;
|
||||
serializedValue: string;
|
||||
serializedValue: { [key: string]: any };
|
||||
modelValue: { [x: string]: any };
|
||||
formattedValue: string;
|
||||
children: Array<HTMLElement & FormControlHost>;
|
||||
|
|
|
|||
Loading…
Reference in a new issue