fix(validate-mixin): determine required or result validators based on characteristics (#2498)

* fix(validate-mixin): determine if a required validator or result validator has been registered based on characteristics

* chore(validate-mixin): create changeset
This commit is contained in:
Mete Abduraman 2025-05-08 09:42:12 +03:00 committed by GitHub
parent 6d7e6a6487
commit 07b089e80f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 127 additions and 7 deletions

View file

@ -0,0 +1,5 @@
---
'@lion/ui': patch
---
[validate-mixin] determine if a required validator or result validator has been registered based on characteristics

View file

@ -8,11 +8,11 @@ import { AsyncQueue } from '../utils/AsyncQueue.js';
import { pascalCase } from '../utils/pascalCase.js';
import { SyncUpdatableMixin } from '../utils/SyncUpdatableMixin.js';
import { LionValidationFeedback } from './LionValidationFeedback.js';
import { ResultValidator as MetaValidator } from './ResultValidator.js';
import { Unparseable } from './Unparseable.js';
import { Required } from './validators/Required.js';
import { FormControlMixin } from '../FormControlMixin.js';
// eslint-disable-next-line no-unused-vars
import { ResultValidator as MetaValidator } from './ResultValidator.js';
// eslint-disable-next-line no-unused-vars
import { Validator } from './Validator.js';
// TODO: [v1] make all @readOnly => @readonly and actually make sure those values cannot be set
@ -134,7 +134,7 @@ export const ValidateMixinImplementation = superclass =>
/**
* Combination of validators provided by Application Developer and the default validators
* @type {Validator[]}
* @type {(Validator | MetaValidator)[]}
* @protected
*/
get _allValidators() {
@ -480,9 +480,9 @@ export const ValidateMixinImplementation = superclass =>
const asyncValidators = /** @type {Validator[]} */ [];
for (const v of this._allValidators) {
if (v instanceof MetaValidator) {
metaValidators.push(v);
} else if (v instanceof Required) {
if (/** @type {MetaValidator} */ (v)?.executeOnResults) {
metaValidators.push(/** @type {MetaValidator} */ (v));
} else if (/** @type {typeof Validator} */ (v.constructor)?.validatorName === 'Required') {
// Required validator was already handled
} else if (/** @type {typeof Validator} */ (v.constructor).async) {
asyncValidators.push(v);

View file

@ -130,6 +130,121 @@ export function runValidateMixinSuite(customConfig) {
expect(el.hasFeedbackFor).to.deep.equal(['error']);
});
it('determines whether the "Required" validator was already handled by judging the validatorName', async () => {
class BundledValidator extends EventTarget {
static ['_$isValidator$'] = true;
static validatorName = '';
constructor() {
super();
this.type = 'error';
}
get config() {
// simplified version of the actual config
return {};
}
execute() {
// simplified version of the action execution
return true;
}
onFormControlConnect() {}
onFormControlDisconnect() {}
async _getMessage() {
// simplified version of the actual logic
return 'You need to enter something.';
}
}
class BundledRequired extends BundledValidator {
static get validatorName() {
return 'Required';
}
}
const el = /** @type {ValidateElement} */ (
await fixture(html`
<${tag}
.validators=${[new BundledRequired()]}
.modelValue=${'myValue'}
>${lightDom}</${tag}>
`)
);
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() {
return ['error', 'success'];
}
}
const elTagString = defineCE(ValidateElementWithSuccessType);
const elTag = unsafeStatic(elTagString);
class BundledValidator extends EventTarget {
static ['_$isValidator$'] = true;
static validatorName = '';
constructor() {
super();
this.type = 'error';
}
get config() {
// simplified version of the actual config
return {};
}
execute() {
// simplified version of the action execution
return true;
}
onFormControlConnect() {}
onFormControlDisconnect() {}
async _getMessage() {
// simplified version of the actual logic
return 'Success message.';
}
}
class BundledDefaultSuccess extends BundledValidator {
constructor() {
super();
this.type = 'success';
}
executeOnResults() {
return true;
}
}
const el = /** @type {ValidateElement} */ (
await fixture(html`
<${elTag}
.validators=${[new Required(), new BundledDefaultSuccess()]}
.modelValue=${'myValue'}
>${lightDom}</${elTag}>
`)
);
expect(el.hasFeedbackFor).to.deep.equal(['success']);
});
it('revalidates when ".modelValue" changes', async () => {
const el = /** @type {ValidateElement} */ (
await fixture(html`