Skip to content

Commit ae42d1f

Browse files
authored
Fix: Prioritize component validation over native validation (#885)
1 parent 052b277 commit ae42d1f

File tree

3 files changed

+54
-21
lines changed

3 files changed

+54
-21
lines changed

packages/uui-base/lib/mixins/FormControlMixin.ts

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,25 @@ type FlagTypes =
2424
| 'badInput'
2525
| 'valid';
2626

27+
const WeightedErrorFlagTypes = [
28+
'customError',
29+
'valueMissing',
30+
'badInput',
31+
'typeMismatch',
32+
'patternMismatch',
33+
'rangeOverflow',
34+
'rangeUnderflow',
35+
'stepMismatch',
36+
'tooLong',
37+
'tooShort',
38+
];
39+
2740
// Acceptable as an internal interface/type, BUT if exposed externally this should be turned into a public class in a separate file.
2841
interface UUIFormControlValidatorConfig {
2942
flagKey: FlagTypes;
3043
getMessageMethod: () => string;
3144
checkMethod: () => boolean;
45+
weight: number;
3246
}
3347

3448
export interface UUIFormControlMixinInterface<ValueType> extends LitElement {
@@ -294,13 +308,18 @@ export const UUIFormControlMixin = <
294308
getMessageMethod: () => string,
295309
checkMethod: () => boolean,
296310
): UUIFormControlValidatorConfig {
297-
const obj = {
311+
const validator = {
298312
flagKey: flagKey,
299313
getMessageMethod: getMessageMethod,
300314
checkMethod: checkMethod,
315+
weight: WeightedErrorFlagTypes.indexOf(flagKey),
301316
};
302-
this.#validators.push(obj);
303-
return obj;
317+
this.#validators.push(validator);
318+
// Sort validators based on the WeightedErrorFlagTypes order. [NL]
319+
this.#validators.sort((a, b) =>
320+
a.weight > b.weight ? 1 : b.weight > a.weight ? -1 : 0,
321+
);
322+
return validator;
304323
}
305324

306325
protected removeValidator(validator: UUIFormControlValidatorConfig) {
@@ -365,29 +384,38 @@ export const UUIFormControlMixin = <
365384
*/
366385
protected _runValidators() {
367386
this.#validity = {};
368-
const messages: Set<string> = new Set();
387+
//const messages: Set<string> = new Set();
388+
let message: string | undefined = undefined;
369389
let innerFormControlEl: NativeFormControlElement | undefined = undefined;
370390

371-
// Loop through inner native form controls to adapt their validityState. [NL]
372-
this.#formCtrlElements.forEach(formCtrlEl => {
373-
let key: keyof ValidityState;
374-
for (key in formCtrlEl.validity) {
375-
if (key !== 'valid' && formCtrlEl.validity[key]) {
376-
this.#validity[key] = true;
377-
messages.add(formCtrlEl.validationMessage);
378-
innerFormControlEl ??= formCtrlEl;
379-
}
380-
}
381-
});
382-
383391
// Loop through custom validators, currently its intentional to have them overwritten native validity. but might need to be reconsidered (This current way enables to overwrite with custom messages) [NL]
384-
this.#validators.forEach(validator => {
392+
this.#validators.some(validator => {
385393
if (validator.checkMethod()) {
386394
this.#validity[validator.flagKey] = true;
387-
messages.add(validator.getMessageMethod());
395+
//messages.add(validator.getMessageMethod());
396+
message = validator.getMessageMethod();
397+
return true;
388398
}
399+
return false;
389400
});
390401

402+
if (!message) {
403+
// Loop through inner native form controls to adapt their validityState. [NL]
404+
this.#formCtrlElements.some(formCtrlEl => {
405+
let key: keyof ValidityState;
406+
for (key in formCtrlEl.validity) {
407+
if (key !== 'valid' && formCtrlEl.validity[key]) {
408+
this.#validity[key] = true;
409+
//messages.add(formCtrlEl.validationMessage);
410+
message = formCtrlEl.validationMessage;
411+
innerFormControlEl ??= formCtrlEl;
412+
return true;
413+
}
414+
}
415+
return false;
416+
});
417+
}
418+
391419
const hasError = Object.values(this.#validity).includes(true);
392420

393421
// https://developer.mozilla.org/en-US/docs/Web/API/ValidityState#valid
@@ -397,7 +425,8 @@ export const UUIFormControlMixin = <
397425
this._internals.setValidity(
398426
this.#validity,
399427
// Turn messages into an array and join them with a comma. [NL]:
400-
[...messages].join(', '),
428+
//[...messages].join(', '),
429+
message,
401430
innerFormControlEl ?? this.getFormElement() ?? undefined,
402431
);
403432

packages/uui-boolean-input/lib/uui-boolean-input.element.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ export abstract class UUIBooleanInputElement extends UUIFormControlMixin(
252252
cursor: not-allowed;
253253
opacity: 0.5;
254254
}
255+
256+
.label {
257+
display: block;
258+
}
255259
`,
256260
];
257261
}

packages/uui-form-validation-message/lib/uui-form-validation-message.element.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ export class UUIFormValidationMessageElement extends LitElement {
8080
} else {
8181
this._messages.delete(ctrl);
8282
}
83-
this.requestUpdate();
83+
this.requestUpdate('_messages');
8484
};
8585

8686
private _onControlValid = (e: UUIFormControlEvent) => {
8787
const ctrl = (e as any).composedPath()[0];
8888
this._messages.delete(ctrl);
89-
this.requestUpdate();
89+
this.requestUpdate('_messages');
9090
};
9191

9292
render() {

0 commit comments

Comments
 (0)