diff --git a/components/modal/nz-modal.component.html b/components/modal/nz-modal.component.html index 552adb92d54..be423e10b5f 100644 --- a/components/modal/nz-modal.component.html +++ b/components/modal/nz-modal.component.html @@ -10,6 +10,7 @@ >
extends NzModalRef private focusTrap: FocusTrap; private scrollStrategy: BlockScrollStrategy; private overlayRef: OverlayRef; + private dialogMouseDown = false; + private timeoutId: number; [key: string]: any; // tslint:disable-line:no-any @@ -260,6 +263,7 @@ export class NzModalComponent extends NzModalRef this.unsubscribe$.next(); this.unsubscribe$.complete(); }); + clearTimeout(this.timeoutId); } setOverlayRef(overlayRef: OverlayRef): void { @@ -309,12 +313,25 @@ export class NzModalComponent extends NzModalRef return this.elementRef && this.elementRef.nativeElement; } + onMaskDialogDown(): void { + this.dialogMouseDown = true; + } + + onDialogUp(): void { + if (this.dialogMouseDown) { + this.timeoutId = setTimeout(() => { + this.dialogMouseDown = false; + }, 0); + } + } + onClickMask($event: MouseEvent): void { if ( this.mask && this.maskClosable && - ($event.target as HTMLElement).classList.contains('ant-modal-wrap') && - this.nzVisible + ($event.target as HTMLElement).classList.contains(WRAP_CLASS_NAME) && + this.nzVisible && + !this.dialogMouseDown ) { this.onClickOkCancel('cancel'); } diff --git a/components/modal/nz-modal.spec.ts b/components/modal/nz-modal.spec.ts index 2d9fb5563d4..324e16e0351 100644 --- a/components/modal/nz-modal.spec.ts +++ b/components/modal/nz-modal.spec.ts @@ -9,7 +9,7 @@ import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/t import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NzButtonComponent, NzButtonModule } from 'ng-zorro-antd/button'; -import { dispatchKeyboardEvent, NzMeasureScrollbarService } from 'ng-zorro-antd/core'; +import { dispatchFakeEvent, dispatchKeyboardEvent, NzMeasureScrollbarService } from 'ng-zorro-antd/core'; import { NzI18nService } from 'ng-zorro-antd/i18n'; import { NzIconTestModule } from 'ng-zorro-antd/icon/testing'; import en_US from '../i18n/languages/en_US'; @@ -441,7 +441,7 @@ describe('NzModal', () => { TestBed.configureTestingModule({ imports: [NoopAnimationsModule, NzModalModule], providers: [NzMeasureScrollbarService], - declarations: [NzDemoModalBasicComponent, ModalByServiceComponent] + declarations: [NzDemoModalBasicComponent, NzDemoModalMaskComponent, ModalByServiceComponent] }); TestBed.compileComponents(); @@ -672,6 +672,44 @@ describe('NzModal', () => { document.body.removeChild(forceScrollElement); })); }); + + describe('close with mask', () => { + let fixture: ComponentFixture; + beforeEach(() => { + fixture = TestBed.createComponent(NzDemoModalMaskComponent); + }); + + it('should close when mask click', fakeAsync(() => { + fixture.componentInstance.isVisible = true; + fixture.detectChanges(); + tick(1000); + fixture.detectChanges(); + const nativeElement = fixture.debugElement.query(By.css('.ant-modal-wrap')).nativeElement; + fixture.detectChanges(); + nativeElement!.click(); + fixture.detectChanges(); + tick(1000); + fixture.detectChanges(); + expectModalHidden(fixture.debugElement.query(By.css('nz-modal')).nativeElement, true); + })); + + it('should not close if mouse down in dialog', fakeAsync(() => { + fixture.componentInstance.isVisible = true; + fixture.detectChanges(); + tick(1000); + fixture.detectChanges(); + const bodyNativeElement = fixture.debugElement.query(By.css('.ant-modal-body')).nativeElement; + dispatchFakeEvent(bodyNativeElement, 'mousedown'); + fixture.detectChanges(); + const warpNativeElement = fixture.debugElement.query(By.css('.ant-modal-wrap')).nativeElement; + dispatchFakeEvent(warpNativeElement, 'mouseup'); + dispatchFakeEvent(warpNativeElement, 'click'); + fixture.detectChanges(); + tick(1000); + fixture.detectChanges(); + expectModalHidden(fixture.debugElement.query(By.css('nz-modal')).nativeElement, false); + })); + }); }); // ------------------------------------------- @@ -689,6 +727,20 @@ class NzDemoModalBasicComponent { modalAvailable = true; } +@Component({ + template: ` + +

content

+
+ ` +}) +class NzDemoModalMaskComponent { + isVisible = false; + handleCancel(): void { + this.isVisible = false; + } +} + @Component({ template: `