Skip to content

Commit 932b4a0

Browse files
karammalerba
authored andcommitted
fix(autocomplete): aria-expanded should be updated when panel hides (#3494)
* fix(autocomplete): aria-expanded should be updated when panel hides * address comments
1 parent bcb16c6 commit 932b4a0

File tree

3 files changed

+80
-50
lines changed

3 files changed

+80
-50
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
114114

115115
/* Whether or not the autocomplete panel is open. */
116116
get panelOpen(): boolean {
117-
return this._panelOpen;
117+
return this._panelOpen && this.autocomplete.showPanel;
118118
}
119119

120120
/** Opens the autocomplete suggestion panel. */

src/lib/autocomplete/autocomplete.spec.ts

Lines changed: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {MdAutocomplete} from './autocomplete';
1515
import {MdInputContainer} from '../input/input-container';
1616
import {Observable} from 'rxjs/Observable';
1717
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
18+
import {typeInElement} from '../core/testing/type-in-element';
1819

1920
import 'rxjs/add/operator/map';
2021

@@ -66,35 +67,39 @@ describe('MdAutocomplete', () => {
6667
input = fixture.debugElement.query(By.css('input')).nativeElement;
6768
});
6869

69-
it('should open the panel when the input is focused', () => {
70+
it('should open the panel when the input is focused', async(() => {
7071
expect(fixture.componentInstance.trigger.panelOpen)
7172
.toBe(false, `Expected panel state to start out closed.`);
7273

7374
dispatchFakeEvent(input, 'focus');
74-
fixture.detectChanges();
75+
fixture.whenStable().then(() => {
76+
fixture.detectChanges();
7577

76-
expect(fixture.componentInstance.trigger.panelOpen)
77-
.toBe(true, `Expected panel state to read open when input is focused.`);
78-
expect(overlayContainerElement.textContent)
79-
.toContain('Alabama', `Expected panel to display when input is focused.`);
80-
expect(overlayContainerElement.textContent)
81-
.toContain('California', `Expected panel to display when input is focused.`);
82-
});
78+
expect(fixture.componentInstance.trigger.panelOpen)
79+
.toBe(true, `Expected panel state to read open when input is focused.`);
80+
expect(overlayContainerElement.textContent)
81+
.toContain('Alabama', `Expected panel to display when input is focused.`);
82+
expect(overlayContainerElement.textContent)
83+
.toContain('California', `Expected panel to display when input is focused.`);
84+
});
85+
}));
8386

84-
it('should open the panel programmatically', () => {
87+
it('should open the panel programmatically', async(() => {
8588
expect(fixture.componentInstance.trigger.panelOpen)
8689
.toBe(false, `Expected panel state to start out closed.`);
8790

8891
fixture.componentInstance.trigger.openPanel();
89-
fixture.detectChanges();
92+
fixture.whenStable().then(() => {
93+
fixture.detectChanges();
9094

91-
expect(fixture.componentInstance.trigger.panelOpen)
92-
.toBe(true, `Expected panel state to read open when opened programmatically.`);
93-
expect(overlayContainerElement.textContent)
94-
.toContain('Alabama', `Expected panel to display when opened programmatically.`);
95-
expect(overlayContainerElement.textContent)
96-
.toContain('California', `Expected panel to display when opened programmatically.`);
97-
});
95+
expect(fixture.componentInstance.trigger.panelOpen)
96+
.toBe(true, `Expected panel state to read open when opened programmatically.`);
97+
expect(overlayContainerElement.textContent)
98+
.toContain('Alabama', `Expected panel to display when opened programmatically.`);
99+
expect(overlayContainerElement.textContent)
100+
.toContain('California', `Expected panel to display when opened programmatically.`);
101+
});
102+
}));
98103

99104
it('should close the panel when blurred', async(() => {
100105
dispatchFakeEvent(input, 'focus');
@@ -190,8 +195,6 @@ describe('MdAutocomplete', () => {
190195
fixture.whenStable().then(() => {
191196
fixture.detectChanges();
192197

193-
expect(fixture.componentInstance.trigger.panelOpen)
194-
.toBe(true, `Expected panel to stay open when options list is empty.`);
195198
expect(panel.classList)
196199
.toContain('mat-autocomplete-hidden', `Expected panel to hide itself when empty.`);
197200
});
@@ -774,20 +777,43 @@ describe('MdAutocomplete', () => {
774777
.toBe('false', 'Expected aria-expanded to be false while panel is closed.');
775778

776779
fixture.componentInstance.trigger.openPanel();
777-
fixture.detectChanges();
780+
fixture.whenStable().then(() => {
781+
fixture.detectChanges();
778782

779-
expect(input.getAttribute('aria-expanded'))
780-
.toBe('true', 'Expected aria-expanded to be true while panel is open.');
783+
expect(input.getAttribute('aria-expanded'))
784+
.toBe('true', 'Expected aria-expanded to be true while panel is open.');
781785

782-
fixture.componentInstance.trigger.closePanel();
783-
fixture.detectChanges();
786+
fixture.componentInstance.trigger.closePanel();
787+
fixture.detectChanges();
784788

785-
fixture.whenStable().then(() => {
786-
expect(input.getAttribute('aria-expanded'))
787-
.toBe('false', 'Expected aria-expanded to be false when panel closes again.');
789+
fixture.whenStable().then(() => {
790+
expect(input.getAttribute('aria-expanded'))
791+
.toBe('false', 'Expected aria-expanded to be false when panel closes again.');
792+
});
788793
});
789794
}));
790795

796+
it('should set aria-expanded properly when the panel is hidden', async(() => {
797+
fixture.componentInstance.trigger.openPanel();
798+
799+
fixture.whenStable().then(() => {
800+
fixture.detectChanges();
801+
expect(input.getAttribute('aria-expanded'))
802+
.toBe('true', 'Expected aria-expanded to be true while panel is open.');
803+
804+
typeInElement('zz', input);
805+
fixture.whenStable().then(() => {
806+
fixture.detectChanges();
807+
808+
fixture.whenStable().then(() => {
809+
fixture.detectChanges();
810+
expect(input.getAttribute('aria-expanded'))
811+
.toBe('false', 'Expected aria-expanded to be false when panel hides itself.');
812+
});
813+
});
814+
});
815+
}));
816+
791817
it('should set aria-owns based on the attached autocomplete', () => {
792818
fixture.componentInstance.trigger.openPanel();
793819
fixture.detectChanges();
@@ -901,21 +927,24 @@ describe('MdAutocomplete', () => {
901927
});
902928
}));
903929

904-
it('should work when input is wrapped in ngIf', () => {
930+
it('should work when input is wrapped in ngIf', async(() => {
905931
const fixture = TestBed.createComponent(NgIfAutocomplete);
906932
fixture.detectChanges();
907933

908934
const input = fixture.debugElement.query(By.css('input')).nativeElement;
909935
dispatchFakeEvent(input, 'focus');
910-
fixture.detectChanges();
911936

912-
expect(fixture.componentInstance.trigger.panelOpen)
913-
.toBe(true, `Expected panel state to read open when input is focused.`);
914-
expect(overlayContainerElement.textContent)
915-
.toContain('One', `Expected panel to display when input is focused.`);
916-
expect(overlayContainerElement.textContent)
917-
.toContain('Two', `Expected panel to display when input is focused.`);
918-
});
937+
fixture.whenStable().then(() => {
938+
fixture.detectChanges();
939+
940+
expect(fixture.componentInstance.trigger.panelOpen)
941+
.toBe(true, `Expected panel state to read open when input is focused.`);
942+
expect(overlayContainerElement.textContent)
943+
.toContain('One', `Expected panel to display when input is focused.`);
944+
expect(overlayContainerElement.textContent)
945+
.toContain('Two', `Expected panel to display when input is focused.`);
946+
});
947+
}));
919948

920949
it('should filter properly with ngIf after setting the active item', fakeAsync(() => {
921950
const fixture = TestBed.createComponent(NgIfAutocomplete);
@@ -1086,18 +1115,6 @@ class AutocompleteWithNgModel {
10861115

10871116
}
10881117

1089-
/**
1090-
* Focuses an input, sets its value and dispatches
1091-
* the `input` event, simulating the user typing.
1092-
* @param value Value to be set on the input.
1093-
* @param element Element onto which to set the value.
1094-
*/
1095-
function typeInElement(value: string, element: HTMLInputElement, autoFocus = true) {
1096-
element.focus();
1097-
element.value = value;
1098-
dispatchFakeEvent(element, 'input');
1099-
}
1100-
11011118
/** This is a mock keyboard event to test keyboard events in the autocomplete. */
11021119
class MockKeyboardEvent {
11031120
constructor(public keyCode: number) {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {dispatchFakeEvent} from './dispatch-events';
2+
3+
/**
4+
* Focuses an input, sets its value and dispatches
5+
* the `input` event, simulating the user typing.
6+
* @param value Value to be set on the input.
7+
* @param element Element onto which to set the value.
8+
*/
9+
export function typeInElement(value: string, element: HTMLInputElement, autoFocus = true) {
10+
element.focus();
11+
element.value = value;
12+
dispatchFakeEvent(element, 'input');
13+
}

0 commit comments

Comments
 (0)