From b758572e4465e172fd8011d0781047fcbefa67ef Mon Sep 17 00:00:00 2001 From: Edoardo Luppi Date: Sun, 28 Jul 2019 18:20:15 +0200 Subject: [PATCH] fix(module:dropdown): hide backdrop when disabled and restore escape (#3831) close #3835 --- .../dropdown/nz-dropdown.directive.spec.ts | 68 +++++++++++++++++-- components/dropdown/nz-dropdown.directive.ts | 12 +++- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/components/dropdown/nz-dropdown.directive.spec.ts b/components/dropdown/nz-dropdown.directive.spec.ts index 519ce595b74..d76d612f637 100644 --- a/components/dropdown/nz-dropdown.directive.spec.ts +++ b/components/dropdown/nz-dropdown.directive.spec.ts @@ -1,9 +1,10 @@ +import { ESCAPE } from '@angular/cdk/keycodes'; import { OverlayContainer } from '@angular/cdk/overlay'; import { Component, Provider, Type } from '@angular/core'; import { fakeAsync, inject, tick, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { dispatchFakeEvent } from '../core/testing'; +import { dispatchFakeEvent, dispatchKeyboardEvent } from '../core/testing'; import { NzMenuModule } from '../menu/nz-menu.module'; import { NzDropDownDirective } from './nz-dropdown.directive'; import { NzDropDownModule } from './nz-dropdown.module'; @@ -95,21 +96,76 @@ describe('dropdown', () => { ); }).not.toThrowError(); })); - it('should backdrop be disabled', fakeAsync(() => { + + describe('when nzBackdrop=false', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = createComponent(NzTestDropdownComponent, [], []); + fixture.componentInstance.backdrop = false; + }); + + it('backdrop should be invisible if nzTrigger=click', fakeAsync(() => { + fixture.componentInstance.trigger = 'click'; + fixture.detectChanges(); + + expect(() => { + const dropdownElement = fixture.debugElement.query(By.directive(NzDropDownDirective)).nativeElement; + dispatchFakeEvent(dropdownElement, 'click'); + + tick(1000); + fixture.detectChanges(); + + const backdrop = overlayContainerElement.querySelector('.nz-overlay-transparent-backdrop'); + expect(backdrop).not.toBeNull(); + }).not.toThrowError(); + })); + + it('should disappear if invisible backdrop clicked if nzTrigger=click', fakeAsync(() => { + fixture.componentInstance.trigger = 'click'; + fixture.detectChanges(); + + expect(() => { + const dropdownElement = fixture.debugElement.query(By.directive(NzDropDownDirective)).nativeElement; + dispatchFakeEvent(dropdownElement, 'click'); + + tick(1000); + fixture.detectChanges(); + + const backdrop = overlayContainerElement.querySelector('.nz-overlay-transparent-backdrop'); + expect(backdrop).not.toBeNull(); + + dispatchFakeEvent(backdrop as Element, 'click'); + tick(1000); + fixture.detectChanges(); + + const nullBackdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); + expect(nullBackdrop).toBeNull(); + }).not.toThrowError(); + })); + }); + + it('should disappear if Escape pressed', fakeAsync(() => { const fixture = createComponent(NzTestDropdownComponent, [], []); fixture.componentInstance.trigger = 'click'; - fixture.componentInstance.backdrop = false; fixture.detectChanges(); expect(() => { const dropdownElement = fixture.debugElement.query(By.directive(NzDropDownDirective)).nativeElement; - dispatchFakeEvent(dropdownElement, 'mouseenter'); - fixture.detectChanges(); + + dispatchFakeEvent(dropdownElement, 'click'); tick(1000); fixture.detectChanges(); const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); - expect(backdrop).toBeNull(); + expect(backdrop).not.toBeNull(); + + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE); + tick(1000); + fixture.detectChanges(); + + const nullBackdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop'); + expect(nullBackdrop).toBeNull(); }).not.toThrowError(); })); it('should nzOverlayClassName and nzOverlayStyle work', fakeAsync(() => { diff --git a/components/dropdown/nz-dropdown.directive.ts b/components/dropdown/nz-dropdown.directive.ts index 9cf2603a3a2..4dab88be598 100644 --- a/components/dropdown/nz-dropdown.directive.ts +++ b/components/dropdown/nz-dropdown.directive.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { hasModifierKey, ESCAPE } from '@angular/cdk/keycodes'; import { ConnectedPosition, ConnectionPositionPair, @@ -31,7 +32,7 @@ import { } from '@angular/core'; import { DEFAULT_DROPDOWN_POSITIONS, InputBoolean, POSITION_MAP } from 'ng-zorro-antd/core'; import { combineLatest, fromEvent, merge, EMPTY, Observable, Subject, Subscription } from 'rxjs'; -import { debounceTime, distinctUntilChanged, map, mapTo, takeUntil, tap } from 'rxjs/operators'; +import { debounceTime, distinctUntilChanged, filter, map, mapTo, takeUntil, tap } from 'rxjs/operators'; import { NzDropdownMenuComponent, NzPlacementType } from './nz-dropdown-menu.component'; @Directive({ @@ -90,7 +91,8 @@ export class NzDropDownDirective implements AfterViewInit, OnDestroy, OnChanges .flexibleConnectedTo(this.el) .withLockedPosition(), minWidth: this.triggerWidth, - hasBackdrop: this.nzBackdrop && this.nzTrigger === 'click', + hasBackdrop: this.nzTrigger === 'click', + backdropClass: this.nzBackdrop ? undefined : 'nz-overlay-transparent-backdrop', scrollStrategy: this.overlay.scrollStrategies.reposition() }); } @@ -133,7 +135,11 @@ export class NzDropDownDirective implements AfterViewInit, OnDestroy, OnChanges private subscribeOverlayEvent(overlayRef: OverlayRef): void { this.overlaySubscription.unsubscribe(); - this.overlaySubscription = merge(overlayRef.backdropClick(), overlayRef.detachments()) + this.overlaySubscription = merge( + overlayRef.backdropClick(), + overlayRef.detachments(), + overlayRef.keydownEvents().pipe(filter(e => e.keyCode === ESCAPE && !hasModifierKey(e))) + ) .pipe(takeUntil(this.destroy$)) .subscribe(() => { this.nzDropdownMenu.setVisibleStateWhen(false);