Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/cdk/overlay/_overlay.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ $backdrop-animation-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
}
}

.cdk-overlay-backdrop-transition-disabled {
// Reset the transition by overriding, rather than adding
// the `transition` conditionally, so we can cover the case
// where consumers specified their own transition.
transition: none;
}

.cdk-overlay-dark-backdrop {
background: $cdk-overlay-dark-backdrop-background;
}
Expand Down
43 changes: 31 additions & 12 deletions src/cdk/overlay/overlay-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
};
});

/** Whether animations are disabled for this overlay. */
private readonly _animationsDisabled: boolean;

/** Stream of keydown events dispatched to this overlay. */
_keydownEvents = new Subject<KeyboardEvent>();

Expand All @@ -70,14 +73,20 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
private _keyboardDispatcher: OverlayKeyboardDispatcher,
private _document: Document,
// @breaking-change 8.0.0 `_location` parameter to be made required.
private _location?: Location) {
private _location?: Location,
/**
* @deprecated `animationMode` parameter to be made required.
* @breaking-change 8.0.0
*/
animationMode?: string) {

if (_config.scrollStrategy) {
this._scrollStrategy = _config.scrollStrategy;
this._scrollStrategy.attach(this);
}

this._positionStrategy = _config.positionStrategy;
this._animationsDisabled = animationMode === 'NoopAnimations';
}

/** The overlay's HTML element */
Expand Down Expand Up @@ -380,6 +389,10 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
this._backdropElement = this._document.createElement('div');
this._backdropElement.classList.add('cdk-overlay-backdrop');

if (this._animationsDisabled) {
this._backdropElement.classList.add('cdk-overlay-backdrop-transition-disabled');
}

if (this._config.backdropClass) {
this._toggleClasses(this._backdropElement, this._config.backdropClass, true);
}
Expand Down Expand Up @@ -449,20 +462,26 @@ export class OverlayRef implements PortalOutlet, OverlayReference {
clearTimeout(timeoutId);
};

backdropToDetach.classList.remove('cdk-overlay-backdrop-showing');
if (this._animationsDisabled) {
// When the animations are disabled via the `NoopAnimationsModule`, we can be
// fairly certain that they won't happen so we can remove the element immediately.
finishDetach();
} else {
backdropToDetach.classList.remove('cdk-overlay-backdrop-showing');

this._ngZone.runOutsideAngular(() => {
backdropToDetach!.addEventListener('transitionend', finishDetach);
});
this._ngZone.runOutsideAngular(() => {
backdropToDetach!.addEventListener('transitionend', finishDetach);
});

// If the backdrop doesn't have a transition, the `transitionend` event won't fire.
// In this case we make it unclickable and we try to remove it after a delay.
backdropToDetach.style.pointerEvents = 'none';
// If the backdrop doesn't have a transition, the `transitionend` event won't fire.
// In this case we make it unclickable and we try to remove it after a delay.
backdropToDetach.style.pointerEvents = 'none';

// Run this outside the Angular zone because there's nothing that Angular cares about.
// If it were to run inside the Angular zone, every test that used Overlay would have to be
// either async or fakeAsync.
timeoutId = this._ngZone.runOutsideAngular(() => setTimeout(finishDetach, 500));
// Run this outside the Angular zone because there's nothing that Angular cares about.
// If it were to run inside the Angular zone, every test that used Overlay would have to be
// either async or fakeAsync.
timeoutId = this._ngZone.runOutsideAngular(() => setTimeout(finishDetach, 500));
}
}

/** Toggles a single CSS class or an array of classes on an element. */
Expand Down
59 changes: 59 additions & 0 deletions src/cdk/overlay/overlay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
ScrollStrategy,
} from './index';
import {OverlayReference} from './overlay-reference';
import {BrowserAnimationsModule, NoopAnimationsModule} from '@angular/platform-browser/animations';


describe('Overlay', () => {
Expand Down Expand Up @@ -703,6 +704,64 @@ describe('Overlay', () => {
expect(backdrop.classList).toContain('cdk-overlay-transparent-backdrop');
});

it('should not disable the backdrop transition if animations are enabled', () => {
overlayContainer.ngOnDestroy();

TestBed
.resetTestingModule()
.configureTestingModule({
imports: [OverlayModule, PortalModule, OverlayTestModule, BrowserAnimationsModule],
})
.compileComponents();

inject([Overlay, OverlayContainer], (o: Overlay, oc: OverlayContainer) => {
overlay = o;
overlayContainer = oc;
overlayContainerElement = oc.getContainerElement();
})();

let fixture = TestBed.createComponent(TestComponentWithTemplatePortals);
fixture.detectChanges();
componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef);
viewContainerFixture = fixture;

let overlayRef = overlay.create(config);
overlayRef.attach(componentPortal);
viewContainerFixture.detectChanges();

let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
expect(backdrop.classList).not.toContain('cdk-overlay-backdrop-transition-disabled');
});

it('should disable the backdrop transition when animations are disabled', () => {
overlayContainer.ngOnDestroy();

TestBed
.resetTestingModule()
.configureTestingModule({
imports: [OverlayModule, PortalModule, OverlayTestModule, NoopAnimationsModule],
})
.compileComponents();

inject([Overlay, OverlayContainer], (o: Overlay, oc: OverlayContainer) => {
overlay = o;
overlayContainer = oc;
overlayContainerElement = oc.getContainerElement();
})();

let fixture = TestBed.createComponent(TestComponentWithTemplatePortals);
fixture.detectChanges();
componentPortal = new ComponentPortal(PizzaMsg, fixture.componentInstance.viewContainerRef);
viewContainerFixture = fixture;

let overlayRef = overlay.create(config);
overlayRef.attach(componentPortal);
viewContainerFixture.detectChanges();

let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop') as HTMLElement;
expect(backdrop.classList).toContain('cdk-overlay-backdrop-transition-disabled');
});

it('should apply multiple custom classes to the overlay', () => {
config.backdropClass = ['custom-class-1', 'custom-class-2'];

Expand Down
10 changes: 8 additions & 2 deletions src/cdk/overlay/overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {OverlayContainer} from './overlay-container';
import {OverlayRef} from './overlay-ref';
import {OverlayPositionBuilder} from './position/overlay-position-builder';
import {ScrollStrategyOptions} from './scroll/index';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces a dependency on @angular/animations that I don't think we want in the CDK

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn’t the token technically in platform-browser? Also we’ve got the experimental/dialog which has a dependency on animations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, though I assume @angular/platform-browser/animations would depend on @angular/animations? If it doesn't then maybe this is fine. @jelbourn might know more



/** Next overlay unique ID. */
Expand Down Expand Up @@ -56,7 +57,12 @@ export class Overlay {
@Inject(DOCUMENT) private _document: any,
private _directionality: Directionality,
// @breaking-change 8.0.0 `_location` parameter to be made required.
@Optional() private _location?: Location) { }
@Optional() private _location?: Location,
/**
* @deprecated `animationMode` parameter to be made required.
* @breaking-change 8.0.0
*/
@Optional() @Inject(ANIMATION_MODULE_TYPE) private _animationMode?: string) { }

/**
* Creates an overlay.
Expand All @@ -72,7 +78,7 @@ export class Overlay {
overlayConfig.direction = overlayConfig.direction || this._directionality.value;

return new OverlayRef(portalOutlet, host, pane, overlayConfig, this._ngZone,
this._keyboardDispatcher, this._document, this._location);
this._keyboardDispatcher, this._document, this._location, this._animationMode);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/material/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,13 +372,16 @@ describe('MatDatepicker', () => {
for (let i = 0; i < 3; i++) {
testComponent.datepicker.open();
fixture.detectChanges();
flush();

testComponent.datepicker.close();
fixture.detectChanges();
flush();
}

testComponent.datepicker.open();
fixture.detectChanges();
flush();

const spy = jasmine.createSpy('close event spy');
const subscription = testComponent.datepicker.closedStream.subscribe(spy);
Expand Down