Skip to content

Commit 97d0d86

Browse files
committed
fix(material/autocomplete): mark control as touched once panel is closed
Currently we mark the autocomplete control as touched on `blur`. The problem is that the `blur` event happens a split second before the panel is closed which can cause the error styling to show up and disappear quickly which looks glitchy. These changes change it so that the control is marked as touched once the panel is closed. Also makes a couple of underscored properties private since they weren't used anywhere in the view. Fixes #18313.
1 parent 8ba7148 commit 97d0d86

File tree

3 files changed

+94
-11
lines changed

3 files changed

+94
-11
lines changed

src/material-experimental/mdc-autocomplete/autocomplete.spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,54 @@ describe('MDC-based MatAutocomplete', () => {
827827
.toBe(false, `Expected control to stay pristine if value is set programmatically.`);
828828
});
829829

830+
it('should mark the autocomplete control as touched when the panel is closed via the keyboard',
831+
fakeAsync(() => {
832+
fixture.componentInstance.trigger.openPanel();
833+
fixture.detectChanges();
834+
zone.simulateZoneExit();
835+
836+
expect(fixture.componentInstance.stateCtrl.touched)
837+
.toBe(false, `Expected control to start out untouched.`);
838+
839+
dispatchKeyboardEvent(input, 'keydown', TAB);
840+
fixture.detectChanges();
841+
842+
expect(fixture.componentInstance.stateCtrl.touched)
843+
.toBe(true, `Expected control to become touched on blur.`);
844+
}));
845+
846+
it('should mark the autocomplete control as touched when the panel is closed by clicking away',
847+
fakeAsync(() => {
848+
fixture.componentInstance.trigger.openPanel();
849+
fixture.detectChanges();
850+
zone.simulateZoneExit();
851+
852+
expect(fixture.componentInstance.stateCtrl.touched)
853+
.toBe(false, `Expected control to start out untouched.`);
854+
855+
dispatchFakeEvent(document, 'click');
856+
fixture.detectChanges();
857+
858+
expect(fixture.componentInstance.stateCtrl.touched)
859+
.toBe(true, `Expected control to become touched on blur.`);
860+
}));
861+
862+
it('should not mark the autocomplete control as touched when the panel is closed ' +
863+
'programmatically', fakeAsync(() => {
864+
fixture.componentInstance.trigger.openPanel();
865+
fixture.detectChanges();
866+
zone.simulateZoneExit();
867+
868+
expect(fixture.componentInstance.stateCtrl.touched)
869+
.toBe(false, `Expected control to start out untouched.`);
870+
871+
fixture.componentInstance.trigger.closePanel();
872+
fixture.detectChanges();
873+
874+
expect(fixture.componentInstance.stateCtrl.touched)
875+
.toBe(false, `Expected control to stay untouched.`);
876+
}));
877+
830878
it('should mark the autocomplete control as touched on blur', () => {
831879
fixture.componentInstance.trigger.openPanel();
832880
fixture.detectChanges();

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ export abstract class _MatAutocompleteTriggerBase implements ControlValueAccesso
545545
}
546546

547547
this.closePanel();
548+
this._onTouched();
548549
}
549550

550551
/**
@@ -772,7 +773,6 @@ export abstract class _MatAutocompleteTriggerBase implements ControlValueAccesso
772773
// Note: we use `focusin`, as opposed to `focus`, in order to open the panel
773774
// a little earlier. This avoids issues where IE delays the focusing of the input.
774775
'(focusin)': '_handleFocus()',
775-
'(blur)': '_onTouched()',
776776
'(input)': '_handleInput($event)',
777777
'(keydown)': '_handleKeydown($event)',
778778
},

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -822,18 +822,53 @@ describe('MatAutocomplete', () => {
822822
.toBe(false, `Expected control to stay pristine if value is set programmatically.`);
823823
});
824824

825-
it('should mark the autocomplete control as touched on blur', () => {
826-
fixture.componentInstance.trigger.openPanel();
827-
fixture.detectChanges();
828-
expect(fixture.componentInstance.stateCtrl.touched)
829-
.toBe(false, `Expected control to start out untouched.`);
825+
it('should mark the autocomplete control as touched when the panel is closed via the keyboard',
826+
fakeAsync(() => {
827+
fixture.componentInstance.trigger.openPanel();
828+
fixture.detectChanges();
829+
zone.simulateZoneExit();
830830

831-
dispatchFakeEvent(input, 'blur');
832-
fixture.detectChanges();
831+
expect(fixture.componentInstance.stateCtrl.touched)
832+
.toBe(false, `Expected control to start out untouched.`);
833833

834-
expect(fixture.componentInstance.stateCtrl.touched)
835-
.toBe(true, `Expected control to become touched on blur.`);
836-
});
834+
dispatchKeyboardEvent(input, 'keydown', TAB);
835+
fixture.detectChanges();
836+
837+
expect(fixture.componentInstance.stateCtrl.touched)
838+
.toBe(true, `Expected control to become touched on blur.`);
839+
}));
840+
841+
it('should mark the autocomplete control as touched when the panel is closed by clicking away',
842+
fakeAsync(() => {
843+
fixture.componentInstance.trigger.openPanel();
844+
fixture.detectChanges();
845+
zone.simulateZoneExit();
846+
847+
expect(fixture.componentInstance.stateCtrl.touched)
848+
.toBe(false, `Expected control to start out untouched.`);
849+
850+
dispatchFakeEvent(document, 'click');
851+
fixture.detectChanges();
852+
853+
expect(fixture.componentInstance.stateCtrl.touched)
854+
.toBe(true, `Expected control to become touched on blur.`);
855+
}));
856+
857+
it('should not mark the autocomplete control as touched when the panel is closed ' +
858+
'programmatically', fakeAsync(() => {
859+
fixture.componentInstance.trigger.openPanel();
860+
fixture.detectChanges();
861+
zone.simulateZoneExit();
862+
863+
expect(fixture.componentInstance.stateCtrl.touched)
864+
.toBe(false, `Expected control to start out untouched.`);
865+
866+
fixture.componentInstance.trigger.closePanel();
867+
fixture.detectChanges();
868+
869+
expect(fixture.componentInstance.stateCtrl.touched)
870+
.toBe(false, `Expected control to stay untouched.`);
871+
}));
837872

838873
it('should disable the input when used with a value accessor and without `matInput`', () => {
839874
overlayContainer.ngOnDestroy();

0 commit comments

Comments
 (0)