Skip to content

Commit 9c0d82a

Browse files
andrewseguinjelbourn
authored andcommitted
fix(tooltip): check tooltip disposed on animation hidden (#1816)
1 parent 77701cc commit 9c0d82a

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

src/lib/tooltip/tooltip.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<div class="md-tooltip"
22
[style.transform-origin]="_transformOrigin"
33
[@state]="_visibility"
4-
(@state.done)="this._afterVisibilityAnimation($event)">
4+
(@state.done)="_afterVisibilityAnimation($event)">
55
{{message}}
66
</div>

src/lib/tooltip/tooltip.spec.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
async, ComponentFixture, TestBed, tick, fakeAsync,
33
flushMicrotasks
44
} from '@angular/core/testing';
5-
import {Component, DebugElement} from '@angular/core';
5+
import {Component, DebugElement, AnimationTransitionEvent} from '@angular/core';
66
import {By} from '@angular/platform-browser';
77
import {TooltipPosition, MdTooltip, TOOLTIP_HIDE_DELAY, MdTooltipModule} from './tooltip';
88
import {OverlayContainer} from '../core';
@@ -123,14 +123,45 @@ describe('MdTooltip', () => {
123123
expect(overlayContainerElement.childNodes.length).toBe(0);
124124
expect(overlayContainerElement.textContent).toBe('');
125125
});
126+
127+
it('should not try to dispose the tooltip when destroyed and done hiding', fakeAsync(() => {
128+
tooltipDirective.show();
129+
fixture.detectChanges();
130+
tick(150);
131+
132+
tooltipDirective.hide();
133+
tick(TOOLTIP_HIDE_DELAY); // Change the tooltip state to hidden and trigger animation start
134+
135+
// Store the tooltip instance, which will be set to null after the button is hidden.
136+
const tooltipInstance = tooltipDirective._tooltipInstance;
137+
fixture.componentInstance.showButton = false;
138+
fixture.detectChanges();
139+
140+
// At this point the animation should be able to complete itself and trigger the
141+
// _afterVisibilityAnimation function, but for unknown reasons in the test infrastructure,
142+
// this does not occur. Manually call this and verify that doing so does not
143+
// throw an error.
144+
tooltipInstance._afterVisibilityAnimation(new AnimationTransitionEvent({
145+
fromState: 'visible',
146+
toState: 'hidden',
147+
totalTime: 150,
148+
phaseName: '',
149+
}));
150+
}));
126151
});
127152
});
128153

129154
@Component({
130155
selector: 'app',
131-
template: `<button [md-tooltip]="message" [tooltip-position]="position">Button</button>`
156+
template: `
157+
<button *ngIf="showButton"
158+
[md-tooltip]="message"
159+
[tooltip-position]="position">
160+
Button
161+
</button>`
132162
})
133163
class BasicTooltipDemo {
134164
position: TooltipPosition = 'below';
135165
message: string = initialTooltipMessage;
166+
showButton: boolean = true;
136167
}

src/lib/tooltip/tooltip.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ export class MdTooltip {
129129

130130
// Dispose the overlay when finished the shown tooltip.
131131
this._tooltipInstance.afterHidden().subscribe(() => {
132-
this._disposeTooltip();
132+
// Check first if the tooltip has already been removed through this components destroy.
133+
if (this._tooltipInstance) {
134+
this._disposeTooltip();
135+
}
133136
});
134137
}
135138

@@ -270,7 +273,6 @@ export class TooltipComponent {
270273
_afterVisibilityAnimation(e: AnimationTransitionEvent): void {
271274
if (e.toState === 'hidden' && !this.isVisible()) {
272275
this._onHide.next();
273-
274276
}
275277
}
276278

0 commit comments

Comments
 (0)