Skip to content

feat(tooltip): remove Hammer.js dependency #17003

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

Merged
merged 1 commit into from
Oct 1, 2019
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
1 change: 0 additions & 1 deletion src/material/tooltip/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ ng_module(
"@npm//@angular/animations",
"@npm//@angular/common",
"@npm//@angular/core",
"@npm//@angular/platform-browser",
"@npm//rxjs",
],
)
Expand Down
8 changes: 2 additions & 6 deletions src/material/tooltip/tooltip-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {OverlayModule} from '@angular/cdk/overlay';
import {A11yModule} from '@angular/cdk/a11y';
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
import {GestureConfig, MatCommonModule} from '@angular/material/core';
import {HAMMER_GESTURE_CONFIG} from '@angular/platform-browser';
import {MatCommonModule} from '@angular/material/core';
import {
MatTooltip,
TooltipComponent,
Expand All @@ -28,9 +27,6 @@ import {
exports: [MatTooltip, TooltipComponent, MatCommonModule],
declarations: [MatTooltip, TooltipComponent],
entryComponents: [TooltipComponent],
providers: [
MAT_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER,
{provide: HAMMER_GESTURE_CONFIG, useClass: GestureConfig},
]
providers: [MAT_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER]
})
export class MatTooltipModule {}
10 changes: 4 additions & 6 deletions src/material/tooltip/tooltip.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@ the positions `before` and `after` should be used instead of `left` and `right`,
### Showing and hiding

By default, the tooltip will be immediately shown when the user's mouse hovers over the tooltip's
trigger element and immediately hides when the user's mouse leaves.
trigger element and immediately hides when the user's mouse leaves.

On mobile, the tooltip is displayed when the user longpresses the element and hides after a
delay of 1500ms. The longpress behavior requires HammerJS to be loaded on the page. To learn more
about adding HammerJS to your app, check out the Gesture Support section of the Getting Started
guide.
delay of 1500ms.

#### Show and hide delays

To add a delay before showing or hiding the tooltip, you can use the inputs `matTooltipShowDelay`
To add a delay before showing or hiding the tooltip, you can use the inputs `matTooltipShowDelay`
and `matTooltipHideDelay` to provide a delay time in milliseconds.

The following example has a tooltip that waits one second to display after the user
Expand All @@ -58,7 +56,7 @@ which both accept a number in milliseconds to delay before applying the display

#### Disabling the tooltip from showing

To completely disable a tooltip, set `matTooltipDisabled`. While disabled, a tooltip will never be
To completely disable a tooltip, set `matTooltipDisabled`. While disabled, a tooltip will never be
shown.

### Accessibility
Expand Down
185 changes: 166 additions & 19 deletions src/material/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
SCROLL_THROTTLE_MS,
TOOLTIP_PANEL_CLASS,
MAT_TOOLTIP_DEFAULT_OPTIONS,
TooltipTouchGestures,
} from './index';


Expand Down Expand Up @@ -883,33 +884,175 @@ describe('MatTooltip', () => {
}));
});

describe('special cases', () => {
describe('touch gestures', () => {
beforeEach(() => {
platform.ANDROID = true;
});

it('should have a delay when showing on touchstart', fakeAsync(() => {
const fixture = TestBed.createComponent(BasicTooltipDemo);
fixture.detectChanges();
const button: HTMLButtonElement = fixture.nativeElement.querySelector('button');

dispatchFakeEvent(button, 'touchstart');
fixture.detectChanges();
tick(250); // Halfway through the delay.

assertTooltipInstance(fixture.componentInstance.tooltip, false);

tick(250); // Finish the delay.
fixture.detectChanges();
tick(500); // Finish the animation.

assertTooltipInstance(fixture.componentInstance.tooltip, true);
}));

it('should be able to disable opening on touch', fakeAsync(() => {
const fixture = TestBed.createComponent(BasicTooltipDemo);
fixture.componentInstance.touchGestures = 'off';
fixture.detectChanges();
const button: HTMLButtonElement = fixture.nativeElement.querySelector('button');

dispatchFakeEvent(button, 'touchstart');
fixture.detectChanges();
tick(500); // Finish the delay.
fixture.detectChanges();
tick(500); // Finish the animation.

assertTooltipInstance(fixture.componentInstance.tooltip, false);
}));

it('should not prevent the default action on touchstart', () => {
const fixture = TestBed.createComponent(BasicTooltipDemo);
fixture.detectChanges();
const button: HTMLButtonElement = fixture.nativeElement.querySelector('button');
const event = dispatchFakeEvent(button, 'touchstart');
fixture.detectChanges();

expect(event.defaultPrevented).toBe(false);
});

it('should close on touchend with a delay', fakeAsync(() => {
const fixture = TestBed.createComponent(BasicTooltipDemo);
fixture.detectChanges();
const button: HTMLButtonElement = fixture.nativeElement.querySelector('button');

dispatchFakeEvent(button, 'touchstart');
fixture.detectChanges();
tick(500); // Finish the open delay.
fixture.detectChanges();
tick(500); // Finish the animation.
assertTooltipInstance(fixture.componentInstance.tooltip, true);

dispatchFakeEvent(button, 'touchend');
fixture.detectChanges();
tick(1000); // 2/3 through the delay
assertTooltipInstance(fixture.componentInstance.tooltip, true);

tick(500); // Finish the delay.
fixture.detectChanges();
tick(500); // Finish the exit animation.

it('should clear the `user-select` when a tooltip is set on a text field', () => {
assertTooltipInstance(fixture.componentInstance.tooltip, false);
}));

it('should close on touchcancel with a delay', fakeAsync(() => {
const fixture = TestBed.createComponent(BasicTooltipDemo);
fixture.detectChanges();
const button: HTMLButtonElement = fixture.nativeElement.querySelector('button');

dispatchFakeEvent(button, 'touchstart');
fixture.detectChanges();
tick(500); // Finish the open delay.
fixture.detectChanges();
tick(500); // Finish the animation.
assertTooltipInstance(fixture.componentInstance.tooltip, true);

dispatchFakeEvent(button, 'touchcancel');
fixture.detectChanges();
tick(1000); // 2/3 through the delay
assertTooltipInstance(fixture.componentInstance.tooltip, true);

tick(500); // Finish the delay.
fixture.detectChanges();
tick(500); // Finish the exit animation.

assertTooltipInstance(fixture.componentInstance.tooltip, false);
}));

it('should disable native touch interactions', () => {
const fixture = TestBed.createComponent(BasicTooltipDemo);
fixture.detectChanges();

const styles = fixture.nativeElement.querySelector('button').style;
expect(styles.touchAction || (styles as any).webkitUserDrag).toBe('none');
});

it('should allow native touch interactions if touch gestures are turned off', () => {
const fixture = TestBed.createComponent(BasicTooltipDemo);
fixture.componentInstance.touchGestures = 'off';
fixture.detectChanges();

const styles = fixture.nativeElement.querySelector('button').style;
expect(styles.touchAction || (styles as any).webkitUserDrag).toBeFalsy();
});

it('should allow text selection on inputs when gestures are set to auto', () => {
const fixture = TestBed.createComponent(TooltipOnTextFields);
const instance = fixture.componentInstance;
fixture.detectChanges();

const inputStyle = fixture.componentInstance.input.nativeElement.style;
const textareaStyle = fixture.componentInstance.textarea.nativeElement.style;

expect(inputStyle.userSelect).toBeFalsy();
expect(inputStyle.webkitUserSelect).toBeFalsy();
expect(inputStyle.msUserSelect).toBeFalsy();
expect((inputStyle as any).MozUserSelect).toBeFalsy();

expect(textareaStyle.userSelect).toBeFalsy();
expect(textareaStyle.webkitUserSelect).toBeFalsy();
expect(textareaStyle.msUserSelect).toBeFalsy();
expect((textareaStyle as any).MozUserSelect).toBeFalsy();
});

it('should disable text selection on inputs when gestures are set to on', () => {
const fixture = TestBed.createComponent(TooltipOnTextFields);
fixture.componentInstance.touchGestures = 'on';
fixture.detectChanges();

expect(instance.input.nativeElement.style.userSelect).toBeFalsy();
expect(instance.input.nativeElement.style.webkitUserSelect).toBeFalsy();
expect(instance.input.nativeElement.style.msUserSelect).toBeFalsy();
const inputStyle = fixture.componentInstance.input.nativeElement.style;
const inputUserSelect = inputStyle.userSelect || inputStyle.webkitUserSelect ||
inputStyle.msUserSelect || (inputStyle as any).MozUserSelect;
const textareaStyle = fixture.componentInstance.textarea.nativeElement.style;
const textareaUserSelect = textareaStyle.userSelect || textareaStyle.webkitUserSelect ||
textareaStyle.msUserSelect || (textareaStyle as any).MozUserSelect;

expect(instance.textarea.nativeElement.style.userSelect).toBeFalsy();
expect(instance.textarea.nativeElement.style.webkitUserSelect).toBeFalsy();
expect(instance.textarea.nativeElement.style.msUserSelect).toBeFalsy();
expect(inputUserSelect).toBe('none');
expect(textareaUserSelect).toBe('none');
});

it('should clear the `-webkit-user-drag` on draggable elements', () => {
it('should allow native dragging on draggable elements when gestures are set to auto', () => {
const fixture = TestBed.createComponent(TooltipOnDraggableElement);

fixture.detectChanges();

expect(fixture.componentInstance.button.nativeElement.style.webkitUserDrag).toBeFalsy();
});

it('should disable native dragging on draggable elements when gestures are set to on', () => {
const fixture = TestBed.createComponent(TooltipOnDraggableElement);
fixture.componentInstance.touchGestures = 'on';
fixture.detectChanges();

const styles = fixture.componentInstance.button.nativeElement.style;

if ('webkitUserDrag' in styles) {
expect(styles.webkitUserDrag).toBe('none');
}
});

it('should not open on `mouseenter` on iOS', () => {
platform.IOS = true;
platform.ANDROID = false;

const fixture = TestBed.createComponent(BasicTooltipDemo);

Expand All @@ -922,6 +1065,7 @@ describe('MatTooltip', () => {

it('should not open on `mouseenter` on Android', () => {
platform.ANDROID = true;
platform.IOS = false;

const fixture = TestBed.createComponent(BasicTooltipDemo);

Expand All @@ -931,7 +1075,6 @@ describe('MatTooltip', () => {

assertTooltipInstance(fixture.componentInstance.tooltip, false);
});

});

});
Expand All @@ -943,7 +1086,8 @@ describe('MatTooltip', () => {
*ngIf="showButton"
[matTooltip]="message"
[matTooltipPosition]="position"
[matTooltipClass]="{'custom-one': showTooltipClass, 'custom-two': showTooltipClass }">
[matTooltipClass]="{'custom-one': showTooltipClass, 'custom-two': showTooltipClass }"
[matTooltipTouchGestures]="touchGestures">
Button
</button>`
})
Expand All @@ -952,6 +1096,7 @@ class BasicTooltipDemo {
message: any = initialTooltipMessage;
showButton: boolean = true;
showTooltipClass = false;
touchGestures: TooltipTouchGestures = 'auto';
@ViewChild(MatTooltip, {static: false}) tooltip: MatTooltip;
@ViewChild('button', {static: false}) button: ElementRef<HTMLButtonElement>;
}
Expand Down Expand Up @@ -1023,31 +1168,33 @@ class DynamicTooltipsDemo {
template: `
<input
#input
style="user-select: none; -webkit-user-select: none"
matTooltip="Something">
matTooltip="Something"
[matTooltipTouchGestures]="touchGestures">

<textarea
#textarea
style="user-select: none; -webkit-user-select: none"
matTooltip="Another thing"></textarea>
matTooltip="Another thing"
[matTooltipTouchGestures]="touchGestures"></textarea>
`,
})
class TooltipOnTextFields {
@ViewChild('input', {static: false}) input: ElementRef<HTMLInputElement>;
@ViewChild('textarea', {static: false}) textarea: ElementRef<HTMLTextAreaElement>;
touchGestures: TooltipTouchGestures = 'auto';
}

@Component({
template: `
<button
#button
style="-webkit-user-drag: none;"
draggable="true"
matTooltip="Drag me"></button>
matTooltip="Drag me"
[matTooltipTouchGestures]="touchGestures"></button>
`,
})
class TooltipOnDraggableElement {
@ViewChild('button', {static: false}) button: ElementRef;
touchGestures: TooltipTouchGestures = 'auto';
}

@Component({
Expand Down
Loading