diff --git a/.changeset/soft-suits-swim.md b/.changeset/soft-suits-swim.md new file mode 100644 index 000000000..7fe95af0a --- /dev/null +++ b/.changeset/soft-suits-swim.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[form-core] make sure renamed Required validator are identified as such, while remaining compatible with multiple versions diff --git a/packages/ui/components/form-core/src/validate/ValidateMixin.js b/packages/ui/components/form-core/src/validate/ValidateMixin.js index 0e595596e..ae5ed857f 100644 --- a/packages/ui/components/form-core/src/validate/ValidateMixin.js +++ b/packages/ui/components/form-core/src/validate/ValidateMixin.js @@ -420,6 +420,22 @@ export const ValidateMixinImplementation = superclass => await this.__executeValidators(); } + /** + * @param {Validator} validator + * @returns {boolean} + */ + #isInstanceOfRequiredValidator(validator) { + // walk prototype and check if validatorName is 'Required' + let validatorLvlInPrototype = validator; + while (validatorLvlInPrototype) { + const ctor = /** @type {typeof Validator} */ (validatorLvlInPrototype.constructor); + if (ctor.validatorName === 'Required') return true; + + validatorLvlInPrototype = Object.getPrototypeOf(validatorLvlInPrototype); + } + return false; + } + /** * @desc step a1-3 + b (as explained in `validate()`) */ @@ -482,7 +498,7 @@ export const ValidateMixinImplementation = superclass => for (const v of this._allValidators) { if (/** @type {MetaValidator} */ (v)?.executeOnResults) { metaValidators.push(/** @type {MetaValidator} */ (v)); - } else if (/** @type {typeof Validator} */ (v.constructor)?.validatorName === 'Required') { + } else if (this.#isInstanceOfRequiredValidator(v)) { // Required validator was already handled } else if (/** @type {typeof Validator} */ (v.constructor).async) { asyncValidators.push(v); diff --git a/packages/ui/components/form-core/test-suites/ValidateMixin.suite.js b/packages/ui/components/form-core/test-suites/ValidateMixin.suite.js index af8914abd..f65db5128 100644 --- a/packages/ui/components/form-core/test-suites/ValidateMixin.suite.js +++ b/packages/ui/components/form-core/test-suites/ValidateMixin.suite.js @@ -130,6 +130,7 @@ export function runValidateMixinSuite(customConfig) { expect(el.hasFeedbackFor).to.deep.equal(['error']); }); + // TODO: keep this use case for backwards compatibility or just required extending Required? it('determines whether the "Required" validator was already handled by judging the validatorName', async () => { class BundledValidator extends EventTarget { static ['_$isValidator$'] = true; @@ -163,9 +164,7 @@ export function runValidateMixinSuite(customConfig) { } class BundledRequired extends BundledValidator { - static get validatorName() { - return 'Required'; - } + static validatorName = 'Required'; } const el = /** @type {ValidateElement} */ ( @@ -180,6 +179,23 @@ export function runValidateMixinSuite(customConfig) { expect(el.hasFeedbackFor).to.deep.equal([]); }); + it('determines whether the "Required" validator was already handled by judging the validatorName', async () => { + class MyRequired extends Required { + static validatorName = 'SomethingOtherThanRequired'; + } + + const el = /** @type {ValidateElement} */ ( + await fixture(html` + <${tag} + .validators=${[new MyRequired()]} + .modelValue=${'myValue'} + >${lightDom} + `) + ); + + expect(el.hasFeedbackFor).to.deep.equal([]); + }); + it('determines whether the passed Validators are ResultValidators judging by the presence of "executeOnResults"', async () => { class ValidateElementWithSuccessType extends ValidateElement { static get validationTypes() {