Skip to content

Commit

Permalink
fix(tooltip): throw a better error when an invalid position is passed
Browse files Browse the repository at this point in the history
* Throws a more informative error message when an invalid position is passed to the tooltip. Previously it would throw something along the lines of `Cannot read property 'originX' of undefined`.
* Moves the various tooltip switch statements to use an enum for easier validation.

Referencing #1959.
  • Loading branch information
crisbeto committed Nov 25, 2016
1 parent cf1b4b9 commit fa4b890
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 35 deletions.
11 changes: 11 additions & 0 deletions src/lib/tooltip/tooltip-errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {MdError} from '../core';

/** Exception thrown when a tooltip has an invalid position. */
export class MdTooltipInvalidPositionError extends MdError {
constructor(position: string, allowedPositions: string[]) {
super(
`Tooltip position "${position}" is invalid. The allowed position are: "` +
allowedPositions.join(', ') + '".'
);
}
}
11 changes: 9 additions & 2 deletions src/lib/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,20 +155,27 @@ describe('MdTooltip', () => {
phaseName: '',
}));
}));

it('should throw when trying to assign an invalid position', () => {
expect(() => {
fixture.componentInstance.position = 'everywhere';
fixture.detectChanges();
}).toThrowError(/Tooltip position "everywhere" is invalid/);
});
});
});

@Component({
selector: 'app',
template: `
<button *ngIf="showButton"
[md-tooltip]="message"
[md-tooltip]="message"
[tooltip-position]="position">
Button
</button>`
})
class BasicTooltipDemo {
position: TooltipPosition = 'below';
position: string = 'below';
message: string = initialTooltipMessage;
showButton: boolean = true;
}
70 changes: 37 additions & 33 deletions src/lib/tooltip/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,41 @@ import {
OriginConnectionPosition,
OVERLAY_PROVIDERS,
} from '../core';
import {MdTooltipInvalidPositionError} from './tooltip-errors';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';

export type TooltipPosition = 'before' | 'after' | 'above' | 'below';

const TOOLTIP_POSITION_CONFIG: {
[key: string]: {
origin: OriginConnectionPosition,
position: OverlayConnectionPosition,
transformOrigin: string
}
} = {
before: {
origin: { originX: 'start', originY: 'center' },
position: { overlayX: 'end', overlayY: 'center' },
transformOrigin: 'right'
},
after: {
origin: { originX: 'end', originY: 'center' },
position: { overlayX: 'start', overlayY: 'center' },
transformOrigin: 'left'
},
above: {
origin: { originX: 'center', originY: 'top' },
position: { overlayX: 'center', overlayY: 'bottom' },
transformOrigin: 'bottom'
},
below: {
origin: { originX: 'center', originY: 'bottom' },
position: { overlayX: 'center', overlayY: 'top' },
transformOrigin: 'top'
}
};

/** Time in ms to delay before changing the tooltip visibility to hidden */
export const TOUCHEND_HIDE_DELAY = 1500;

Expand Down Expand Up @@ -59,6 +89,10 @@ export class MdTooltip {
}

set position(value: TooltipPosition) {
if (!TOOLTIP_POSITION_CONFIG.hasOwnProperty(value)) {
throw new MdTooltipInvalidPositionError(value, Object.keys(TOOLTIP_POSITION_CONFIG));
}

if (value !== this._position) {
this._position = value;

Expand Down Expand Up @@ -136,8 +170,8 @@ export class MdTooltip {

/** Create the overlay config and position strategy */
private _createOverlay(): void {
let origin = this._getOrigin();
let position = this._getOverlayPosition();
let origin = TOOLTIP_POSITION_CONFIG[this.position].origin;
let position = TOOLTIP_POSITION_CONFIG[this.position].position;
let strategy = this._overlay.position().connectedTo(this._elementRef, origin, position);
let config = new OverlayState();
config.positionStrategy = strategy;
Expand All @@ -152,26 +186,6 @@ export class MdTooltip {
this._tooltipInstance = null;
}

/** Returns the origin position based on the user's position preference */
private _getOrigin(): OriginConnectionPosition {
switch (this.position) {
case 'before': return { originX: 'start', originY: 'center' };
case 'after': return { originX: 'end', originY: 'center' };
case 'above': return { originX: 'center', originY: 'top' };
case 'below': return { originX: 'center', originY: 'bottom' };
}
}

/** Returns the overlay position based on the user's preference */
private _getOverlayPosition(): OverlayConnectionPosition {
switch (this.position) {
case 'before': return { overlayX: 'end', overlayY: 'center' };
case 'after': return { overlayX: 'start', overlayY: 'center' };
case 'above': return { overlayX: 'center', overlayY: 'bottom' };
case 'below': return { overlayX: 'center', overlayY: 'top' };
}
}

/** Updates the tooltip message and repositions the overlay according to the new message length */
private _setTooltipMessage(message: string) {
// Must wait for the message to be painted to the tooltip so that the overlay can properly
Expand Down Expand Up @@ -228,7 +242,7 @@ export class TooltipComponent {
show(position: TooltipPosition): void {
this._closeOnInteraction = false;
this._visibility = 'visible';
this._setTransformOrigin(position);
this._transformOrigin = TOOLTIP_POSITION_CONFIG[position].transformOrigin;

// Cancel the delayed hide if it is scheduled
if (this._hideTimeoutId) {
Expand Down Expand Up @@ -258,16 +272,6 @@ export class TooltipComponent {
return this._visibility === 'visible';
}

/** Sets the tooltip transform origin according to the tooltip position */
_setTransformOrigin(value: TooltipPosition) {
switch (value) {
case 'before': this._transformOrigin = 'right'; break;
case 'after': this._transformOrigin = 'left'; break;
case 'above': this._transformOrigin = 'bottom'; break;
case 'below': this._transformOrigin = 'top'; break;
}
}

_afterVisibilityAnimation(e: AnimationTransitionEvent): void {
if (e.toState === 'hidden' && !this.isVisible()) {
this._onHide.next();
Expand Down

0 comments on commit fa4b890

Please sign in to comment.