diff --git a/src/lib/tooltip/tooltip-errors.ts b/src/lib/tooltip/tooltip-errors.ts new file mode 100644 index 000000000000..01f473fb78c5 --- /dev/null +++ b/src/lib/tooltip/tooltip-errors.ts @@ -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(', ') + '".' + ); + } +} diff --git a/src/lib/tooltip/tooltip.spec.ts b/src/lib/tooltip/tooltip.spec.ts index 05e894fc504c..769edbca2dbc 100644 --- a/src/lib/tooltip/tooltip.spec.ts +++ b/src/lib/tooltip/tooltip.spec.ts @@ -155,6 +155,13 @@ 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/); + }); }); }); @@ -162,13 +169,13 @@ describe('MdTooltip', () => { selector: 'app', template: ` ` }) class BasicTooltipDemo { - position: TooltipPosition = 'below'; + position: string = 'below'; message: string = initialTooltipMessage; showButton: boolean = true; } diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts index c08dc93a3a7f..1888d0c8f623 100644 --- a/src/lib/tooltip/tooltip.ts +++ b/src/lib/tooltip/tooltip.ts @@ -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; @@ -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; @@ -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; @@ -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 @@ -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) { @@ -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();