diff --git a/components/modal/doc/index.en-US.md b/components/modal/doc/index.en-US.md
index 29ffa05edb1..9559e64c908 100644
--- a/components/modal/doc/index.en-US.md
+++ b/components/modal/doc/index.en-US.md
@@ -45,6 +45,7 @@ The dialog is currently divided into 2 modes, `normal mode` and `confirm box mod
| nzCancelLoading | Whether to apply loading visual effect for Cancel button or not | boolean | false |
| nzFooter | Footer content, set as footer=null when you don't need default buttons. 1. Only valid in normal mode.
2. You can customize the buttons to the maximum extent by passing a `ModalButtonOptions` configuration (see the case or the instructions below). | string
TemplateRef
ModalButtonOptions | OK and Cancel buttons |
| nzGetContainer | The mount node for Modal | HTMLElement / () => HTMLElement| A default container |
+| nzKeyboard | Whether support press esc to close | boolean | true |
| nzMask | Whether show mask or not. | boolean | true |
| nzMaskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true |
| nzMaskStyle | Style for modal's mask element. | object | - |
diff --git a/components/modal/doc/index.zh-CN.md b/components/modal/doc/index.zh-CN.md
index 906f06217ab..0ea67eafc37 100644
--- a/components/modal/doc/index.zh-CN.md
+++ b/components/modal/doc/index.zh-CN.md
@@ -46,6 +46,7 @@ title: Modal
| nzCancelLoading | 取消按钮 loading | boolean | false |
| nzFooter | 底部内容。1. 仅在普通模式下有效。
2. 可通过传入 ModalButtonOptions 来最大程度自定义按钮(详见案例或下方说明)。
3. 当不需要底部时,可以设为 null | string
TemplateRef
ModalButtonOptions | 默认的确定取消按钮 |
| nzGetContainer | 指定 Modal 挂载的 HTML 节点 | HTMLElement
() => HTMLElement| 默认容器 |
+| nzKeyboard | 是否支持键盘esc关闭 | boolean | true |
| nzMask | 是否展示遮罩 | boolean | true |
| nzMaskClosable | 点击蒙层是否允许关闭 | boolean | true |
| nzMaskStyle | 遮罩样式 | object | 无 |
diff --git a/components/modal/nz-modal.component.ts b/components/modal/nz-modal.component.ts
index 18e82921150..e4fe9011a27 100644
--- a/components/modal/nz-modal.component.ts
+++ b/components/modal/nz-modal.component.ts
@@ -23,7 +23,7 @@ import {
ViewContainerRef
} from '@angular/core';
-import { Observable, Subject } from 'rxjs';
+import { fromEvent, Observable, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { NzMeasureScrollbarService } from '../core/services/nz-measure-scrollbar.service';
@@ -31,6 +31,7 @@ import { NzMeasureScrollbarService } from '../core/services/nz-measure-scrollbar
import { InputBoolean } from '../core/util/convert';
import { NzI18nService } from '../i18n/nz-i18n.service';
+import { ESCAPE } from '@angular/cdk/keycodes';
import ModalUtil from './modal-util';
import { NzModalConfig, NZ_MODAL_CONFIG, NZ_MODAL_DEFAULT_CONFIG } from './nz-modal-config';
import { NzModalControlService } from './nz-modal-control.service';
@@ -108,6 +109,8 @@ export class NzModalComponent extends NzModalRef impleme
@ViewChild('modalContainer') modalContainer: ElementRef;
@ViewChild('bodyContainer', { read: ViewContainerRef }) bodyContainer: ViewContainerRef;
+ @Input() @InputBoolean() nzKeyboard: boolean = true;
+
get hidden(): boolean {
return !this.nzVisible && !this.animationState;
} // Indicate whether this dialog should hidden
@@ -140,6 +143,8 @@ export class NzModalComponent extends NzModalRef impleme
ngOnInit(): void {
this.i18n.localeChange.pipe(takeUntil(this.unsubscribe$)).subscribe(() => this.locale = this.i18n.getLocaleData('Modal'));
+ fromEvent(this.document.body, 'keydown').pipe(takeUntil(this.unsubscribe$)).subscribe(e => this.keydownListener(e));
+
if (this.isComponent(this.nzContent)) {
this.createDynamicComponent(this.nzContent as Type); // Create component along without View
}
@@ -195,6 +200,12 @@ export class NzModalComponent extends NzModalRef impleme
});
}
+ keydownListener(event: KeyboardEvent): void {
+ if (event.keyCode === ESCAPE && this.nzKeyboard) {
+ this.onClickOkCancel('cancel');
+ }
+ }
+
open(): void {
this.changeVisibleFromInside(true);
}
diff --git a/components/modal/nz-modal.spec.ts b/components/modal/nz-modal.spec.ts
index 05fabd0a1c8..eccc0c9adc9 100644
--- a/components/modal/nz-modal.spec.ts
+++ b/components/modal/nz-modal.spec.ts
@@ -11,6 +11,8 @@ import { NzButtonComponent } from '../button/nz-button.component';
import { NzButtonModule } from '../button/nz-button.module';
import { NzMeasureScrollbarService } from '../core/services/nz-measure-scrollbar.service';
+import { ESCAPE } from '@angular/cdk/keycodes';
+import { createKeyboardEvent, dispatchKeyboardEvent } from '../core/testing';
import en_US from '../i18n/languages/en_US';
import { NzI18nService } from '../i18n/nz-i18n.service';
import { NzIconModule } from '../icon/nz-icon.module';
@@ -22,6 +24,8 @@ import { NzModalComponent } from './nz-modal.component';
import { NzModalModule } from './nz-modal.module';
import { NzModalService } from './nz-modal.service';
+let counter = 0;
+
describe('modal testing (legacy)', () => {
let instance;
let fixture: ComponentFixture<{}>;
@@ -112,9 +116,10 @@ describe('modal testing (legacy)', () => {
}); // /confirm-promise
describe('NormalModal: created by service with most APIs', () => {
- const tempModalId = generateUniqueId(); // Temp unique id to mark the confirm modal that created by service
+ let tempModalId; // Temp unique id to mark the confirm modal that created by service
let modalAgent: NzModalRef;
let modalElement: HTMLElement;
+ let modalInstance;
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -129,13 +134,13 @@ describe('modal testing (legacy)', () => {
instance = fixture.debugElement.componentInstance;
modalAgent = instance.basicModal;
modalElement = modalAgent.getElement();
+ tempModalId = generateUniqueId();
modalElement.classList.add(tempModalId); // Mark with id
+ modalInstance = modalAgent.getInstance();
});
it('should correctly render all basic props', fakeAsync(() => {
- const modalInstance = modalAgent.getInstance();
spyOn(console, 'log');
-
// [Hack] Codes that can't be covered by normal operations
// tslint:disable-next-line:no-any
expect((modalInstance as any).changeVisibleFromInside(true) instanceof Promise).toBe(true);
@@ -159,8 +164,13 @@ describe('modal testing (legacy)', () => {
// click ok button
getButtonOk(modalElement).click();
+ flush();
expect(console.log).toHaveBeenCalledWith('click ok');
expectModalDestroyed(tempModalId, false); // shouldn't destroy when ok button returns false
+ })); // /basic props
+
+ it('should be closed when clicking cancel button', fakeAsync(() => {
+ spyOn(console, 'log');
// change and click mask
modalInstance.nzMask = true;
// should show mask
@@ -178,9 +188,22 @@ describe('modal testing (legacy)', () => {
(modalElement.querySelector('.ant-modal-wrap') as HTMLElement).click();
expect(console.log).not.toHaveBeenCalledWith('click cancel');
flush();
- // TODO: repair this, why my modifying this case would influence another case?
- // expectModalDestroyed(tempModalId, true); // should be destroyed
- })); // /basic props
+ fixture.detectChanges();
+ expectModalDestroyed(tempModalId, true); // should be destroyed
+ }));
+
+ it('should be closed when clicking ESC', fakeAsync(() => {
+ // click 'ESC' key
+ dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
+ fixture.detectChanges();
+ expectModalDestroyed(tempModalId, false);
+
+ modalInstance.nzKeyboard = true;
+ dispatchKeyboardEvent(document.body, 'keydown', ESCAPE);
+ flush();
+ fixture.detectChanges();
+ expectModalDestroyed(tempModalId, true);
+ }));
});
describe('NormalModal: created by service with vary nzContent and nzFooter', () => {
@@ -658,6 +681,7 @@ class TestBasicServiceComponent {
nzTitle: 'TEST BOLD TITLE',
nzContent: 'test html content
',
nzClosable: false,
+ nzKeyboard: false,
nzMask: false,
nzMaskClosable: false,
nzMaskStyle: { opacity: 0.4 },
@@ -785,7 +809,6 @@ function expectModalDestroyed(classId: string, destroyed: boolean): void {
}
}
-let counter = 0;
function generateUniqueId(): string {
return `testing-uniqueid-${counter++}`;
}
diff --git a/components/modal/nz-modal.type.ts b/components/modal/nz-modal.type.ts
index 42e73ed63c6..c971f03872f 100644
--- a/components/modal/nz-modal.type.ts
+++ b/components/modal/nz-modal.type.ts
@@ -21,6 +21,7 @@ export interface ModalOptions { // tslint:disable-line:no-any
nzContent?: string | TemplateRef<{}> | Type;
nzComponentParams?: Partial;
nzClosable?: boolean;
+ nzKeyboard?: boolean;
nzMask?: boolean;
nzMaskClosable?: boolean;
nzMaskStyle?: object;