From e50d0ef988579bca89f45d89f76819a4d1e0c31b Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Tue, 7 Mar 2017 21:50:25 -0800 Subject: [PATCH 1/2] fix(autocomplete): aria-expanded should be updated when panel hides --- src/lib/autocomplete/autocomplete-trigger.ts | 2 +- src/lib/autocomplete/autocomplete.spec.ts | 102 ++++++++++++------- 2 files changed, 66 insertions(+), 38 deletions(-) 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..ad095e7926e8 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -66,35 +66,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 +194,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 +776,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 +926,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); From 40e48e4ca110db245b09ba163e1878d09bab2020 Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Fri, 10 Mar 2017 10:12:50 -0800 Subject: [PATCH 2/2] address comments --- src/lib/autocomplete/autocomplete.spec.ts | 15 ++------------- src/lib/core/testing/type-in-element.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 src/lib/core/testing/type-in-element.ts diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index ad095e7926e8..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'; @@ -807,7 +808,7 @@ describe('MdAutocomplete', () => { fixture.whenStable().then(() => { fixture.detectChanges(); expect(input.getAttribute('aria-expanded')) - .toBe('false', 'Expected aria-expanded to be false when panel hides itself.'); + .toBe('false', 'Expected aria-expanded to be false when panel hides itself.'); }); }); }); @@ -1114,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'); +}