Skip to content

Commit 49ef930

Browse files
Merge branch '10.2.x' into gedinakova/fix-8971-10.2
2 parents d72d03e + b99a9ad commit 49ef930

File tree

2 files changed

+86
-10
lines changed

2 files changed

+86
-10
lines changed

projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
ViewEncapsulation
1111
} from '@angular/core';
1212
import { TestBed, fakeAsync, tick, async, inject } from '@angular/core/testing';
13-
import { BrowserModule, By } from '@angular/platform-browser';
13+
import { BrowserModule } from '@angular/platform-browser';
1414
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
1515
import { IgxOverlayService } from './overlay';
1616
import { IgxToggleDirective, IgxToggleModule, IgxOverlayOutletDirective } from './../../directives/toggle/toggle.directive';
@@ -204,17 +204,34 @@ describe('igxOverlay', () => {
204204
beforeEach(() => {
205205
mockElement = {
206206
style: { visibility: '', cursor: '', transitionDuration: '' },
207+
children: [],
207208
classList: { add: () => { }, remove: () => { } },
208-
appendChild: () => { },
209-
removeChild: () => { },
209+
appendChild(element: any) {
210+
this.children.push(element);
211+
},
212+
removeChild(element: any) {
213+
const index = this.children.indexOf(element);
214+
if (index !== -1) {
215+
this.children.splice(index, 1);
216+
}
217+
},
210218
addEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { },
211219
removeEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { },
212220
getBoundingClientRect: () => ({ width: 10, height: 10 }),
213-
insertBefore: (newChild: HTMLDivElement, refChild: Node) => { },
214-
contains: () => { }
221+
insertBefore(newChild: HTMLDivElement, refChild: Node) {
222+
let refIndex = this.children.indexOf(refChild);
223+
if (refIndex === -1) {
224+
refIndex = 0;
225+
}
226+
this.children.splice(refIndex, 0, newChild);
227+
},
228+
contains(element: any) {
229+
return this.children.indexOf(element) !== -1;
230+
}
215231
};
216232
mockElement.parent = mockElement;
217233
mockElement.parentElement = mockElement;
234+
mockElement.parentNode = mockElement;
218235
mockElementRef = { nativeElement: mockElement };
219236
mockFactoryResolver = {
220237
resolveComponentFactory: (c: any) => {
@@ -235,11 +252,34 @@ describe('igxOverlay', () => {
235252
mockAnimationBuilder = {};
236253
mockDocument = {
237254
body: mockElement,
255+
listeners: { },
238256
defaultView: mockElement,
257+
// this is used be able to properly invoke rxjs `fromEvent` operator, which, turns out
258+
// just adds an event listener to the element and emits accordingly
259+
dispatchEvent(event: KeyboardEvent) {
260+
const type = event.type;
261+
if (this.listeners[type]) {
262+
this.listeners[type].forEach(listener => {
263+
listener(event);
264+
});
265+
}
266+
},
239267
createElement: () => mockElement,
240268
appendChild: () => { },
241-
addEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { },
242-
removeEventListener: (type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) => { }
269+
addEventListener(type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) {
270+
if (!this.listeners[type]) {
271+
this.listeners[type] = [];
272+
}
273+
this.listeners[type].push(listener);
274+
},
275+
removeEventListener(type: string, listener: (this: HTMLElement, ev: MouseEvent) => any) {
276+
if (this.listeners[type]) {
277+
const index = this.listeners[type].indexOf(listener);
278+
if (index !== -1) {
279+
this.listeners[type].splice(index, 1);
280+
}
281+
}
282+
}
243283
};
244284
mockNgZone = {};
245285
mockPlatformUtil = { isIOS: false };
@@ -273,6 +313,40 @@ describe('igxOverlay', () => {
273313
overlay.hide(id);
274314
expect(mockDocument.body.style.cursor).toEqual('initialCursorValue');
275315
});
316+
317+
it('Should clear listener for escape key when overlay settings have outlet specified', () => {
318+
const mockOverlaySettings: OverlaySettings = {
319+
modal: false,
320+
closeOnEscape: true,
321+
outlet: mockElement,
322+
positionStrategy: new GlobalPositionStrategy({ openAnimation: null, closeAnimation: null })
323+
};
324+
const id = overlay.attach(mockElementRef, mockOverlaySettings);
325+
326+
// show the overlay
327+
overlay.show(id);
328+
329+
// expect escape listener to be added to document
330+
expect(mockDocument.listeners['keydown'].length > 0).toBeTruthy();
331+
const keydownListener = mockDocument.listeners['keydown'][0];
332+
333+
spyOn(overlay, 'hide').and.callThrough();
334+
spyOn(mockDocument, 'removeEventListener').and.callThrough();
335+
336+
mockDocument.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
337+
338+
// expect hide to have been called
339+
expect(overlay.hide).toHaveBeenCalledTimes(1);
340+
expect(mockDocument.removeEventListener).toHaveBeenCalled();
341+
342+
// the keydown listener is now removed
343+
expect(mockDocument.removeEventListener).toHaveBeenCalledWith('keydown', keydownListener, undefined);
344+
345+
// fire event again, expecting hide NOT to be fired again
346+
mockDocument.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
347+
expect(overlay.hide).toHaveBeenCalledTimes(1);
348+
expect(mockDocument.listeners['keydown'].length).toBe(0);
349+
});
276350
});
277351

278352
describe('Unit Tests: ', () => {

projects/igniteui-angular/src/lib/services/overlay/overlay.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -661,9 +661,11 @@ export class IgxOverlayService implements OnDestroy {
661661
this._overlayInfos.splice(index, 1);
662662

663663
// this._overlayElement.parentElement check just for tests that manually delete the element
664-
if (this._overlayInfos.length === 0 && this._overlayElement && this._overlayElement.parentElement) {
665-
this._overlayElement.parentElement.removeChild(this._overlayElement);
666-
this._overlayElement = null;
664+
if (this._overlayInfos.length === 0) {
665+
if (this._overlayElement && this._overlayElement.parentElement) {
666+
this._overlayElement.parentElement.removeChild(this._overlayElement);
667+
this._overlayElement = null;
668+
}
667669
this.removeCloseOnEscapeListener();
668670
}
669671
}

0 commit comments

Comments
 (0)