mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(select): close the panel when pressing escape (#3879)
This commit is contained in:
committed by
Kara
parent
81a6f8dbd8
commit
94a2855d45
@@ -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;
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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>) {
|
||||
|
||||
@@ -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', () => {
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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';
|
||||
@@ -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 = [
|
||||
@@ -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;
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
@@ -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. */
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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', () => {
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user