From 8e7726102b9d41c7bb7fcc08e58af9f276b9a8ab Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 29 Sep 2017 22:12:47 +0200 Subject: [PATCH] fix(snack-bar): subsequent snack bars not opening; animation issues (#7086) * Fixes subsequent snack bars not showing up if they are opened while another snack bar is open. This was due to the latter snack bar getting closed immediately, because one of the animation events coming in is `void->void`. * Simplifies the logic for determining the animation state. Using the getter ended up passing returning an invalid state in some cases (e.g. `undefined-top` or `undefined-bottom`). Fixes #7063. --- src/lib/snack-bar/snack-bar-container.ts | 24 ++++++++---------------- src/lib/snack-bar/snack-bar.spec.ts | 14 +++++++------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/lib/snack-bar/snack-bar-container.ts b/src/lib/snack-bar/snack-bar-container.ts index 15288df08ef..84eeeea76c5 100644 --- a/src/lib/snack-bar/snack-bar-container.ts +++ b/src/lib/snack-bar/snack-bar-container.ts @@ -38,8 +38,6 @@ import {Subject} from 'rxjs/Subject'; import {MatSnackBarConfig} from './snack-bar-config'; -export type SnackBarState = 'visible' | 'hidden' | 'void'; - // TODO(jelbourn): we can't use constants from animation.ts here because you can't use // a text interpolation in anything that is analyzed statically with ngc (for AoT compile). export const SHOW_ANIMATION = '225ms cubic-bezier(0.4,0.0,1,1)'; @@ -60,7 +58,7 @@ export const HIDE_ANIMATION = '195ms cubic-bezier(0.0,0.0,0.2,1)'; host: { 'role': 'alert', 'class': 'mat-snack-bar-container', - '[@state]': 'getAnimationState()', + '[@state]': '_animationState', '(@state.done)': 'onAnimationEnd($event)' }, animations: [ @@ -93,7 +91,7 @@ export class MatSnackBarContainer extends BasePortalHost implements OnDestroy { _onEnter: Subject = new Subject(); /** The state of the snack bar animations. */ - private _animationState: SnackBarState; + _animationState = 'void'; /** The snack bar configuration. */ snackBarConfig: MatSnackBarConfig; @@ -106,14 +104,6 @@ export class MatSnackBarContainer extends BasePortalHost implements OnDestroy { super(); } - /** - * Gets the current animation state both combining one of the possibilities from - * SnackBarState and the vertical location. - */ - getAnimationState(): string { - return `${this._animationState}-${this.snackBarConfig.verticalPosition}`; - } - /** Attach a component portal as content to this snack bar container. */ attachComponentPortal(portal: ComponentPortal): ComponentRef { if (this._portalHost.hasAttached()) { @@ -146,11 +136,13 @@ export class MatSnackBarContainer extends BasePortalHost implements OnDestroy { /** Handle end of animations, updating the state of the snackbar. */ onAnimationEnd(event: AnimationEvent) { - if (event.toState === 'void' || event.toState.startsWith('hidden')) { + const {fromState, toState} = event; + + if ((toState === 'void' && fromState !== 'void') || toState.startsWith('hidden')) { this._completeExit(); } - if (event.toState.startsWith('visible')) { + if (toState.startsWith('visible')) { // Note: we shouldn't use `this` inside the zone callback, // because it can cause a memory leak. const onEnter = this._onEnter; @@ -165,14 +157,14 @@ export class MatSnackBarContainer extends BasePortalHost implements OnDestroy { /** Begin animation of snack bar entrance into view. */ enter(): void { if (!this._destroyed) { - this._animationState = 'visible'; + this._animationState = `visible-${this.snackBarConfig.verticalPosition}`; this._changeDetectorRef.detectChanges(); } } /** Begin animation of the snack bar exiting from view. */ exit(): Observable { - this._animationState = 'hidden'; + this._animationState = `hidden-${this.snackBarConfig.verticalPosition}`; return this._onExit; } diff --git a/src/lib/snack-bar/snack-bar.spec.ts b/src/lib/snack-bar/snack-bar.spec.ts index 91f6adb70f0..2133b84d810 100644 --- a/src/lib/snack-bar/snack-bar.spec.ts +++ b/src/lib/snack-bar/snack-bar.spec.ts @@ -186,12 +186,12 @@ describe('MatSnackBar', () => { let snackBarRef = snackBar.open(simpleMessage, undefined, config); viewContainerFixture.detectChanges(); - expect(snackBarRef.containerInstance.getAnimationState()) + expect(snackBarRef.containerInstance._animationState) .toBe('visible-bottom', `Expected the animation state would be 'visible-bottom'.`); snackBarRef.dismiss(); viewContainerFixture.detectChanges(); - expect(snackBarRef.containerInstance.getAnimationState()) + expect(snackBarRef.containerInstance._animationState) .toBe('hidden-bottom', `Expected the animation state would be 'hidden-bottom'.`); }); @@ -201,7 +201,7 @@ describe('MatSnackBar', () => { snackBarRef.dismiss(); viewContainerFixture.detectChanges(); - expect(snackBarRef.containerInstance.getAnimationState()) + expect(snackBarRef.containerInstance._animationState) .toBe('hidden-bottom', `Expected the animation state would be 'hidden-bottom'.`); }); @@ -212,7 +212,7 @@ describe('MatSnackBar', () => { let dismissObservableCompleted = false; viewContainerFixture.detectChanges(); - expect(snackBarRef.containerInstance.getAnimationState()) + expect(snackBarRef.containerInstance._animationState) .toBe('visible-bottom', `Expected the animation state would be 'visible-bottom'.`); let config2 = {viewContainerRef: testViewContainerRef}; @@ -225,9 +225,9 @@ describe('MatSnackBar', () => { viewContainerFixture.whenStable().then(() => { expect(dismissObservableCompleted).toBe(true); - expect(snackBarRef.containerInstance.getAnimationState()) + expect(snackBarRef.containerInstance._animationState) .toBe('hidden-bottom', `Expected the animation state would be 'hidden-bottom'.`); - expect(snackBarRef2.containerInstance.getAnimationState()) + expect(snackBarRef2.containerInstance._animationState) .toBe('visible-bottom', `Expected the animation state would be 'visible-bottom'.`); }); })); @@ -248,7 +248,7 @@ describe('MatSnackBar', () => { // Wait for the snackbar open animation to finish. viewContainerFixture.whenStable().then(() => { - expect(snackBarRef.containerInstance.getAnimationState()) + expect(snackBarRef.containerInstance._animationState) .toBe('visible-bottom', `Expected the animation state would be 'visible-bottom'.`); }); });