fix(tooltip): not working properly with ChangeDetectionStrategy.OnPush (#2721)

Fixes #2713
This commit is contained in:
Thomas Pink
2017-01-31 23:28:26 +01:00
committed by Kara
parent 08e9d70414
commit 632b964b0d
3 changed files with 89 additions and 5 deletions
+2 -1
View File
@@ -1,4 +1,4 @@
import {Component} from '@angular/core';
import {Component, ChangeDetectionStrategy} from '@angular/core';
import {TooltipPosition} from '@angular/material';
@@ -7,6 +7,7 @@ import {TooltipPosition} from '@angular/material';
selector: 'tooltip-demo',
templateUrl: 'tooltip-demo.html',
styleUrls: ['tooltip-demo.css'],
changeDetection: ChangeDetectionStrategy.OnPush // make sure tooltip also works OnPush
})
export class TooltipDemo {
position: TooltipPosition = 'below';
+76 -2
View File
@@ -6,7 +6,12 @@ import {
fakeAsync,
flushMicrotasks
} from '@angular/core/testing';
import {Component, DebugElement, AnimationTransitionEvent} from '@angular/core';
import {
Component,
DebugElement,
AnimationTransitionEvent,
ChangeDetectionStrategy
} from '@angular/core';
import {By} from '@angular/platform-browser';
import {TooltipPosition, MdTooltip, MdTooltipModule} from './tooltip';
import {OverlayContainer} from '../core';
@@ -22,7 +27,7 @@ describe('MdTooltip', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdTooltipModule.forRoot(), OverlayModule],
declarations: [BasicTooltipDemo],
declarations: [BasicTooltipDemo, OnPushTooltipDemo],
providers: [
{provide: OverlayContainer, useFactory: () => {
overlayContainerElement = document.createElement('div');
@@ -59,6 +64,15 @@ describe('MdTooltip', () => {
expect(tooltipDirective._isTooltipVisible()).toBe(true);
fixture.detectChanges();
// wait till animation has finished
tick(500);
// Make sure tooltip is shown to the user and animation has finished
const tooltipElement = overlayContainerElement.querySelector('.md-tooltip') as HTMLElement;
expect(tooltipElement instanceof HTMLElement).toBe(true);
expect(tooltipElement.style.transform).toBe('scale(1)');
expect(overlayContainerElement.textContent).toContain(initialTooltipMessage);
// After hide called, a timeout delay is created that will to hide the tooltip.
@@ -297,6 +311,53 @@ describe('MdTooltip', () => {
}).toThrowError('Tooltip position "everywhere" is invalid.');
});
});
describe('with OnPush', () => {
let fixture: ComponentFixture<OnPushTooltipDemo>;
let buttonDebugElement: DebugElement;
let buttonElement: HTMLButtonElement;
let tooltipDirective: MdTooltip;
beforeEach(() => {
fixture = TestBed.createComponent(OnPushTooltipDemo);
fixture.detectChanges();
buttonDebugElement = fixture.debugElement.query(By.css('button'));
buttonElement = <HTMLButtonElement> buttonDebugElement.nativeElement;
tooltipDirective = buttonDebugElement.injector.get(MdTooltip);
});
it('should show and hide the tooltip', fakeAsync(() => {
expect(tooltipDirective._tooltipInstance).toBeUndefined();
tooltipDirective.show();
tick(0); // Tick for the show delay (default is 0)
expect(tooltipDirective._isTooltipVisible()).toBe(true);
fixture.detectChanges();
// wait till animation has finished
tick(500);
// Make sure tooltip is shown to the user and animation has finished
const tooltipElement = overlayContainerElement.querySelector('.md-tooltip') as HTMLElement;
expect(tooltipElement instanceof HTMLElement).toBe(true);
expect(tooltipElement.style.transform).toBe('scale(1)');
// After hide called, a timeout delay is created that will to hide the tooltip.
const tooltipDelay = 1000;
tooltipDirective.hide(tooltipDelay);
expect(tooltipDirective._isTooltipVisible()).toBe(true);
// After the tooltip delay elapses, expect that the tooltip is not visible.
tick(tooltipDelay);
fixture.detectChanges();
expect(tooltipDirective._isTooltipVisible()).toBe(false);
// On animation complete, should expect that the tooltip has been detached.
flushMicrotasks();
expect(tooltipDirective._tooltipInstance).toBeNull();
}));
});
});
@Component({
@@ -313,4 +374,17 @@ class BasicTooltipDemo {
message: string = initialTooltipMessage;
showButton: boolean = true;
}
@Component({
selector: 'app',
template: `
<button [mdTooltip]="message"
[mdTooltipPosition]="position">
Button
</button>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
class OnPushTooltipDemo {
position: string = 'below';
message: string = initialTooltipMessage;
}
+11 -2
View File
@@ -14,7 +14,8 @@ import {
AnimationTransitionEvent,
NgZone,
Optional,
OnDestroy
OnDestroy,
ChangeDetectorRef
} from '@angular/core';
import {
Overlay,
@@ -306,7 +307,7 @@ export class TooltipComponent {
/** Subject for notifying that the tooltip has been hidden from the view */
private _onHide: Subject<any> = new Subject();
constructor(@Optional() private _dir: Dir) {}
constructor(@Optional() private _dir: Dir, private _changeDetectorRef: ChangeDetectorRef) {}
/**
* Shows the tooltip with an animation originating from the provided origin
@@ -329,6 +330,10 @@ export class TooltipComponent {
// If this was set to true immediately, then a body click that triggers show() would
// trigger interaction and close the tooltip right after it was displayed.
this._closeOnInteraction = false;
// Mark for check so if any parent component has set the
// ChangeDetectionStrategy to OnPush it will be checked anyways
this._changeDetectorRef.markForCheck();
setTimeout(() => { this._closeOnInteraction = true; }, 0);
}, delay);
}
@@ -346,6 +351,10 @@ export class TooltipComponent {
this._hideTimeoutId = setTimeout(() => {
this._visibility = 'hidden';
this._closeOnInteraction = false;
// Mark for check so if any parent component has set the
// ChangeDetectionStrategy to OnPush it will be checked anyways
this._changeDetectorRef.markForCheck();
}, delay);
}