From f43e3e8962bf3524ef3f3fef95f66fc04fef572e Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Sun, 26 Jan 2020 22:42:51 +0100 Subject: [PATCH] fix(material-experimental/mdc-form-field): ensure validity styling is not reset by foundation (#18266) Currently, whenever the abstract form-field control turns invalid, the text-field foundation does not know about the validity change. Since it refreshes the validity upon focus/blur, it could end up accidentally removing the invalid class. We fix this, by ensuring that the foundation shares the validity state with the one from the form-field control. --- .../mdc-form-field/form-field.ts | 6 ++++++ src/material-experimental/mdc-input/input.spec.ts | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/material-experimental/mdc-form-field/form-field.ts b/src/material-experimental/mdc-form-field/form-field.ts index 39a3de844f73..b370d7ad1b66 100644 --- a/src/material-experimental/mdc-form-field/form-field.ts +++ b/src/material-experimental/mdc-form-field/form-field.ts @@ -283,6 +283,12 @@ export class MatFormField implements AfterViewInit, OnDestroy, AfterContentCheck get: () => this._shouldLabelFloat(), }); + // By default, the foundation determines the validity of the text-field from the + // specified native input. Since we don't pass a native input to the foundation because + // abstract form controls are not necessarily consisting of an input, we handle the + // text-field validity through the abstract form-field control state. + this._foundation.isValid = () => !this._control.errorState; + // Initial focus state sync. This happens rarely, but we want to account for // it in case the form-field control has "focused" set to true on init. this._updateFocusState(); diff --git a/src/material-experimental/mdc-input/input.spec.ts b/src/material-experimental/mdc-input/input.spec.ts index 37c75f4ea473..fa355b0d65b2 100644 --- a/src/material-experimental/mdc-input/input.spec.ts +++ b/src/material-experimental/mdc-input/input.spec.ts @@ -830,6 +830,21 @@ describe('MatMdcInput with forms', () => { .toBe('true', 'Expected aria-invalid to be set to "true".'); })); + it('should not reset text-field validity if focus changes for an invalid input', + fakeAsync(() => { + // Mark the control as touched, so that the form-field displays as invalid. + testComponent.formControl.markAsTouched(); + fixture.detectChanges(); + flush(); + + const wrapperEl = containerEl.querySelector('.mdc-text-field')!; + expect(wrapperEl.classList).toContain('mdc-text-field--invalid'); + + dispatchFakeEvent(inputEl, 'focus'); + dispatchFakeEvent(inputEl, 'blur'); + expect(wrapperEl.classList).toContain('mdc-text-field--invalid'); + })); + it('should display an error message when the parent form is submitted', fakeAsync(() => { expect(testComponent.form.submitted).toBe(false, 'Expected form not to have been submitted'); expect(testComponent.formControl.invalid).toBe(true, 'Expected form control to be invalid');