Skip to content

Commit 6b5ab21

Browse files
Elliott Marquezcopybara-github
authored andcommitted
fix(select,textfield): native form validation shows error state
PiperOrigin-RevId: 578632986
1 parent 87dfee4 commit 6b5ab21

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

select/internal/select.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ export abstract class Select extends selectBaseClass {
254254
@query('#label') private readonly labelEl!: HTMLElement;
255255
@queryAssignedElements({slot: 'leading-icon', flatten: true})
256256
private readonly leadingIcons!: Element[];
257+
private isCheckingValidity = false;
258+
private isReportingValidity = false;
257259
private customValidationMessage = '';
258260

259261
/**
@@ -304,8 +306,11 @@ export abstract class Select extends selectBaseClass {
304306
* @return true if the select is valid, or false if not.
305307
*/
306308
checkValidity() {
309+
this.isCheckingValidity = true;
307310
this.syncValidity();
308-
return this[internals].checkValidity();
311+
const isValid = this[internals].checkValidity();
312+
this.isCheckingValidity = false;
313+
return isValid;
309314
}
310315

311316
/**
@@ -325,6 +330,7 @@ export abstract class Select extends selectBaseClass {
325330
* @return true if the select is valid, or false if not.
326331
*/
327332
reportValidity() {
333+
this.isReportingValidity = true;
328334
let invalidEvent: Event | undefined;
329335
this.addEventListener(
330336
'invalid',
@@ -335,6 +341,14 @@ export abstract class Select extends selectBaseClass {
335341
);
336342

337343
const valid = this.checkValidity();
344+
this.showErrorMessage(valid, invalidEvent);
345+
346+
this.isReportingValidity = false;
347+
348+
return valid;
349+
}
350+
351+
private showErrorMessage(valid: boolean, invalidEvent: Event | undefined) {
338352
if (invalidEvent?.defaultPrevented) {
339353
return valid;
340354
}
@@ -836,6 +850,27 @@ export abstract class Select extends selectBaseClass {
836850
return select.validationMessage;
837851
}
838852

853+
private readonly onInvalid = (invalidEvent: Event) => {
854+
if (this.isCheckingValidity || this.isReportingValidity) {
855+
return;
856+
}
857+
858+
this.showErrorMessage(false, invalidEvent);
859+
};
860+
861+
override connectedCallback() {
862+
super.connectedCallback();
863+
864+
// Handles the case where the user submits the form and native validation
865+
// error pops up. We want the error styles to show.
866+
this.addEventListener('invalid', this.onInvalid);
867+
}
868+
869+
override disconnectedCallback() {
870+
super.disconnectedCallback();
871+
this.removeEventListener('invalid', this.onInvalid);
872+
}
873+
839874
// Writable mixin properties for lit-html binding, needed for lit-analyzer
840875
declare disabled: boolean;
841876
declare name: string;

textfield/internal/text-field.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ export abstract class TextField extends textFieldBaseClass {
387387
private readonly leadingIcons!: Element[];
388388
@queryAssignedElements({slot: 'trailing-icon'})
389389
private readonly trailingIcons!: Element[];
390+
private isCheckingValidity = false;
391+
private isReportingValidity = false;
390392
// Needed for Safari, see https://bugs.webkit.org/show_bug.cgi?id=261432
391393
// Replace with this[internals].validity.customError when resolved.
392394
private hasCustomValidityError = false;
@@ -402,8 +404,11 @@ export abstract class TextField extends textFieldBaseClass {
402404
* @return true if the text field is valid, or false if not.
403405
*/
404406
checkValidity() {
407+
this.isCheckingValidity = true;
405408
this.syncValidity();
406-
return this[internals].checkValidity();
409+
const isValid = this[internals].checkValidity();
410+
this.isCheckingValidity = false;
411+
return isValid;
407412
}
408413

409414
/**
@@ -425,6 +430,7 @@ export abstract class TextField extends textFieldBaseClass {
425430
* @return true if the text field is valid, or false if not.
426431
*/
427432
reportValidity() {
433+
this.isReportingValidity = true;
428434
let invalidEvent: Event | undefined;
429435
this.addEventListener(
430436
'invalid',
@@ -435,6 +441,14 @@ export abstract class TextField extends textFieldBaseClass {
435441
);
436442

437443
const valid = this.checkValidity();
444+
this.showErrorMessage(valid, invalidEvent);
445+
446+
this.isReportingValidity = false;
447+
448+
return valid;
449+
}
450+
451+
private showErrorMessage(valid: boolean, invalidEvent: Event | undefined) {
438452
if (invalidEvent?.defaultPrevented) {
439453
return valid;
440454
}
@@ -825,6 +839,26 @@ export abstract class TextField extends textFieldBaseClass {
825839
this.hasTrailingIcon = this.trailingIcons.length > 0;
826840
}
827841

842+
private readonly onInvalid = (invalidEvent: Event) => {
843+
if (this.isCheckingValidity || this.isReportingValidity) {
844+
return;
845+
}
846+
847+
this.showErrorMessage(false, invalidEvent);
848+
};
849+
850+
override connectedCallback() {
851+
super.connectedCallback();
852+
853+
// Handles the case where the user submits the form and native validation
854+
// error pops up. We want the error styles to show.
855+
this.addEventListener('invalid', this.onInvalid);
856+
}
857+
858+
override disconnectedCallback() {
859+
super.disconnectedCallback();
860+
this.removeEventListener('invalid', this.onInvalid);
861+
}
828862
// Writable mixin properties for lit-html binding, needed for lit-analyzer
829863
declare disabled: boolean;
830864
declare name: string;

0 commit comments

Comments
 (0)