Skip to content

Commit d030ee4

Browse files
committed
fix(material/tooltip): add opt-in for better touch device detection
Currently the touch device detection in the tooltip is based purely on the `Platform` provider which isn't able to detect some newer iOS devices properly. These changes add an additional check through a media query. The check is currently opt-in, because it can cause false positives in tests that run in headless browsers. Fixes #32503. Fixes #25287.
1 parent ef53e73 commit d030ee4

File tree

2 files changed

+26
-4
lines changed

2 files changed

+26
-4
lines changed

goldens/material/tooltip/index.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
9292

9393
// @public
9494
export interface MatTooltipDefaultOptions {
95+
detectHoverCapability?: boolean;
9596
disableTooltipInteractivity?: boolean;
9697
hideDelay: number;
9798
position?: TooltipPosition;

src/material/tooltip/tooltip.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import {
5252
VerticalConnectionPos,
5353
} from '@angular/cdk/overlay';
5454
import {ComponentPortal} from '@angular/cdk/portal';
55+
import {MediaMatcher} from '@angular/cdk/layout';
5556
import {Observable, Subject} from 'rxjs';
5657
import {_animationsDisabled} from '../core';
5758

@@ -146,6 +147,14 @@ export interface MatTooltipDefaultOptions {
146147
* `tooltipClass` is defined directly on the tooltip element, as it will override the default.
147148
*/
148149
tooltipClass?: string | string[];
150+
151+
/**
152+
* Whether the tooltip should use a media query to detect if the device is able to hover.
153+
* Note that this may affect tests that run in a headless browser which reports that it's
154+
* unable to hover. In such cases you may need to include an additional timeout, because
155+
* the tooltip will fall back to treating the device as a touch screen.
156+
*/
157+
detectHoverCapability?: boolean;
149158
}
150159

151160
/**
@@ -190,6 +199,7 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
190199
protected _dir = inject(Directionality);
191200
private _injector = inject(Injector);
192201
private _viewContainerRef = inject(ViewContainerRef);
202+
private _mediaMatcher = inject(MediaMatcher);
193203
private _animationsDisabled = _animationsDisabled();
194204
private _defaultOptions = inject<MatTooltipDefaultOptions>(MAT_TOOLTIP_DEFAULT_OPTIONS, {
195205
optional: true,
@@ -784,7 +794,7 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
784794

785795
// The mouse events shouldn't be bound on mobile devices, because they can prevent the
786796
// first tap from firing its click event or can cause the tooltip to open for clicks.
787-
if (this._platformSupportsMouseEvents()) {
797+
if (!this._isTouchPlatform()) {
788798
this._passiveListeners.push([
789799
'mouseenter',
790800
event => {
@@ -830,7 +840,7 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
830840
this._pointerExitEventsInitialized = true;
831841

832842
const exitListeners: (readonly [string, EventListenerOrEventListenerObject])[] = [];
833-
if (this._platformSupportsMouseEvents()) {
843+
if (!this._isTouchPlatform()) {
834844
exitListeners.push(
835845
[
836846
'mouseleave',
@@ -865,8 +875,19 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
865875
});
866876
}
867877

868-
private _platformSupportsMouseEvents() {
869-
return !this._platform.IOS && !this._platform.ANDROID;
878+
private _isTouchPlatform(): boolean {
879+
if (this._platform.IOS || this._platform.ANDROID) {
880+
// If we detected iOS or Android, it's definitely supported.
881+
return true;
882+
} else if (!this._platform.isBrowser) {
883+
// If it's not a browser, it's definitely not supported.
884+
return false;
885+
}
886+
887+
return (
888+
!!this._defaultOptions?.detectHoverCapability &&
889+
this._mediaMatcher.matchMedia('(any-hover: none)').matches
890+
);
870891
}
871892

872893
/** Listener for the `wheel` event on the element. */

0 commit comments

Comments
 (0)