Skip to content

Commit 2c7ce85

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 8a12da7 commit 2c7ce85

File tree

4 files changed

+101
-14
lines changed

4 files changed

+101
-14
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ export const MAT_AUTOCOMPLETE_VALUE_ACCESSOR: any = {
3838
// Note: we use `focusin`, as opposed to `focus`, in order to open the panel
3939
// a little earlier. This avoids issues where IE delays the focusing of the input.
4040
'(focusin)': '_handleFocus()',
41-
'(blur)': '_onTouched()',
4241
'(input)': '_handleInput($event)',
4342
'(keydown)': '_handleKeydown($event)',
4443
},

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

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -891,20 +891,66 @@ describe('MDC-based MatAutocomplete', () => {
891891
.toBe(false);
892892
});
893893

894-
it('should mark the autocomplete control as touched on blur', () => {
894+
it('should mark the autocomplete control as touched when the panel is closed via the keyboard', fakeAsync(() => {
895895
fixture.componentInstance.trigger.openPanel();
896896
fixture.detectChanges();
897-
expect(fixture.componentInstance.stateCtrl.touched)
898-
.withContext(`Expected control to start out untouched.`)
899-
.toBe(false);
897+
zone.simulateZoneExit();
900898

901-
dispatchFakeEvent(input, 'blur');
899+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
900+
false,
901+
`Expected control to start out untouched.`,
902+
);
903+
904+
dispatchKeyboardEvent(input, 'keydown', TAB);
902905
fixture.detectChanges();
903906

904-
expect(fixture.componentInstance.stateCtrl.touched)
905-
.withContext(`Expected control to become touched on blur.`)
906-
.toBe(true);
907-
});
907+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
908+
true,
909+
`Expected control to become touched on blur.`,
910+
);
911+
}));
912+
913+
it('should mark the autocomplete control as touched when the panel is closed by clicking away', fakeAsync(() => {
914+
fixture.componentInstance.trigger.openPanel();
915+
fixture.detectChanges();
916+
zone.simulateZoneExit();
917+
918+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
919+
false,
920+
`Expected control to start out untouched.`,
921+
);
922+
923+
dispatchFakeEvent(document, 'click');
924+
fixture.detectChanges();
925+
926+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
927+
true,
928+
`Expected control to become touched on blur.`,
929+
);
930+
}));
931+
932+
it(
933+
'should not mark the autocomplete control as touched when the panel is closed ' +
934+
'programmatically',
935+
fakeAsync(() => {
936+
fixture.componentInstance.trigger.openPanel();
937+
fixture.detectChanges();
938+
zone.simulateZoneExit();
939+
940+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
941+
false,
942+
`Expected control to start out untouched.`,
943+
);
944+
945+
fixture.componentInstance.trigger.closePanel();
946+
fixture.detectChanges();
947+
948+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
949+
false,
950+
`Expected control to stay untouched.`,
951+
);
952+
}),
953+
);
908954

909955
it('should disable the input when used with a value accessor and without `matInput`', () => {
910956
fixture.destroy();

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,7 @@ export abstract class _MatAutocompleteTriggerBase
573573
}
574574

575575
this.closePanel();
576+
this._onTouched();
576577
}
577578

578579
/**
@@ -797,7 +798,6 @@ export abstract class _MatAutocompleteTriggerBase
797798
// Note: we use `focusin`, as opposed to `focus`, in order to open the panel
798799
// a little earlier. This avoids issues where IE delays the focusing of the input.
799800
'(focusin)': '_handleFocus()',
800-
'(blur)': '_onTouched()',
801801
'(input)': '_handleInput($event)',
802802
'(keydown)': '_handleKeydown($event)',
803803
},

src/material/autocomplete/autocomplete.spec.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -886,20 +886,62 @@ describe('MatAutocomplete', () => {
886886
.toBe(false);
887887
});
888888

889-
it('should mark the autocomplete control as touched on blur', () => {
889+
it('should mark the autocomplete control as touched when the panel is closed via the keyboard', fakeAsync(() => {
890890
fixture.componentInstance.trigger.openPanel();
891891
fixture.detectChanges();
892+
zone.simulateZoneExit();
893+
892894
expect(fixture.componentInstance.stateCtrl.touched)
893895
.withContext(`Expected control to start out untouched.`)
894896
.toBe(false);
895897

896-
dispatchFakeEvent(input, 'blur');
898+
dispatchKeyboardEvent(input, 'keydown', TAB);
897899
fixture.detectChanges();
898900

899901
expect(fixture.componentInstance.stateCtrl.touched)
900902
.withContext(`Expected control to become touched on blur.`)
901903
.toBe(true);
902-
});
904+
}));
905+
906+
it('should mark the autocomplete control as touched when the panel is closed by clicking away', fakeAsync(() => {
907+
fixture.componentInstance.trigger.openPanel();
908+
fixture.detectChanges();
909+
zone.simulateZoneExit();
910+
911+
expect(fixture.componentInstance.stateCtrl.touched)
912+
.withContext(`Expected control to start out untouched.`)
913+
.toBe(false);
914+
915+
dispatchFakeEvent(document, 'click');
916+
fixture.detectChanges();
917+
918+
expect(fixture.componentInstance.stateCtrl.touched)
919+
.withContext(`Expected control to become touched on blur.`)
920+
.toBe(true);
921+
}));
922+
923+
it(
924+
'should not mark the autocomplete control as touched when the panel is closed ' +
925+
'programmatically',
926+
fakeAsync(() => {
927+
fixture.componentInstance.trigger.openPanel();
928+
fixture.detectChanges();
929+
zone.simulateZoneExit();
930+
931+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
932+
false,
933+
`Expected control to start out untouched.`,
934+
);
935+
936+
fixture.componentInstance.trigger.closePanel();
937+
fixture.detectChanges();
938+
939+
expect(fixture.componentInstance.stateCtrl.touched).toBe(
940+
false,
941+
`Expected control to stay untouched.`,
942+
);
943+
}),
944+
);
903945

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

0 commit comments

Comments
 (0)