diff --git a/src/demo-app/tooltip/tooltip-demo.html b/src/demo-app/tooltip/tooltip-demo.html
index c96a266f0be..6da3a8b8efc 100644
--- a/src/demo-app/tooltip/tooltip-demo.html
+++ b/src/demo-app/tooltip/tooltip-demo.html
@@ -15,6 +15,8 @@
Below
Above
+ Left
+ Right
Before
After
diff --git a/src/lib/tooltip/README.md b/src/lib/tooltip/README.md
index a13bd047a58..033404c7528 100644
--- a/src/lib/tooltip/README.md
+++ b/src/lib/tooltip/README.md
@@ -1,24 +1,28 @@
# MdTooltip
-Tooltip allows the user to specify text to be displayed when the mouse hover over an element.
+Tooltip allows the user to specify text to be displayed when the mouse hovers over an element.
+
+The positions `before` and `after` should be used instead of `left` and `right` respectively when the tooltip positioning should change depending on the HTML dir global attribute for left-to-right or right-to-left layout.
### Examples
A button with a tooltip
```html
-
+
```
+
+
## `[md-tooltip]`
### Properties
| Name | Type | Description |
| --- | --- | --- |
| `md-tooltip` | `string` | The message to be displayed. |
-| `tooltip-position` | `"above"|"below"|"before"|"after"` | The position of the tooltip. |
+| `tooltip-position` | `"before"|"after"|"above"|"below"|"left"|"right"` | The position of the tooltip. |
### Methods
| Name | Description |
-| --- | --- | --- |
+| --- | --- |
| `show` | Displays the tooltip. |
| `hide` | Removes the tooltip. |
| `toggle` | Displays or hides the tooltip. |
diff --git a/src/lib/tooltip/tooltip.spec.ts b/src/lib/tooltip/tooltip.spec.ts
index 05e894fc504..6421cde3f52 100644
--- a/src/lib/tooltip/tooltip.spec.ts
+++ b/src/lib/tooltip/tooltip.spec.ts
@@ -10,12 +10,13 @@ import {Component, DebugElement, AnimationTransitionEvent} from '@angular/core';
import {By} from '@angular/platform-browser';
import {TooltipPosition, MdTooltip, MdTooltipModule} from './tooltip';
import {OverlayContainer} from '../core';
+import {Dir, LayoutDirection} from '../core/rtl/dir';
const initialTooltipMessage = 'initial tooltip message';
describe('MdTooltip', () => {
let overlayContainerElement: HTMLElement;
-
+ let dir: {value: LayoutDirection};
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -25,6 +26,9 @@ describe('MdTooltip', () => {
{provide: OverlayContainer, useFactory: () => {
overlayContainerElement = document.createElement('div');
return {getContainerElement: () => overlayContainerElement};
+ }},
+ {provide: Dir, useFactory: () => {
+ return dir = { value: 'ltr' };
}}
]
});
@@ -155,6 +159,76 @@ describe('MdTooltip', () => {
phaseName: '',
}));
}));
+
+ it('should consistently position before and after overlay origin in ltr and rtl dir', () => {
+ tooltipDirective.position = 'left';
+ const leftOrigin = tooltipDirective._getOrigin();
+ tooltipDirective.position = 'right';
+ const rightOrigin = tooltipDirective._getOrigin();
+
+ // Test expectations in LTR
+ tooltipDirective.position = 'before';
+ expect(tooltipDirective._getOrigin()).toEqual(leftOrigin);
+ tooltipDirective.position = 'after';
+ expect(tooltipDirective._getOrigin()).toEqual(rightOrigin);
+
+ // Test expectations in LTR
+ dir.value = 'rtl';
+ tooltipDirective.position = 'before';
+ expect(tooltipDirective._getOrigin()).toEqual(rightOrigin);
+ tooltipDirective.position = 'after';
+ expect(tooltipDirective._getOrigin()).toEqual(leftOrigin);
+ });
+
+ it('should consistently position before and after overlay position in ltr and rtl dir', () => {
+ tooltipDirective.position = 'left';
+ const leftOverlayPosition = tooltipDirective._getOverlayPosition();
+ tooltipDirective.position = 'right';
+ const rightOverlayPosition = tooltipDirective._getOverlayPosition();
+
+ // Test expectations in LTR
+ tooltipDirective.position = 'before';
+ expect(tooltipDirective._getOverlayPosition()).toEqual(leftOverlayPosition);
+ tooltipDirective.position = 'after';
+ expect(tooltipDirective._getOverlayPosition()).toEqual(rightOverlayPosition);
+
+ // Test expectations in LTR
+ dir.value = 'rtl';
+ tooltipDirective.position = 'before';
+ expect(tooltipDirective._getOverlayPosition()).toEqual(rightOverlayPosition);
+ tooltipDirective.position = 'after';
+ expect(tooltipDirective._getOverlayPosition()).toEqual(leftOverlayPosition);
+ });
+
+ it('should have consistent left transform origin in any dir', () => {
+ tooltipDirective.position = 'right';
+ tooltipDirective.show();
+ expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('left');
+
+ tooltipDirective.position = 'after';
+ tooltipDirective.show();
+ expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('left');
+
+ dir.value = 'rtl';
+ tooltipDirective.position = 'before';
+ tooltipDirective.show();
+ expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('left');
+ });
+
+ it('should have consistent right transform origin in any dir', () => {
+ tooltipDirective.position = 'left';
+ tooltipDirective.show();
+ expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('right');
+
+ tooltipDirective.position = 'before';
+ tooltipDirective.show();
+ expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('right');
+
+ dir.value = 'rtl';
+ tooltipDirective.position = 'after';
+ tooltipDirective.show();
+ expect(tooltipDirective._tooltipInstance._transformOrigin).toBe('right');
+ });
});
});
diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts
index a9621d78840..cd46c7891f3 100644
--- a/src/lib/tooltip/tooltip.ts
+++ b/src/lib/tooltip/tooltip.ts
@@ -13,6 +13,7 @@ import {
animate,
AnimationTransitionEvent,
NgZone,
+ Optional,
} from '@angular/core';
import {
Overlay,
@@ -27,9 +28,9 @@ import {
} from '../core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
+import {Dir} from '../core/rtl/dir';
-
-export type TooltipPosition = 'before' | 'after' | 'above' | 'below';
+export type TooltipPosition = 'left' | 'right' | 'above' | 'below' | 'before' | 'after';
/** Time in ms to delay before changing the tooltip visibility to hidden */
export const TOUCHEND_HIDE_DELAY = 1500;
@@ -85,7 +86,8 @@ export class MdTooltip {
}
constructor(private _overlay: Overlay, private _elementRef: ElementRef,
- private _viewContainerRef: ViewContainerRef, private _ngZone: NgZone) {}
+ private _viewContainerRef: ViewContainerRef, private _ngZone: NgZone,
+ @Optional() private _dir: Dir) {}
/** Dispose the tooltip when destroyed */
ngOnDestroy() {
@@ -155,22 +157,46 @@ export class MdTooltip {
}
/** 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' };
+ _getOrigin(): OriginConnectionPosition {
+ if (this.position == 'above' || this.position == 'below') {
+ return {originX: 'center', originY: this.position == 'above' ? 'top' : 'bottom'};
+ }
+
+ const isDirectionLtr = !this._dir || this._dir.value == 'ltr';
+ if (this.position == 'left' ||
+ this.position == 'before' && isDirectionLtr ||
+ this.position == 'after' && !isDirectionLtr) {
+ return {originX: 'start', originY: 'center'};
+ }
+
+ if (this.position == 'right' ||
+ this.position == 'after' && isDirectionLtr ||
+ this.position == 'before' && !isDirectionLtr) {
+ return {originX: 'end', originY: 'center'};
}
}
/** 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' };
+ _getOverlayPosition(): OverlayConnectionPosition {
+ if (this.position == 'above') {
+ return {overlayX: 'center', overlayY: 'bottom'};
+ }
+
+ if (this.position == 'below') {
+ return {overlayX: 'center', overlayY: 'top'};
+ }
+
+ const isLtr = !this._dir || this._dir.value == 'ltr';
+ if (this.position == 'left' ||
+ this.position == 'before' && isLtr ||
+ this.position == 'after' && !isLtr) {
+ return {overlayX: 'end', overlayY: 'center'};
+ }
+
+ if (this.position == 'right' ||
+ this.position == 'after' && isLtr ||
+ this.position == 'before' && !isLtr) {
+ return {overlayX: 'start', overlayY: 'center'};
}
}
@@ -226,6 +252,8 @@ export class TooltipComponent {
/** Subject for notifying that the tooltip has been hidden from the view */
private _onHide: Subject = new Subject();
+ constructor(@Optional() private _dir: Dir) {}
+
/** Shows the tooltip with an animation originating from the provided origin */
show(position: TooltipPosition): void {
this._closeOnInteraction = false;
@@ -262,11 +290,14 @@ export class TooltipComponent {
/** Sets the tooltip transform origin according to the tooltip position */
_setTransformOrigin(value: TooltipPosition) {
+ const isLtr = !this._dir || this._dir.value == 'ltr';
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;
+ case 'before': this._transformOrigin = isLtr ? 'right' : 'left'; break;
+ case 'after': this._transformOrigin = isLtr ? 'left' : 'right'; break;
+ case 'left': this._transformOrigin = 'right'; break;
+ case 'right': this._transformOrigin = 'left'; break;
+ case 'above': this._transformOrigin = 'bottom'; break;
+ case 'below': this._transformOrigin = 'top'; break;
}
}