Skip to content

Commit fa4b890

Browse files
committed
fix(tooltip): throw a better error when an invalid position is passed
* 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.
1 parent cf1b4b9 commit fa4b890

File tree

3 files changed

+57
-35
lines changed

3 files changed

+57
-35
lines changed

src/lib/tooltip/tooltip-errors.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {MdError} from '../core';
2+
3+
/** Exception thrown when a tooltip has an invalid position. */
4+
export class MdTooltipInvalidPositionError extends MdError {
5+
constructor(position: string, allowedPositions: string[]) {
6+
super(
7+
`Tooltip position "${position}" is invalid. The allowed position are: "` +
8+
allowedPositions.join(', ') + '".'
9+
);
10+
}
11+
}

src/lib/tooltip/tooltip.spec.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,20 +155,27 @@ describe('MdTooltip', () => {
155155
phaseName: '',
156156
}));
157157
}));
158+
159+
it('should throw when trying to assign an invalid position', () => {
160+
expect(() => {
161+
fixture.componentInstance.position = 'everywhere';
162+
fixture.detectChanges();
163+
}).toThrowError(/Tooltip position "everywhere" is invalid/);
164+
});
158165
});
159166
});
160167

161168
@Component({
162169
selector: 'app',
163170
template: `
164171
<button *ngIf="showButton"
165-
[md-tooltip]="message"
172+
[md-tooltip]="message"
166173
[tooltip-position]="position">
167174
Button
168175
</button>`
169176
})
170177
class BasicTooltipDemo {
171-
position: TooltipPosition = 'below';
178+
position: string = 'below';
172179
message: string = initialTooltipMessage;
173180
showButton: boolean = true;
174181
}

src/lib/tooltip/tooltip.ts

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,41 @@ import {
2424
OriginConnectionPosition,
2525
OVERLAY_PROVIDERS,
2626
} from '../core';
27+
import {MdTooltipInvalidPositionError} from './tooltip-errors';
2728
import {Observable} from 'rxjs/Observable';
2829
import {Subject} from 'rxjs/Subject';
2930

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

33+
const TOOLTIP_POSITION_CONFIG: {
34+
[key: string]: {
35+
origin: OriginConnectionPosition,
36+
position: OverlayConnectionPosition,
37+
transformOrigin: string
38+
}
39+
} = {
40+
before: {
41+
origin: { originX: 'start', originY: 'center' },
42+
position: { overlayX: 'end', overlayY: 'center' },
43+
transformOrigin: 'right'
44+
},
45+
after: {
46+
origin: { originX: 'end', originY: 'center' },
47+
position: { overlayX: 'start', overlayY: 'center' },
48+
transformOrigin: 'left'
49+
},
50+
above: {
51+
origin: { originX: 'center', originY: 'top' },
52+
position: { overlayX: 'center', overlayY: 'bottom' },
53+
transformOrigin: 'bottom'
54+
},
55+
below: {
56+
origin: { originX: 'center', originY: 'bottom' },
57+
position: { overlayX: 'center', overlayY: 'top' },
58+
transformOrigin: 'top'
59+
}
60+
};
61+
3262
/** Time in ms to delay before changing the tooltip visibility to hidden */
3363
export const TOUCHEND_HIDE_DELAY = 1500;
3464

@@ -59,6 +89,10 @@ export class MdTooltip {
5989
}
6090

6191
set position(value: TooltipPosition) {
92+
if (!TOOLTIP_POSITION_CONFIG.hasOwnProperty(value)) {
93+
throw new MdTooltipInvalidPositionError(value, Object.keys(TOOLTIP_POSITION_CONFIG));
94+
}
95+
6296
if (value !== this._position) {
6397
this._position = value;
6498

@@ -136,8 +170,8 @@ export class MdTooltip {
136170

137171
/** Create the overlay config and position strategy */
138172
private _createOverlay(): void {
139-
let origin = this._getOrigin();
140-
let position = this._getOverlayPosition();
173+
let origin = TOOLTIP_POSITION_CONFIG[this.position].origin;
174+
let position = TOOLTIP_POSITION_CONFIG[this.position].position;
141175
let strategy = this._overlay.position().connectedTo(this._elementRef, origin, position);
142176
let config = new OverlayState();
143177
config.positionStrategy = strategy;
@@ -152,26 +186,6 @@ export class MdTooltip {
152186
this._tooltipInstance = null;
153187
}
154188

155-
/** Returns the origin position based on the user's position preference */
156-
private _getOrigin(): OriginConnectionPosition {
157-
switch (this.position) {
158-
case 'before': return { originX: 'start', originY: 'center' };
159-
case 'after': return { originX: 'end', originY: 'center' };
160-
case 'above': return { originX: 'center', originY: 'top' };
161-
case 'below': return { originX: 'center', originY: 'bottom' };
162-
}
163-
}
164-
165-
/** Returns the overlay position based on the user's preference */
166-
private _getOverlayPosition(): OverlayConnectionPosition {
167-
switch (this.position) {
168-
case 'before': return { overlayX: 'end', overlayY: 'center' };
169-
case 'after': return { overlayX: 'start', overlayY: 'center' };
170-
case 'above': return { overlayX: 'center', overlayY: 'bottom' };
171-
case 'below': return { overlayX: 'center', overlayY: 'top' };
172-
}
173-
}
174-
175189
/** Updates the tooltip message and repositions the overlay according to the new message length */
176190
private _setTooltipMessage(message: string) {
177191
// Must wait for the message to be painted to the tooltip so that the overlay can properly
@@ -228,7 +242,7 @@ export class TooltipComponent {
228242
show(position: TooltipPosition): void {
229243
this._closeOnInteraction = false;
230244
this._visibility = 'visible';
231-
this._setTransformOrigin(position);
245+
this._transformOrigin = TOOLTIP_POSITION_CONFIG[position].transformOrigin;
232246

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

261-
/** Sets the tooltip transform origin according to the tooltip position */
262-
_setTransformOrigin(value: TooltipPosition) {
263-
switch (value) {
264-
case 'before': this._transformOrigin = 'right'; break;
265-
case 'after': this._transformOrigin = 'left'; break;
266-
case 'above': this._transformOrigin = 'bottom'; break;
267-
case 'below': this._transformOrigin = 'top'; break;
268-
}
269-
}
270-
271275
_afterVisibilityAnimation(e: AnimationTransitionEvent): void {
272276
if (e.toState === 'hidden' && !this.isVisible()) {
273277
this._onHide.next();

0 commit comments

Comments
 (0)