Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be good to move this method to test utils

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');
}