Skip to content

Commit

Permalink
fix(select,textfield): native form validation shows error state
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 575349644
  • Loading branch information
Elliott Marquez authored and copybara-github committed Oct 20, 2023
1 parent b7be1cb commit 76c1ff2
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 3 deletions.
34 changes: 32 additions & 2 deletions select/internal/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ export abstract class Select extends LitElement {
@query('#label') private readonly labelEl!: HTMLElement;
@queryAssignedElements({slot: 'leading-icon', flatten: true})
private readonly leadingIcons!: Element[];
private isCheckingValidity = false;
private isReportingValidity = false;
private customValidationMessage = '';
private readonly internals =
(this as HTMLElement /* needed for closure */).attachInternals();
Expand Down Expand Up @@ -308,8 +310,11 @@ export abstract class Select extends LitElement {
* @return true if the select is valid, or false if not.
*/
checkValidity() {
this.isCheckingValidity = true;
this.syncValidity();
return this.internals.checkValidity();
const isValid = this.internals.checkValidity();
this.isCheckingValidity = false;
return isValid;
}

/**
Expand All @@ -329,12 +334,21 @@ export abstract class Select extends LitElement {
* @return true if the select is valid, or false if not.
*/
reportValidity() {
this.isReportingValidity = true;
let invalidEvent: Event|undefined;
this.addEventListener('invalid', event => {
invalidEvent = event;
}, {once: true});

const valid = this.checkValidity();
this.showErrorMessage(valid, invalidEvent);

this.isReportingValidity = false;

return valid;
}

private showErrorMessage(valid: boolean, invalidEvent: Event|undefined) {
if (invalidEvent?.defaultPrevented) {
return valid;
}
Expand Down Expand Up @@ -801,7 +815,9 @@ export abstract class Select extends LitElement {
const validationMessage = this.customValidationMessage ||
valueMissing && this.getRequiredValidationMessage() || '';

this.internals.setValidity({valueMissing, customError}, validationMessage);
this.internals.setValidity(
{valueMissing, customError}, validationMessage,
this.field ?? undefined);
}

// Returns the platform `<select>` validation message for i18n.
Expand All @@ -811,6 +827,20 @@ export abstract class Select extends LitElement {
return select.validationMessage;
}

override connectedCallback() {
super.connectedCallback();

// Handles the case where the user submits the form and native validation
// error pops up. We want the error styles to show.
this.addEventListener('invalid', (invalidEvent: Event) => {
if (this.isCheckingValidity || this.isReportingValidity) {
return;
}

this.showErrorMessage(false, invalidEvent);
});
}

/** @private */
formResetCallback() {
this.reset();
Expand Down
30 changes: 29 additions & 1 deletion textfield/internal/text-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ export abstract class TextField extends LitElement {
private readonly leadingIcons!: Element[];
@queryAssignedElements({slot: 'trailing-icon'})
private readonly trailingIcons!: Element[];
private isCheckingValidity = false;
private isReportingValidity = false;
// Needed for Safari, see https://bugs.webkit.org/show_bug.cgi?id=261432
// Replace with this.internals.validity.customError when resolved.
private hasCustomValidityError = false;
Expand All @@ -372,8 +374,11 @@ export abstract class TextField extends LitElement {
* @return true if the text field is valid, or false if not.
*/
checkValidity() {
this.isCheckingValidity = true;
this.syncValidity();
return this.internals.checkValidity();
const isValid = this.internals.checkValidity();
this.isCheckingValidity = false;
return isValid;
}

/**
Expand All @@ -395,12 +400,21 @@ export abstract class TextField extends LitElement {
* @return true if the text field is valid, or false if not.
*/
reportValidity() {
this.isReportingValidity = true;
let invalidEvent: Event|undefined;
this.addEventListener('invalid', event => {
invalidEvent = event;
}, {once: true});

const valid = this.checkValidity();
this.showErrorMessage(valid, invalidEvent);

this.isReportingValidity = false;

return valid;
}

private showErrorMessage(valid: boolean, invalidEvent: Event|undefined) {
if (invalidEvent?.defaultPrevented) {
return valid;
}
Expand Down Expand Up @@ -778,6 +792,20 @@ export abstract class TextField extends LitElement {
this.hasTrailingIcon = this.trailingIcons.length > 0;
}

override connectedCallback() {
super.connectedCallback();

// Handles the case where the user submits the form and native validation
// error pops up. We want the error styles to show.
this.addEventListener('invalid', (invalidEvent: Event) => {
if (this.isCheckingValidity || this.isReportingValidity) {
return;
}

this.showErrorMessage(false, invalidEvent);
});
}

/** @private */
formResetCallback() {
this.reset();
Expand Down

0 comments on commit 76c1ff2

Please sign in to comment.