• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 15192134227

22 May 2025 04:40PM UTC coverage: 91.613% (+0.005%) from 91.608%
15192134227

Pull #15815

github

web-flow
Merge 4e07091f5 into 610da485e
Pull Request #15815: fix(tooltip): make touch events passive - 18.2.x

13007 of 15242 branches covered (85.34%)

18 of 18 new or added lines in 2 files covered. (100.0%)

1 existing line in 1 file now uncovered.

26367 of 28781 relevant lines covered (91.61%)

34000.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.64
/projects/igniteui-angular/src/lib/directives/tooltip/tooltip-target.directive.ts
1
import { useAnimation } from '@angular/animations';
2
import { Directive, OnInit, OnDestroy, Output, ElementRef, Optional, ViewContainerRef, HostListener, Input, EventEmitter, booleanAttribute } from '@angular/core';
3
import { Subject } from 'rxjs';
4
import { takeUntil } from 'rxjs/operators';
5
import { IgxNavigationService } from '../../core/navigation';
6
import { IBaseEventArgs } from '../../core/utils';
7
import { AutoPositionStrategy, HorizontalAlignment, PositionSettings } from '../../services/public_api';
8
import { IgxToggleActionDirective } from '../toggle/toggle.directive';
9
import { IgxTooltipComponent } from './tooltip.component';
10
import { IgxTooltipDirective } from './tooltip.directive';
11
import { fadeOut, scaleInCenter } from 'igniteui-angular/animations';
12

13
export interface ITooltipShowEventArgs extends IBaseEventArgs {
14
    target: IgxTooltipTargetDirective;
15
    tooltip: IgxTooltipDirective;
16
    cancel: boolean;
17
}
18
export interface ITooltipHideEventArgs extends IBaseEventArgs {
19
    target: IgxTooltipTargetDirective;
20
    tooltip: IgxTooltipDirective;
21
    cancel: boolean;
22
}
23

24
/**
25
 * **Ignite UI for Angular Tooltip Target** -
26
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tooltip)
27
 *
28
 * The Ignite UI for Angular Tooltip Target directive is used to mark an HTML element in the markup as one that has a tooltip.
29
 * The tooltip target is used in combination with the Ignite UI for Angular Tooltip by assigning the exported tooltip reference to the
30
 * target's selector property.
31
 *
32
 * Example:
33
 * ```html
34
 * <button type="button" igxButton [igxTooltipTarget]="tooltipRef">Hover me</button>
35
 * <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
36
 * ```
37
 */
38
@Directive({
39
    exportAs: 'tooltipTarget',
40
    selector: '[igxTooltipTarget]',
41
    standalone: true
42
})
43
export class IgxTooltipTargetDirective extends IgxToggleActionDirective implements OnInit, OnDestroy {
2✔
44
    /**
45
     * Gets/sets the amount of milliseconds that should pass before showing the tooltip.
46
     *
47
     * ```typescript
48
     * // get
49
     * let tooltipShowDelay = this.tooltipTarget.showDelay;
50
     * ```
51
     *
52
     * ```html
53
     * <!--set-->
54
     * <button type="button" igxButton [igxTooltipTarget]="tooltipRef" [showDelay]="1500">Hover me</button>
55
     * <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
56
     * ```
57
     */
58
    @Input('showDelay')
59
    public showDelay = 500;
70✔
60

61
    /**
62
     * Gets/sets the amount of milliseconds that should pass before hiding the tooltip.
63
     *
64
     * ```typescript
65
     * // get
66
     * let tooltipHideDelay = this.tooltipTarget.hideDelay;
67
     * ```
68
     *
69
     * ```html
70
     * <!--set-->
71
     * <button type="button" igxButton [igxTooltipTarget]="tooltipRef" [hideDelay]="1500">Hover me</button>
72
     * <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
73
     * ```
74
     */
75
    @Input('hideDelay')
76
    public hideDelay = 500;
70✔
77

78
    /**
79
     * Specifies if the tooltip should not show when hovering its target with the mouse. (defaults to false)
80
     * While setting this property to 'true' will disable the user interactions that shows/hides the tooltip,
81
     * the developer will still be able to show/hide the tooltip through the API.
82
     *
83
     * ```typescript
84
     * // get
85
     * let tooltipDisabledValue = this.tooltipTarget.tooltipDisabled;
86
     * ```
87
     *
88
     * ```html
89
     * <!--set-->
90
     * <button type="button" igxButton [igxTooltipTarget]="tooltipRef" [tooltipDisabled]="true">Hover me</button>
91
     * <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
92
     * ```
93
     */
94
    @Input({ alias: 'tooltipDisabled', transform: booleanAttribute })
95
    public tooltipDisabled = false;
70✔
96

97
    /**
98
     * @hidden
99
     */
100
    @Input('igxTooltipTarget')
101
    public override set target(target: any) {
102
        if (target instanceof IgxTooltipDirective) {
71✔
103
            this._target = target;
68✔
104
        }
105
    }
106

107
    /**
108
     * @hidden
109
     */
110
    public override get target(): any {
111
        if (typeof this._target === 'string') {
1,139!
112
            return this._navigationService.get(this._target);
×
113
        }
114
        return this._target;
1,139✔
115
    }
116

117
    /**
118
    * @hidden
119
    */
120
    @Input()
121
    public set tooltip(content: any) {
122
        if (!this.target && (typeof content === 'string' || content instanceof String)) {
32!
123
            const tooltipComponent = this._viewContainerRef.createComponent(IgxTooltipComponent);
2✔
124
            tooltipComponent.instance.content = content as string;
2✔
125

126
            this._target = tooltipComponent.instance.tooltip;
2✔
127
        }
128
    }
129

130
    /**
131
     * Gets the respective native element of the directive.
132
     *
133
     * ```typescript
134
     * let tooltipTargetElement = this.tooltipTarget.nativeElement;
135
     * ```
136
     */
137
    public get nativeElement() {
138
        return this._element.nativeElement;
148✔
139
    }
140

141
    /**
142
     * Indicates if the tooltip that is is associated with this target is currently hidden.
143
     *
144
     * ```typescript
145
     * let tooltipHiddenValue = this.tooltipTarget.tooltipHidden;
146
     * ```
147
     */
148
    public get tooltipHidden(): boolean {
149
        return !this.target || this.target.collapsed;
54✔
150
    }
151

152
    /**
153
     * Emits an event when the tooltip that is associated with this target starts showing.
154
     * This event is fired before the start of the countdown to showing the tooltip.
155
     *
156
     * ```typescript
157
     * tooltipShowing(args: ITooltipShowEventArgs) {
158
     *    alert("Tooltip started showing!");
159
     * }
160
     * ```
161
     *
162
     * ```html
163
     * <button type="button" igxButton [igxTooltipTarget]="tooltipRef" (tooltipShow)='tooltipShowing($event)'>Hover me</button>
164
     * <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
165
     * ```
166
     */
167
    @Output()
168
    public tooltipShow = new EventEmitter<ITooltipShowEventArgs>();
70✔
169

170
    /**
171
     * Emits an event when the tooltip that is associated with this target starts hiding.
172
     * This event is fired before the start of the countdown to hiding the tooltip.
173
     *
174
     * ```typescript
175
     * tooltipHiding(args: ITooltipHideEventArgs) {
176
     *    alert("Tooltip started hiding!");
177
     * }
178
     * ```
179
     *
180
     * ```html
181
     * <button type="button" igxButton [igxTooltipTarget]="tooltipRef" (tooltipHide)='tooltipHiding($event)'>Hover me</button>
182
     * <span #tooltipRef="tooltip" igxTooltip>Hello there, I am a tooltip!</span>
183
     * ```
184
     */
185
    @Output()
186
    public tooltipHide = new EventEmitter<ITooltipHideEventArgs>();
70✔
187

188
    private destroy$ = new Subject<void>();
70✔
189

190
    constructor(private _element: ElementRef,
70✔
191
        @Optional() private _navigationService: IgxNavigationService, private _viewContainerRef: ViewContainerRef) {
70✔
192
        super(_element, _navigationService);
70✔
193
    }
194

195
    /**
196
     * @hidden
197
     */
198
    @HostListener('click')
199
    public override onClick() {
200
        if (!this.target.collapsed) {
6✔
201
            this.target.forceClose(this.mergedOverlaySettings);
4✔
202
        }
203
    }
204

205
    /**
206
     * @hidden
207
     */
208
    @HostListener('mouseenter')
209
    public onMouseEnter() {
210
        if (this.tooltipDisabled) {
27✔
211
            return;
1✔
212
        }
213

214
        this.checkOutletAndOutsideClick();
26✔
215
        const shouldReturn = this.preMouseEnterCheck();
26✔
216
        if (shouldReturn) {
26!
217
            return;
×
218
        }
219

220
        this.target.tooltipTarget = this;
26✔
221

222
        const showingArgs = { target: this, tooltip: this.target, cancel: false };
26✔
223
        this.tooltipShow.emit(showingArgs);
26✔
224

225
        if (showingArgs.cancel) {
26✔
226
            return;
1✔
227
        }
228

229
        this.target.toBeShown = true;
25✔
230
        this.target.timeoutId = setTimeout(() => {
25✔
231
            this.target.open(this.mergedOverlaySettings); // Call open() of IgxTooltipDirective
24✔
232
            this.target.toBeShown = false;
24✔
233
        }, this.showDelay);
234
    }
235

236
    /**
237
     * @hidden
238
     */
239
    @HostListener('mouseleave')
240
    public onMouseLeave() {
241
        if (this.tooltipDisabled) {
14!
242
            return;
×
243
        }
244

245
        this.checkOutletAndOutsideClick();
14✔
246
        const shouldReturn = this.preMouseLeaveCheck();
14✔
247
        if (shouldReturn || this.target.collapsed) {
14✔
248
            return;
2✔
249
        }
250

251
        this.target.toBeHidden = true;
12✔
252
        this.target.timeoutId = setTimeout(() => {
12✔
253
            this.target.close(); // Call close() of IgxTooltipDirective
11✔
254
            this.target.toBeHidden = false;
11✔
255
        }, this.hideDelay);
256

257

258
    }
259

260
    /**
261
     * @hidden
262
     */
263
    public onTouchStart() {
264
        if (this.tooltipDisabled) {
6✔
265
            return;
1✔
266
        }
267

268
        this.showTooltip();
5✔
269
    }
270

271
    /**
272
     * @hidden
273
     */
274
    public onDocumentTouchStart(event) {
275
        if (this.tooltipDisabled) {
3!
UNCOV
276
            return;
×
277
        }
278

279
        if (this.nativeElement !== event.target &&
3✔
280
            !this.nativeElement.contains(event.target)
281
        ) {
282
            this.hideTooltip();
3✔
283
        }
284
    }
285

286
    /**
287
     * @hidden
288
     */
289
    public override ngOnInit() {
290
        super.ngOnInit();
70✔
291

292
        const positionSettings: PositionSettings = {
70✔
293
            horizontalDirection: HorizontalAlignment.Center,
294
            horizontalStartPoint: HorizontalAlignment.Center,
295
            openAnimation: useAnimation(scaleInCenter, { params: { duration: '150ms' } }),
296
            closeAnimation: useAnimation(fadeOut, { params: { duration: '75ms' } })
297
        };
298

299
        this._overlayDefaults.positionStrategy = new AutoPositionStrategy(positionSettings);
70✔
300
        this._overlayDefaults.closeOnOutsideClick = false;
70✔
301
        this._overlayDefaults.closeOnEscape = true;
70✔
302

303
        this.target.closing.pipe(takeUntil(this.destroy$)).subscribe((event) => {
70✔
304
            if (this.target.tooltipTarget !== this) {
27✔
305
                return;
5✔
306
            }
307

308
            const hidingArgs = { target: this, tooltip: this.target, cancel: false };
22✔
309
            this.tooltipHide.emit(hidingArgs);
22✔
310

311
            if (hidingArgs.cancel) {
22✔
312
                event.cancel = true;
2✔
313
            }
314
        });
315

316
        this.nativeElement.addEventListener('touchstart', this.onTouchStart = this.onTouchStart.bind(this), { passive: true });
70✔
317
    }
318

319
    /**
320
     * @hidden
321
     */
322
    public ngOnDestroy() {
323
        this.hideTooltip();
70✔
324
        this.nativeElement.removeEventListener('touchstart', this.onTouchStart);
70✔
325
        this.destroy$.next();
70✔
326
        this.destroy$.complete();
70✔
327
    }
328

329
    /**
330
     * Shows the tooltip by respecting the 'showDelay' property.
331
     *
332
     * ```typescript
333
     * this.tooltipTarget.showTooltip();
334
     * ```
335
     */
336
    public showTooltip() {
337
        clearTimeout(this.target.timeoutId);
16✔
338

339
        if (!this.target.collapsed) {
16✔
340
            //  if close animation has started finish it, or close the tooltip with no animation
341
            this.target.forceClose(this.mergedOverlaySettings);
1✔
342
            this.target.toBeHidden = false;
1✔
343
        }
344

345
        this.target.tooltipTarget = this;
16✔
346

347
        const showingArgs = { target: this, tooltip: this.target, cancel: false };
16✔
348
        this.tooltipShow.emit(showingArgs);
16✔
349

350
        if (showingArgs.cancel) {
16✔
351
            return;
1✔
352
        }
353

354
        this.target.toBeShown = true;
15✔
355
        this.target.timeoutId = setTimeout(() => {
15✔
356
            this.target.open(this.mergedOverlaySettings); // Call open() of IgxTooltipDirective
15✔
357
            this.target.toBeShown = false;
15✔
358
        }, this.showDelay);
359
    }
360

361
    /**
362
     * Hides the tooltip by respecting the 'hideDelay' property.
363
     *
364
     * ```typescript
365
     * this.tooltipTarget.hideTooltip();
366
     * ```
367
     */
368
    public hideTooltip() {
369
        if (this.target.collapsed && this.target.toBeShown) {
78!
370
            clearTimeout(this.target.timeoutId);
×
371
        }
372

373
        if (this.target.collapsed || this.target.toBeHidden) {
78✔
374
            return;
31✔
375
        }
376

377
        this.target.toBeHidden = true;
47✔
378
        this.target.timeoutId = setTimeout(() => {
47✔
379
            this.target.close(); // Call close() of IgxTooltipDirective
47✔
380
            this.target.toBeHidden = false;
47✔
381
        }, this.hideDelay);
382
    }
383

384
    private checkOutletAndOutsideClick() {
385
        if (this.outlet) {
40✔
386
            this._overlayDefaults.outlet = this.outlet;
1✔
387
        }
388
    }
389

390
    private get mergedOverlaySettings() {
391
        return Object.assign({}, this._overlayDefaults, this.overlaySettings);
46✔
392
    }
393

394
    // Return true if the execution in onMouseEnter should be terminated after this method
395
    private preMouseEnterCheck() {
396
        // If tooltip is about to be opened
397
        if (this.target.toBeShown) {
26!
398
            clearTimeout(this.target.timeoutId);
×
399
            this.target.toBeShown = false;
×
400
        }
401

402
        // If Tooltip is opened or about to be hidden
403
        if (!this.target.collapsed || this.target.toBeHidden) {
26✔
404
            clearTimeout(this.target.timeoutId);
2✔
405

406
            //  if close animation has started finish it, or close the tooltip with no animation
407
            this.target.forceClose(this.mergedOverlaySettings);
2✔
408
            this.target.toBeHidden = false;
2✔
409
        }
410

411
        return false;
26✔
412
    }
413

414
    // Return true if the execution in onMouseLeave should be terminated after this method
415
    private preMouseLeaveCheck(): boolean {
416
        clearTimeout(this.target.timeoutId);
14✔
417

418
        // If tooltip is about to be opened
419
        if (this.target.toBeShown) {
14✔
420
            this.target.toBeShown = false;
1✔
421
            this.target.toBeHidden = false;
1✔
422
            return true;
1✔
423
        }
424

425
        return false;
13✔
426
    }
427
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc