fix(autocomplete): highlighted option not reset when closed with escape or enter key (#6403)

Fixes the autocomplete active option not being reset when closing through the escape or enter keys. Previously the old active option would remain active upon reopening, however the user's focus would shift back to the top as soon as they press an arrow key.

Fixes #6258.
This commit is contained in:
Kristiyan Kostadinov
2017-08-15 23:50:03 +02:00
committed by Andrew Seguin
parent ea17d3d0f2
commit bf59468b01
2 changed files with 79 additions and 7 deletions
+5 -3
View File
@@ -292,18 +292,20 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
_handleKeydown(event: KeyboardEvent): void {
if (event.keyCode === ESCAPE && this.panelOpen) {
this._resetActiveItem();
this.closePanel();
event.stopPropagation();
} else if (this.activeOption && event.keyCode === ENTER && this.panelOpen) {
this.activeOption._selectViaInteraction();
this._resetActiveItem();
event.preventDefault();
} else {
const prevActiveItem = this.autocomplete._keyManager.activeItem;
const isArrowKey = event.keyCode === UP_ARROW || event.keyCode === DOWN_ARROW;
this.autocomplete._keyManager.onKeydown(event);
if (isArrowKey) {
if (this.panelOpen) {
this.autocomplete._keyManager.onKeydown(event);
} else if (isArrowKey) {
this.openPanel();
}
+74 -4
View File
@@ -640,6 +640,13 @@ describe('MdAutocomplete', () => {
tick();
fixture.detectChanges();
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, 'Expected first down press to open the pane.');
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
tick();
fixture.detectChanges();
expect(fixture.componentInstance.trigger.activeOption)
.toBe(fixture.componentInstance.options.first, 'Expected first option to be active.');
expect(optionEls[0].classList).toContain('mat-active');
@@ -665,6 +672,13 @@ describe('MdAutocomplete', () => {
tick();
fixture.detectChanges();
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, 'Expected first up press to open the pane.');
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
tick();
fixture.detectChanges();
expect(fixture.componentInstance.trigger.activeOption)
.toBe(fixture.componentInstance.options.last, 'Expected last option to be active.');
expect(optionEls[10].classList).toContain('mat-active');
@@ -686,6 +700,8 @@ describe('MdAutocomplete', () => {
fixture.detectChanges();
typeInElement('o', input);
fixture.detectChanges();
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
tick();
fixture.detectChanges();
@@ -812,7 +828,7 @@ describe('MdAutocomplete', () => {
expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to scroll.`);
// These down arrows will set the 6th option active, below the fold.
[1, 2, 3, 4, 5].forEach(() => {
[1, 2, 3, 4, 5, 6].forEach(() => {
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
tick();
});
@@ -848,7 +864,7 @@ describe('MdAutocomplete', () => {
expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to scroll.`);
// These down arrows will set the 6th option active, below the fold.
[1, 2, 3, 4, 5].forEach(() => {
[1, 2, 3, 4, 5, 6].forEach(() => {
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
tick();
});
@@ -879,12 +895,12 @@ describe('MdAutocomplete', () => {
expect(scrollContainer.scrollTop).toEqual(0, `Expected panel not to scroll.`);
// These down arrows will set the 7th option active, below the fold.
[1, 2, 3, 4, 5, 6].forEach(() => {
[1, 2, 3, 4, 5, 6, 7].forEach(() => {
fixture.componentInstance.trigger._handleKeydown(DOWN_ARROW_EVENT);
tick();
});
// These up arrows will set the 2nd option active
// These up arrows will set the 2nd option as active
[5, 4, 3, 2, 1].forEach(() => {
fixture.componentInstance.trigger._handleKeydown(UP_ARROW_EVENT);
tick();
@@ -914,6 +930,60 @@ describe('MdAutocomplete', () => {
});
}));
it('should reset the active option when closing with the escape key', fakeAsync(() => {
const trigger = fixture.componentInstance.trigger;
trigger.openPanel();
fixture.detectChanges();
tick();
expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');
expect(!!trigger.activeOption).toBe(false, 'Expected no active option.');
// Press the down arrow a few times.
[1, 2, 3].forEach(() => {
trigger._handleKeydown(DOWN_ARROW_EVENT);
tick();
fixture.detectChanges();
});
// Note that this casts to a boolean, in order to prevent Jasmine
// from crashing when trying to stringify the option if the test fails.
expect(!!trigger.activeOption).toBe(true, 'Expected to find an active option.');
trigger._handleKeydown(createKeyboardEvent('keydown', ESCAPE));
tick();
expect(!!trigger.activeOption).toBe(false, 'Expected no active options.');
}));
it('should reset the active option when closing by selecting with enter', fakeAsync(() => {
const trigger = fixture.componentInstance.trigger;
trigger.openPanel();
fixture.detectChanges();
tick();
expect(trigger.panelOpen).toBe(true, 'Expected panel to be open.');
expect(!!trigger.activeOption).toBe(false, 'Expected no active option.');
// Press the down arrow a few times.
[1, 2, 3].forEach(() => {
trigger._handleKeydown(DOWN_ARROW_EVENT);
tick();
fixture.detectChanges();
});
// Note that this casts to a boolean, in order to prevent Jasmine
// from crashing when trying to stringify the option if the test fails.
expect(!!trigger.activeOption).toBe(true, 'Expected to find an active option.');
trigger._handleKeydown(ENTER_EVENT);
tick();
expect(!!trigger.activeOption).toBe(false, 'Expected no active options.');
}));
});
describe('aria', () => {