Skip to content

Commit

Permalink
feat(select): close the panel when pressing escape (#3879)
Browse files Browse the repository at this point in the history
  • Loading branch information
crisbeto authored and kara committed Apr 21, 2017
1 parent 81a6f8d commit 94a2855
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 9 deletions.
7 changes: 3 additions & 4 deletions src/lib/core/a11y/list-key-manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ describe('Key managers', () => {
TAB_EVENT = new FakeEvent(TAB) as KeyboardEvent;
HOME_EVENT = new FakeEvent(HOME) as KeyboardEvent;
END_EVENT = new FakeEvent(END) as KeyboardEvent;

});


Expand Down Expand Up @@ -218,11 +217,11 @@ describe('Key managers', () => {
});

it('should emit tabOut when the tab key is pressed', () => {
let tabOutEmitted = false;
keyManager.tabOut.first().subscribe(() => tabOutEmitted = true);
let spy = jasmine.createSpy('tabOut spy');
keyManager.tabOut.first().subscribe(spy);
keyManager.onKeydown(TAB_EVENT);

expect(tabOutEmitted).toBe(true);
expect(spy).toHaveBeenCalled();
});

it('should prevent the default keyboard action', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/core/a11y/list-key-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface CanDisable {
export class ListKeyManager<T extends CanDisable> {
private _activeItemIndex: number = null;
private _activeItem: T;
private _tabOut: Subject<any> = new Subject();
private _tabOut = new Subject<void>();
private _wrap: boolean = false;

constructor(private _items: QueryList<T>) {
Expand Down
13 changes: 13 additions & 0 deletions src/lib/core/overlay/overlay-directives.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {OverlayContainer} from './overlay-container';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
import {ConnectedOverlayPositionChange} from './position/connected-position';
import {Dir} from '../rtl/dir';
import {dispatchKeyboardEvent} from '../testing/dispatch-events';
import {ESCAPE} from '../keyboard/keycodes';


describe('Overlay directives', () => {
Expand Down Expand Up @@ -98,6 +100,17 @@ describe('Overlay directives', () => {
expect(getPaneElement().getAttribute('dir')).toBe('ltr');
});

it('should close when pressing escape', () => {
fixture.componentInstance.isOpen = true;
fixture.detectChanges();

dispatchKeyboardEvent(document, 'keydown', ESCAPE);
fixture.detectChanges();

expect(overlayContainerElement.textContent.trim()).toBe('',
'Expected overlay to have been detached.');
});

describe('inputs', () => {

it('should set the width', () => {
Expand Down
28 changes: 26 additions & 2 deletions src/lib/core/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
Input,
OnDestroy,
Output,
ElementRef
ElementRef,
Renderer2,
} from '@angular/core';
import {Overlay, OVERLAY_PROVIDERS} from './overlay';
import {OverlayRef} from './overlay-ref';
Expand All @@ -21,10 +22,12 @@ import {
} from './position/connected-position';
import {PortalModule} from '../portal/portal-directives';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
import {Subscription} from 'rxjs/Subscription';
import {Dir, LayoutDirection} from '../rtl/dir';
import {Scrollable} from './scroll/scrollable';
import {coerceBooleanProperty} from '../coercion/boolean-property';
import {ESCAPE} from '../keyboard/keycodes';
import {Subscription} from 'rxjs/Subscription';


/** Default set of positions for the overlay. Follows the behavior of a dropdown. */
let defaultPositionList = [
Expand Down Expand Up @@ -68,6 +71,7 @@ export class ConnectedOverlayDirective implements OnDestroy {
private _offsetX: number = 0;
private _offsetY: number = 0;
private _position: ConnectedPositionStrategy;
private _escapeListener: Function;

/** Origin for the connected overlay. */
@Input() origin: OverlayOrigin;
Expand Down Expand Up @@ -152,6 +156,7 @@ export class ConnectedOverlayDirective implements OnDestroy {

constructor(
private _overlay: Overlay,
private _renderer: Renderer2,
templateRef: TemplateRef<any>,
viewContainerRef: ViewContainerRef,
@Optional() private _dir: Dir) {
Expand Down Expand Up @@ -249,6 +254,7 @@ export class ConnectedOverlayDirective implements OnDestroy {

this._position.withDirection(this.dir);
this._overlayRef.getState().direction = this.dir;
this._initEscapeListener();

if (!this._overlayRef.hasAttached()) {
this._overlayRef.attach(this._templatePortal);
Expand All @@ -273,6 +279,10 @@ export class ConnectedOverlayDirective implements OnDestroy {
this._backdropSubscription.unsubscribe();
this._backdropSubscription = null;
}

if (this._escapeListener) {
this._escapeListener();
}
}

/** Destroys the overlay created by this directive. */
Expand All @@ -284,9 +294,23 @@ export class ConnectedOverlayDirective implements OnDestroy {
if (this._backdropSubscription) {
this._backdropSubscription.unsubscribe();
}

if (this._positionSubscription) {
this._positionSubscription.unsubscribe();
}

if (this._escapeListener) {
this._escapeListener();
}
}

/** Sets the event listener that closes the overlay when pressing Escape. */
private _initEscapeListener() {
this._escapeListener = this._renderer.listen('document', 'keydown', (event: KeyboardEvent) => {
if (event.keyCode === ESCAPE) {
this._detachOverlay();
}
});
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/select/select.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<ng-template cdk-connected-overlay [origin]="origin" [open]="panelOpen" hasBackdrop (backdropClick)="close()"
backdropClass="cdk-overlay-transparent-backdrop" [positions]="_positions" [minWidth]="_triggerWidth"
[offsetY]="_offsetY" [offsetX]="_offsetX" (attach)="_setScrollTop()">
[offsetY]="_offsetY" [offsetX]="_offsetX" (attach)="_setScrollTop()" (detach)="close()">
<div class="mat-select-panel" [@transformPanel]="'showing'" (@transformPanel.done)="_onPanelDone()"
(keydown)="_keyManager.onKeydown($event)" [style.transformOrigin]="_transformOrigin"
[class.mat-select-panel-done-animating]="_panelDoneAnimating">
Expand Down
17 changes: 16 additions & 1 deletion src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import {
ControlValueAccessor, FormControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule
} from '@angular/forms';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
import {dispatchFakeEvent} from '../core/testing/dispatch-events';
import {dispatchFakeEvent, dispatchKeyboardEvent} from '../core/testing/dispatch-events';
import {wrappedErrorMessage} from '../core/testing/wrapped-error-message';
import {TAB} from '../core/keyboard/keycodes';


describe('MdSelect', () => {
Expand Down Expand Up @@ -205,6 +206,20 @@ describe('MdSelect', () => {
});
}));

it('should close the panel when tabbing out', async(() => {
trigger.click();
fixture.detectChanges();
expect(fixture.componentInstance.select.panelOpen).toBe(true);

const panel = overlayContainerElement.querySelector('.mat-select-panel');
dispatchKeyboardEvent(panel, 'keydown', TAB);
fixture.detectChanges();

fixture.whenStable().then(() => {
expect(fixture.componentInstance.select.panelOpen).toBe(false);
});
}));

});

describe('selection logic', () => {
Expand Down

0 comments on commit 94a2855

Please sign in to comment.