From 932b4a0767dc63149297a66ef717bafd410d5eb3 Mon Sep 17 00:00:00 2001 From: Kara Date: Mon, 13 Mar 2017 14:14:55 -0700 Subject: [PATCH] fix(autocomplete): aria-expanded should be updated when panel hides (#3494) * fix(autocomplete): aria-expanded should be updated when panel hides * address comments --- src/lib/autocomplete/autocomplete-trigger.ts | 2 +- src/lib/autocomplete/autocomplete.spec.ts | 115 +++++++++++-------- src/lib/core/testing/type-in-element.ts | 13 +++ 3 files changed, 80 insertions(+), 50 deletions(-) create mode 100644 src/lib/core/testing/type-in-element.ts diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 1b773673e843..64912f092f95 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -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. */ diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 78eb4a4b118c..fd7ad0c6480e 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -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'; @@ -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'); @@ -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.`); }); @@ -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(); @@ -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); @@ -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) {} diff --git a/src/lib/core/testing/type-in-element.ts b/src/lib/core/testing/type-in-element.ts new file mode 100644 index 000000000000..326bee5a6d51 --- /dev/null +++ b/src/lib/core/testing/type-in-element.ts @@ -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'); +}