Skip to content

Commit

Permalink
fix(autocomplete): aria-expanded should be updated when panel hides (#…
Browse files Browse the repository at this point in the history
…3494)

* fix(autocomplete): aria-expanded should be updated when panel hides

* address comments
  • Loading branch information
kara authored and mmalerba committed Mar 13, 2017
1 parent bcb16c6 commit 932b4a0
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 50 deletions.
2 changes: 1 addition & 1 deletion src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {

/* Whether or not the autocomplete panel is open. */
get panelOpen(): boolean {
return this._panelOpen;
return this._panelOpen && this.autocomplete.showPanel;
}

/** Opens the autocomplete suggestion panel. */
Expand Down
115 changes: 66 additions & 49 deletions src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {MdAutocomplete} from './autocomplete';
import {MdInputContainer} from '../input/input-container';
import {Observable} from 'rxjs/Observable';
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
import {typeInElement} from '../core/testing/type-in-element';

import 'rxjs/add/operator/map';

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

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

dispatchFakeEvent(input, 'focus');
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();

expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, `Expected panel state to read open when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('Alabama', `Expected panel to display when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('California', `Expected panel to display when input is focused.`);
});
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, `Expected panel state to read open when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('Alabama', `Expected panel to display when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('California', `Expected panel to display when input is focused.`);
});
}));

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

fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();

expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, `Expected panel state to read open when opened programmatically.`);
expect(overlayContainerElement.textContent)
.toContain('Alabama', `Expected panel to display when opened programmatically.`);
expect(overlayContainerElement.textContent)
.toContain('California', `Expected panel to display when opened programmatically.`);
});
expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, `Expected panel state to read open when opened programmatically.`);
expect(overlayContainerElement.textContent)
.toContain('Alabama', `Expected panel to display when opened programmatically.`);
expect(overlayContainerElement.textContent)
.toContain('California', `Expected panel to display when opened programmatically.`);
});
}));

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

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

fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();

expect(input.getAttribute('aria-expanded'))
.toBe('true', 'Expected aria-expanded to be true while panel is open.');
expect(input.getAttribute('aria-expanded'))
.toBe('true', 'Expected aria-expanded to be true while panel is open.');

fixture.componentInstance.trigger.closePanel();
fixture.detectChanges();
fixture.componentInstance.trigger.closePanel();
fixture.detectChanges();

fixture.whenStable().then(() => {
expect(input.getAttribute('aria-expanded'))
.toBe('false', 'Expected aria-expanded to be false when panel closes again.');
fixture.whenStable().then(() => {
expect(input.getAttribute('aria-expanded'))
.toBe('false', 'Expected aria-expanded to be false when panel closes again.');
});
});
}));

it('should set aria-expanded properly when the panel is hidden', async(() => {
fixture.componentInstance.trigger.openPanel();

fixture.whenStable().then(() => {
fixture.detectChanges();
expect(input.getAttribute('aria-expanded'))
.toBe('true', 'Expected aria-expanded to be true while panel is open.');

typeInElement('zz', input);
fixture.whenStable().then(() => {
fixture.detectChanges();

fixture.whenStable().then(() => {
fixture.detectChanges();
expect(input.getAttribute('aria-expanded'))
.toBe('false', 'Expected aria-expanded to be false when panel hides itself.');
});
});
});
}));

it('should set aria-owns based on the attached autocomplete', () => {
fixture.componentInstance.trigger.openPanel();
fixture.detectChanges();
Expand Down Expand Up @@ -901,21 +927,24 @@ describe('MdAutocomplete', () => {
});
}));

it('should work when input is wrapped in ngIf', () => {
it('should work when input is wrapped in ngIf', async(() => {
const fixture = TestBed.createComponent(NgIfAutocomplete);
fixture.detectChanges();

const input = fixture.debugElement.query(By.css('input')).nativeElement;
dispatchFakeEvent(input, 'focus');
fixture.detectChanges();

expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, `Expected panel state to read open when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('One', `Expected panel to display when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('Two', `Expected panel to display when input is focused.`);
});
fixture.whenStable().then(() => {
fixture.detectChanges();

expect(fixture.componentInstance.trigger.panelOpen)
.toBe(true, `Expected panel state to read open when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('One', `Expected panel to display when input is focused.`);
expect(overlayContainerElement.textContent)
.toContain('Two', `Expected panel to display when input is focused.`);
});
}));

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

}

/**
* Focuses an input, sets its value and dispatches
* the `input` event, simulating the user typing.
* @param value Value to be set on the input.
* @param element Element onto which to set the value.
*/
function typeInElement(value: string, element: HTMLInputElement, autoFocus = true) {
element.focus();
element.value = value;
dispatchFakeEvent(element, 'input');
}

/** This is a mock keyboard event to test keyboard events in the autocomplete. */
class MockKeyboardEvent {
constructor(public keyCode: number) {}
Expand Down
13 changes: 13 additions & 0 deletions src/lib/core/testing/type-in-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {dispatchFakeEvent} from './dispatch-events';

/**
* Focuses an input, sets its value and dispatches
* the `input` event, simulating the user typing.
* @param value Value to be set on the input.
* @param element Element onto which to set the value.
*/
export function typeInElement(value: string, element: HTMLInputElement, autoFocus = true) {
element.focus();
element.value = value;
dispatchFakeEvent(element, 'input');
}

0 comments on commit 932b4a0

Please sign in to comment.