Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tooltip): add input for delaying show and hide #2101

Merged
merged 5 commits into from
Dec 9, 2016
Merged
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
16 changes: 14 additions & 2 deletions src/demo-app/tooltip/tooltip-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ <h1>Tooltip Demo</h1>
md-raised-button
color="primary"
[md-tooltip]="message"
[tooltip-position]="position">
[tooltip-position]="position"
[tooltipShowDelay]="showDelay"
[tooltipHideDelay]="hideDelay">
Mouse over to see the tooltip
</button>
</p>
Expand All @@ -27,11 +29,21 @@ <h1>Tooltip Demo</h1>
<md-input type="text" [(ngModel)]="message"></md-input>
</p>

<p>
<strong>Show Delay (ms): </strong>
<md-input type="number" [(ngModel)]="showDelay"></md-input>
</p>

<p>
<strong>Hide Delay (ms): </strong>
<md-input type="number" [(ngModel)]="hideDelay"></md-input>
</p>

<strong>Mouse over to</strong>
<button md-raised-button color="primary" (mouseenter)="tooltip.show()">
Show tooltip
</button>
<button md-raised-button color="primary" (mouseenter)="tooltip.hide(0)">
<button md-raised-button color="primary" (mouseenter)="tooltip.hide()">
Hide tooltip
</button>
<button md-raised-button color="primary" (mouseenter)="tooltip.toggle()">
Expand Down
2 changes: 2 additions & 0 deletions src/demo-app/tooltip/tooltip-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ import {TooltipPosition} from '@angular/material';
export class TooltipDemo {
position: TooltipPosition = 'below';
message: string = 'Here is the tooltip';
showDelay = 0;
hideDelay = 0;
}
46 changes: 40 additions & 6 deletions src/lib/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('MdTooltip', () => {
expect(tooltipDirective._tooltipInstance).toBeUndefined();

tooltipDirective.show();
tick(0); // Tick for the show delay (default is 0)
expect(tooltipDirective._isTooltipVisible()).toBe(true);

fixture.detectChanges();
Expand All @@ -74,7 +75,37 @@ describe('MdTooltip', () => {
expect(tooltipDirective._tooltipInstance).toBeNull();
}));

it('should not show tooltip if message is not present or empty', fakeAsync(() => {
it('should show with delay', fakeAsync(() => {
expect(tooltipDirective._tooltipInstance).toBeUndefined();

const tooltipDelay = 1000;
tooltipDirective.show(tooltipDelay);
expect(tooltipDirective._isTooltipVisible()).toBe(false);

fixture.detectChanges();
expect(overlayContainerElement.textContent).toContain('');

tick(tooltipDelay);
expect(tooltipDirective._isTooltipVisible()).toBe(true);
expect(overlayContainerElement.textContent).toContain(initialTooltipMessage);
}));

it('should not show if hide is called before delay finishes', fakeAsync(() => {
expect(tooltipDirective._tooltipInstance).toBeUndefined();

const tooltipDelay = 1000;
tooltipDirective.show(tooltipDelay);
expect(tooltipDirective._isTooltipVisible()).toBe(false);

fixture.detectChanges();
expect(overlayContainerElement.textContent).toContain('');

tooltipDirective.hide();
tick(tooltipDelay);
expect(tooltipDirective._isTooltipVisible()).toBe(false);
}));

it('should not show tooltip if message is not present or empty', () => {
expect(tooltipDirective._tooltipInstance).toBeUndefined();

tooltipDirective.message = undefined;
Expand All @@ -96,10 +127,11 @@ describe('MdTooltip', () => {
fixture.detectChanges();
tooltipDirective.show();
expect(tooltipDirective._tooltipInstance).toBeUndefined();
}));
});

it('should not follow through with hide if show is called after', fakeAsync(() => {
tooltipDirective.show();
tick(0); // Tick for the show delay (default is 0)
expect(tooltipDirective._isTooltipVisible()).toBe(true);

// After hide called, a timeout delay is created that will to hide the tooltip.
Expand Down Expand Up @@ -133,10 +165,11 @@ describe('MdTooltip', () => {
expect(tooltipDirective._overlayRef).toBeNull();
});

it('should be able to modify the tooltip message', () => {
it('should be able to modify the tooltip message', fakeAsync(() => {
expect(tooltipDirective._tooltipInstance).toBeUndefined();

tooltipDirective.show();
tick(0); // Tick for the show delay (default is 0)
expect(tooltipDirective._tooltipInstance._visibility).toBe('visible');

fixture.detectChanges();
Expand All @@ -147,16 +180,17 @@ describe('MdTooltip', () => {

fixture.detectChanges();
expect(overlayContainerElement.textContent).toContain(newMessage);
});
}));

it('should be removed after parent destroyed', () => {
it('should be removed after parent destroyed', fakeAsync(() => {
tooltipDirective.show();
tick(0); // Tick for the show delay (default is 0)
expect(tooltipDirective._isTooltipVisible()).toBe(true);

fixture.destroy();
expect(overlayContainerElement.childNodes.length).toBe(0);
expect(overlayContainerElement.textContent).toBe('');
});
}));

it('should not try to dispose the tooltip when destroyed and done hiding', fakeAsync(() => {
tooltipDirective.show();
Expand Down
54 changes: 36 additions & 18 deletions src/lib/tooltip/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ export class MdTooltip {
}
}

/** The default delay in ms before showing the tooltip after show is called */
@Input('tooltipShowDelay') showDelay = 0;

/** The default delay in ms before hiding the tooltip after hide is called */
@Input('tooltipHideDelay') hideDelay = 0;

/** The message to be displayed in the tooltip */
private _message: string;
@Input('md-tooltip') get message() {
Expand All @@ -97,22 +103,20 @@ export class MdTooltip {
}
}

/** Shows the tooltip */
show(): void {
if (!this._message || !this._message.trim()) {
return;
}
/** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */
show(delay: number = this.showDelay): void {
if (!this._message || !this._message.trim()) { return; }

if (!this._tooltipInstance) {
this._createTooltip();
}

this._setTooltipMessage(this._message);
this._tooltipInstance.show(this._position);
this._tooltipInstance.show(this._position, delay);
}

/** Hides the tooltip after the provided delay in ms, defaulting to 0ms. */
hide(delay = 0): void {
/** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */
hide(delay: number = this.hideDelay): void {
if (this._tooltipInstance) {
this._tooltipInstance.hide(delay);
}
Expand Down Expand Up @@ -222,7 +226,7 @@ export class MdTooltip {
}
}

export type TooltipVisibility = 'visible' | 'hidden';
export type TooltipVisibility = 'initial' | 'visible' | 'hidden';

@Component({
moduleId: module.id,
Expand All @@ -232,6 +236,7 @@ export type TooltipVisibility = 'visible' | 'hidden';
animations: [
trigger('state', [
state('void', style({transform: 'scale(0)'})),
state('initial', style({transform: 'scale(0)'})),
state('visible', style({transform: 'scale(1)'})),
state('hidden', style({transform: 'scale(0)'})),
transition('* => visible', animate('150ms cubic-bezier(0.0, 0.0, 0.2, 1)')),
Expand All @@ -246,11 +251,14 @@ export class TooltipComponent {
/** Message to display in the tooltip */
message: string;

/** The timeout ID of any current timer set to show the tooltip */
_showTimeoutId: number;

/** The timeout ID of any current timer set to hide the tooltip */
_hideTimeoutId: number;

/** Property watched by the animation framework to show or hide the tooltip */
_visibility: TooltipVisibility;
_visibility: TooltipVisibility = 'initial';

/** Whether interactions on the page should close the tooltip */
_closeOnInteraction: boolean = false;
Expand All @@ -264,23 +272,33 @@ export class TooltipComponent {
constructor(@Optional() private _dir: Dir) {}

/** Shows the tooltip with an animation originating from the provided origin */
show(position: TooltipPosition): void {
this._closeOnInteraction = false;
this._visibility = 'visible';
this._setTransformOrigin(position);

show(position: TooltipPosition, delay: number): void {
// Cancel the delayed hide if it is scheduled
if (this._hideTimeoutId) {
clearTimeout(this._hideTimeoutId);
}

// If this was set to true immediately, then the body click would trigger interaction and
// close the tooltip right after it was displayed.
setTimeout(() => { this._closeOnInteraction = true; }, 0);
// Body interactions should cancel the tooltip if there is a delay in showing.
this._closeOnInteraction = true;

this._setTransformOrigin(position);
this._showTimeoutId = setTimeout(() => {
this._visibility = 'visible';

// 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;
setTimeout(() => { this._closeOnInteraction = true; }, 0);
}, delay);
}

/** Begins the animation to hide the tooltip after the provided delay in ms */
hide(delay: number): void {
// Cancel the delayed show if it is scheduled
if (this._showTimeoutId) {
clearTimeout(this._showTimeoutId);
}

this._hideTimeoutId = setTimeout(() => {
this._visibility = 'hidden';
this._closeOnInteraction = false;
Expand Down