From 3f7b97bcbbfa96aaca95e3f6c5159435afe1150a Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Sat, 1 Aug 2026 15:15:58 +0300 Subject: [PATCH 1/5] fix(tooltip): prevent delayed tooltip after target moves --- .../tooltip/tooltip-target.directive.ts | 51 +++++++++++++++++++ .../tooltip/tooltip.directive.spec.ts | 50 +++++++++++++++++- 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts index c55e158f229..497e9cf012b 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts @@ -27,6 +27,13 @@ export interface ITooltipHideEventArgs extends IBaseEventArgs { cancel: boolean; } +const HOVER_SHOW_TRIGGERS = new Set(['mouseenter', 'mouseover', 'pointerenter', 'pointerover']); + +interface TooltipPointerPosition { + clientX: number; + clientY: number; +} + /** * **Ignite UI for Angular Tooltip Target** - * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tooltip) @@ -381,9 +388,16 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen private _showTriggers = new Set(['pointerenter']); private _hideTriggers = new Set(['pointerleave', 'click']); private _pendingShowTrigger: string | null = null; + private _pointerPosition: TooltipPointerPosition | null = null; private _abortController = new AbortController(); + private _onPointerMove = (event: PointerEvent): void => { + if (this._pointerPosition) { + this._pointerPosition = { clientX: event.clientX, clientY: event.clientY }; + } + }; + /** * @hidden */ @@ -506,6 +520,7 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen for (const each of this._hideTriggers) { this.nativeElement.addEventListener(each, this.onHide, options); } + this.nativeElement.addEventListener('pointermove', this._onPointerMove, options); } private removeEventListeners(): void { @@ -562,14 +577,49 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._evaluateStickyState(); this._pendingShowTrigger = triggerEvent?.type ?? null; + this._pointerPosition = withDelay && this.showDelay > 0 + ? this._getPointerPosition(triggerEvent) + : null; this.target.timeoutId = setTimeout(() => { // Call open() of IgxTooltipDirective + const pointerPosition = this._pointerPosition; + this.target.timeoutId = null; this._pendingShowTrigger = null; + this._pointerPosition = null; + + if (pointerPosition && !this._isPointerOverTarget(pointerPosition)) { + return; + } + this.target.open(this._mergedOverlaySettings); }, withDelay ? this.showDelay : 0); } + private _getPointerPosition(event?: Event): TooltipPointerPosition | null { + if (!event || !HOVER_SHOW_TRIGGERS.has(event.type)) { + return null; + } + + const pointerEvent = event as MouseEvent; + if (typeof pointerEvent.clientX !== 'number' || typeof pointerEvent.clientY !== 'number') { + return null; + } + if (!event.isTrusted && pointerEvent.clientX === 0 && pointerEvent.clientY === 0) { + return null; + } + + return { clientX: pointerEvent.clientX, clientY: pointerEvent.clientY }; + } + + private _isPointerOverTarget(position: TooltipPointerPosition): boolean { + const root = this.nativeElement.getRootNode() as DocumentOrShadowRoot; + const hitTestRoot = typeof root.elementFromPoint === 'function' + ? root + : this.nativeElement.ownerDocument; + const element = hitTestRoot.elementFromPoint(position.clientX, position.clientY); + return !!element && this.nativeElement.contains(element); + } private _showOnInteraction(triggerEvent?: Event): void { this._stopTimeoutAndAnimation(); @@ -620,6 +670,7 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen clearTimeout(this.target.timeoutId); this.target.timeoutId = null; this._pendingShowTrigger = null; + this._pointerPosition = null; } /** diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts index 318b4e0f1e6..ddd6ab6fcf0 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts @@ -167,6 +167,53 @@ describe('IgxTooltip', () => { verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, true); })); + it('should not show a delayed tooltip after its target moves away from the pointer', fakeAsync(() => { + tooltipTarget.showDelay = 500; + const target = button.nativeElement; + target.style.position = 'fixed'; + target.style.top = '20px'; + target.style.left = '20px'; + target.style.width = '100px'; + target.style.height = '40px'; + target.style.zIndex = '9999'; + const bounds = target.getBoundingClientRect(); + const pointer = { + clientX: bounds.left + bounds.width / 2, + clientY: bounds.top + bounds.height / 2 + }; + + expect(target.contains(document.elementFromPoint(pointer.clientX, pointer.clientY))).toBeTrue(); + hoverElement(button, pointer); + target.style.transform = 'translateX(200px)'; + expect(target.contains(document.elementFromPoint(pointer.clientX, pointer.clientY))).toBeFalse(); + + tick(500); + verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, false); + })); + + it('should not hit-test hover interactions without a show delay', fakeAsync(() => { + tooltipTarget.showDelay = 0; + const elementFromPointSpy = spyOn(document, 'elementFromPoint'); + + hoverElement(button, { clientX: 20, clientY: 30 }); + tick(); + + expect(elementFromPointSpy).not.toHaveBeenCalled(); + verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, true); + })); + + it('should validate a delayed tooltip against the latest pointer position', fakeAsync(() => { + tooltipTarget.showDelay = 500; + const elementFromPointSpy = spyOn(document, 'elementFromPoint').and.returnValue(button.nativeElement); + + hoverElement(button, { clientX: 20, clientY: 30 }); + button.nativeElement.dispatchEvent(new PointerEvent('pointermove', { clientX: 40, clientY: 50 })); + tick(500); + + expect(elementFromPointSpy).toHaveBeenCalledWith(40, 50); + verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, true); + })); + it('IgxTooltip mouse interaction respects hideDelay', fakeAsync(() => { tooltipTarget.hideDelay = 700; fix.detectChanges(); @@ -1169,7 +1216,8 @@ interface ElementRefLike { nativeElement: HTMLElement } -const hoverElement = (element: ElementRefLike) => element.nativeElement.dispatchEvent(new MouseEvent('pointerenter')); +const hoverElement = (element: ElementRefLike, eventInit: MouseEventInit = {}) => + element.nativeElement.dispatchEvent(new MouseEvent('pointerenter', eventInit)); const unhoverElement = (element: ElementRefLike) => element.nativeElement.dispatchEvent(new MouseEvent('pointerleave')); From cfbfb364ca2ad544e1abd1c78365516a01f6e5f4 Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Wed, 12 Aug 2026 14:16:17 +0300 Subject: [PATCH 2/5] fix(tooltip): improve hover interaction handling --- .../tooltip/tooltip-target.directive.ts | 57 +++--------- .../tooltip/tooltip.directive.spec.ts | 86 +++++++------------ 2 files changed, 43 insertions(+), 100 deletions(-) diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts index 497e9cf012b..d22ca32e042 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts @@ -29,11 +29,6 @@ export interface ITooltipHideEventArgs extends IBaseEventArgs { const HOVER_SHOW_TRIGGERS = new Set(['mouseenter', 'mouseover', 'pointerenter', 'pointerover']); -interface TooltipPointerPosition { - clientX: number; - clientY: number; -} - /** * **Ignite UI for Angular Tooltip Target** - * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/tooltip) @@ -388,16 +383,9 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen private _showTriggers = new Set(['pointerenter']); private _hideTriggers = new Set(['pointerleave', 'click']); private _pendingShowTrigger: string | null = null; - private _pointerPosition: TooltipPointerPosition | null = null; private _abortController = new AbortController(); - private _onPointerMove = (event: PointerEvent): void => { - if (this._pointerPosition) { - this._pointerPosition = { clientX: event.clientX, clientY: event.clientY }; - } - }; - /** * @hidden */ @@ -520,7 +508,6 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen for (const each of this._hideTriggers) { this.nativeElement.addEventListener(each, this.onHide, options); } - this.nativeElement.addEventListener('pointermove', this._onPointerMove, options); } private removeEventListeners(): void { @@ -577,18 +564,13 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._evaluateStickyState(); this._pendingShowTrigger = triggerEvent?.type ?? null; - this._pointerPosition = withDelay && this.showDelay > 0 - ? this._getPointerPosition(triggerEvent) - : null; this.target.timeoutId = setTimeout(() => { - // Call open() of IgxTooltipDirective - const pointerPosition = this._pointerPosition; - this.target.timeoutId = null; + const isHoverTrigger = this._pendingShowTrigger && HOVER_SHOW_TRIGGERS.has(this._pendingShowTrigger); this._pendingShowTrigger = null; - this._pointerPosition = null; + this.target.timeoutId = null; - if (pointerPosition && !this._isPointerOverTarget(pointerPosition)) { + if (isHoverTrigger && !this.nativeElement.matches(':hover')) { return; } @@ -596,30 +578,6 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen }, withDelay ? this.showDelay : 0); } - private _getPointerPosition(event?: Event): TooltipPointerPosition | null { - if (!event || !HOVER_SHOW_TRIGGERS.has(event.type)) { - return null; - } - - const pointerEvent = event as MouseEvent; - if (typeof pointerEvent.clientX !== 'number' || typeof pointerEvent.clientY !== 'number') { - return null; - } - if (!event.isTrusted && pointerEvent.clientX === 0 && pointerEvent.clientY === 0) { - return null; - } - - return { clientX: pointerEvent.clientX, clientY: pointerEvent.clientY }; - } - - private _isPointerOverTarget(position: TooltipPointerPosition): boolean { - const root = this.nativeElement.getRootNode() as DocumentOrShadowRoot; - const hitTestRoot = typeof root.elementFromPoint === 'function' - ? root - : this.nativeElement.ownerDocument; - const element = hitTestRoot.elementFromPoint(position.clientX, position.clientY); - return !!element && this.nativeElement.contains(element); - } private _showOnInteraction(triggerEvent?: Event): void { this._stopTimeoutAndAnimation(); @@ -670,7 +628,6 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen clearTimeout(this.target.timeoutId); this.target.timeoutId = null; this._pendingShowTrigger = null; - this._pointerPosition = null; } /** @@ -714,7 +671,7 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen } /** - * Creates (if not already created) an instance of the IgxTooltipCloseButtonComponent, + * Creates (if not already created) an instance of the tooltip close button, * and assigns it the provided custom template. */ private _createCloseTemplate(template?: TemplateRef | undefined): void { @@ -740,6 +697,9 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._renderer.appendChild(this.target.element, this._closeButtonRef.location.nativeElement); this._closeButtonRef.changeDetectorRef.detectChanges(); this.target.role = "status" + // Mark the tooltip directive as Dirty to ensure that + // the CD refreshes the bindings + this.target.cdr?.markForCheck(); } } @@ -751,6 +711,9 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._renderer.removeChild(this.target.element, this._closeButtonRef.location.nativeElement); this._closeButtonRef.changeDetectorRef.detectChanges(); this.target.role = "tooltip" + // Mark the tooltip directive as Dirty to ensure that + // the CD refreshes the bindings + this.target.cdr?.markForCheck(); } } diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts index ddd6ab6fcf0..3a2e48321ae 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts @@ -1,9 +1,9 @@ -import { DebugElement } from '@angular/core'; +import { DebugElement, ErrorHandler, provideZonelessChangeDetection } from '@angular/core'; import { fakeAsync, TestBed, tick, flush, waitForAsync, ComponentFixture } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IgxTooltipSingleTargetComponent, IgxTooltipMultipleTargetsComponent, IgxTooltipPlainStringComponent, IgxTooltipWithToggleActionComponent, IgxTooltipWithCloseButtonComponent, IgxTooltipWithNestedContentComponent, IgxTooltipNestedTooltipsComponent } from '../../../../test-utils/tooltip-components.spec'; -import { UIInteractions } from '../../../../test-utils/ui-interactions.spec'; +import { UIInteractions, wait } from '../../../../test-utils/ui-interactions.spec'; import { HorizontalAlignment, VerticalAlignment, AutoPositionStrategy } from '../../../../core/src/services/public_api'; import { IgxTooltipDirective } from './tooltip.directive'; import { IgxTooltipTargetDirective } from './tooltip-target.directive'; @@ -15,6 +15,7 @@ const SHOW_DELAY = 200; const HIDE_DELAY = 300; const AUTO_HIDE_DELAY = 180; const TOOLTIP_ARROW_SELECTOR = '[data-arrow="true"]'; +const hoveredElements = new WeakSet(); describe('IgxTooltip', () => { let fix: ComponentFixture; @@ -23,6 +24,11 @@ describe('IgxTooltip', () => { let button: DebugElement; beforeEach(waitForAsync(() => { + const matches = Element.prototype.matches; + spyOn(Element.prototype, 'matches').and.callFake(function(this: Element, selectors: string): boolean { + return selectors === ':hover' ? hoveredElements.has(this) : matches.call(this, selectors); + } as typeof Element.prototype.matches); + TestBed.configureTestingModule({ imports: [ NoopAnimationsModule, @@ -167,53 +173,6 @@ describe('IgxTooltip', () => { verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, true); })); - it('should not show a delayed tooltip after its target moves away from the pointer', fakeAsync(() => { - tooltipTarget.showDelay = 500; - const target = button.nativeElement; - target.style.position = 'fixed'; - target.style.top = '20px'; - target.style.left = '20px'; - target.style.width = '100px'; - target.style.height = '40px'; - target.style.zIndex = '9999'; - const bounds = target.getBoundingClientRect(); - const pointer = { - clientX: bounds.left + bounds.width / 2, - clientY: bounds.top + bounds.height / 2 - }; - - expect(target.contains(document.elementFromPoint(pointer.clientX, pointer.clientY))).toBeTrue(); - hoverElement(button, pointer); - target.style.transform = 'translateX(200px)'; - expect(target.contains(document.elementFromPoint(pointer.clientX, pointer.clientY))).toBeFalse(); - - tick(500); - verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, false); - })); - - it('should not hit-test hover interactions without a show delay', fakeAsync(() => { - tooltipTarget.showDelay = 0; - const elementFromPointSpy = spyOn(document, 'elementFromPoint'); - - hoverElement(button, { clientX: 20, clientY: 30 }); - tick(); - - expect(elementFromPointSpy).not.toHaveBeenCalled(); - verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, true); - })); - - it('should validate a delayed tooltip against the latest pointer position', fakeAsync(() => { - tooltipTarget.showDelay = 500; - const elementFromPointSpy = spyOn(document, 'elementFromPoint').and.returnValue(button.nativeElement); - - hoverElement(button, { clientX: 20, clientY: 30 }); - button.nativeElement.dispatchEvent(new PointerEvent('pointermove', { clientX: 40, clientY: 50 })); - tick(500); - - expect(elementFromPointSpy).toHaveBeenCalledWith(40, 50); - verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, true); - })); - it('IgxTooltip mouse interaction respects hideDelay', fakeAsync(() => { tooltipTarget.hideDelay = 700; fix.detectChanges(); @@ -579,6 +538,22 @@ describe('IgxTooltip', () => { tick(300); verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, false); })); + + for (const trigger of ['mouseenter', 'mouseover', 'pointerenter', 'pointerover']) { + it(`should not open after the delay when the target is no longer hovered using ${trigger}`, fakeAsync(() => { + tooltipTarget.showDelay = 500; + tooltipTarget.showTriggers = trigger; + tooltipTarget.hideTriggers = 'click'; + fix.detectChanges(); + + hoverElement(button, trigger); + tick(300); + unhoverElement(button); + tick(200); + + verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, false); + })); + } }); }); @@ -1216,10 +1191,15 @@ interface ElementRefLike { nativeElement: HTMLElement } -const hoverElement = (element: ElementRefLike, eventInit: MouseEventInit = {}) => - element.nativeElement.dispatchEvent(new MouseEvent('pointerenter', eventInit)); +const hoverElement = (element: ElementRefLike, event = 'pointerenter') => { + hoveredElements.add(element.nativeElement); + element.nativeElement.dispatchEvent(new MouseEvent(event)); +}; -const unhoverElement = (element: ElementRefLike) => element.nativeElement.dispatchEvent(new MouseEvent('pointerleave')); +const unhoverElement = (element: ElementRefLike) => { + hoveredElements.delete(element.nativeElement); + element.nativeElement.dispatchEvent(new MouseEvent('pointerleave')); +}; const simulateTriggerEvent = (element: ElementRefLike, event: string) => element.nativeElement.dispatchEvent(new Event(event, { bubbles: true })); @@ -1236,7 +1216,7 @@ const alignmentTolerance = 2; export const verifyTooltipPosition = ( tooltipNativeElement: HTMLElement, actualTarget: { nativeElement: HTMLElement }, - shouldAlign:boolean = true, + shouldAlign: boolean = true, placement: Placement = Placement.Bottom, offset: number = 6 ) => { From cfa00f881b01933c98b0a6e4aba3d8d34393aefc Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Wed, 12 Aug 2026 14:23:34 +0300 Subject: [PATCH 3/5] chore(*): remove leftovers --- .../src/directives/tooltip/tooltip-target.directive.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts index d22ca32e042..394a4f2f557 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip-target.directive.ts @@ -671,7 +671,7 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen } /** - * Creates (if not already created) an instance of the tooltip close button, + * Creates (if not already created) an instance of the IgxTooltipCloseButtonComponent, * and assigns it the provided custom template. */ private _createCloseTemplate(template?: TemplateRef | undefined): void { @@ -697,9 +697,6 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._renderer.appendChild(this.target.element, this._closeButtonRef.location.nativeElement); this._closeButtonRef.changeDetectorRef.detectChanges(); this.target.role = "status" - // Mark the tooltip directive as Dirty to ensure that - // the CD refreshes the bindings - this.target.cdr?.markForCheck(); } } @@ -711,9 +708,6 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen this._renderer.removeChild(this.target.element, this._closeButtonRef.location.nativeElement); this._closeButtonRef.changeDetectorRef.detectChanges(); this.target.role = "tooltip" - // Mark the tooltip directive as Dirty to ensure that - // the CD refreshes the bindings - this.target.cdr?.markForCheck(); } } From a7cbda39bd9b5a485771c40d2710445c97a9f4f3 Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Wed, 12 Aug 2026 14:26:00 +0300 Subject: [PATCH 4/5] fix(tooltip): remove unused imports --- .../src/directives/tooltip/tooltip.directive.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts index 3a2e48321ae..aaa8d5536bf 100644 --- a/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts +++ b/projects/igniteui-angular/directives/src/directives/tooltip/tooltip.directive.spec.ts @@ -1,9 +1,9 @@ -import { DebugElement, ErrorHandler, provideZonelessChangeDetection } from '@angular/core'; +import { DebugElement } from '@angular/core'; import { fakeAsync, TestBed, tick, flush, waitForAsync, ComponentFixture } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IgxTooltipSingleTargetComponent, IgxTooltipMultipleTargetsComponent, IgxTooltipPlainStringComponent, IgxTooltipWithToggleActionComponent, IgxTooltipWithCloseButtonComponent, IgxTooltipWithNestedContentComponent, IgxTooltipNestedTooltipsComponent } from '../../../../test-utils/tooltip-components.spec'; -import { UIInteractions, wait } from '../../../../test-utils/ui-interactions.spec'; +import { UIInteractions } from '../../../../test-utils/ui-interactions.spec'; import { HorizontalAlignment, VerticalAlignment, AutoPositionStrategy } from '../../../../core/src/services/public_api'; import { IgxTooltipDirective } from './tooltip.directive'; import { IgxTooltipTargetDirective } from './tooltip-target.directive'; From 5b9578b1cd18c9d2772ccd1da285daa0da64c103 Mon Sep 17 00:00:00 2001 From: Viktor Kombov Date: Wed, 12 Aug 2026 18:56:55 +0300 Subject: [PATCH 5/5] fix(grid-validation): mock element.matches for tooltip hover test --- projects/igniteui-angular/grids/grid/src/grid-validation.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/grids/grid/src/grid-validation.spec.ts b/projects/igniteui-angular/grids/grid/src/grid-validation.spec.ts index 2e6ab931322..5e5c274860e 100644 --- a/projects/igniteui-angular/grids/grid/src/grid-validation.spec.ts +++ b/projects/igniteui-angular/grids/grid/src/grid-validation.spec.ts @@ -225,6 +225,7 @@ describe('IgxGrid - Validation #grid', () => { expect(cell.errorTooltip.first.collapsed).toBeTrue(); const element = fixture.debugElement.query(By.directive(IgxTooltipTargetDirective)).nativeElement; + spyOn(element, 'matches').and.returnValue(true); element.dispatchEvent(new MouseEvent('pointerenter')); flush(); fixture.detectChanges();