@@ -10,7 +10,7 @@ import {
1010 ViewEncapsulation
1111} from '@angular/core' ;
1212import { TestBed , fakeAsync , tick , inject , waitForAsync } from '@angular/core/testing' ;
13- import { BrowserModule , By } from '@angular/platform-browser' ;
13+ import { BrowserModule } from '@angular/platform-browser' ;
1414import { NoopAnimationsModule } from '@angular/platform-browser/animations' ;
1515import { IgxOverlayService } from './overlay' ;
1616import { IgxToggleDirective , IgxToggleModule , IgxOverlayOutletDirective } from './../../directives/toggle/toggle.directive' ;
@@ -220,17 +220,34 @@ describe('igxOverlay', () => {
220220 beforeEach ( ( ) => {
221221 mockElement = {
222222 style : { visibility : '' , cursor : '' , transitionDuration : '' } ,
223+ children : [ ] ,
223224 classList : { add : ( ) => { } , remove : ( ) => { } } ,
224- appendChild : ( ) => { } ,
225- removeChild : ( ) => { } ,
225+ appendChild ( element : any ) {
226+ this . children . push ( element ) ;
227+ } ,
228+ removeChild ( element : any ) {
229+ const index = this . children . indexOf ( element ) ;
230+ if ( index !== - 1 ) {
231+ this . children . splice ( index , 1 ) ;
232+ }
233+ } ,
226234 addEventListener : ( type : string , listener : ( this : HTMLElement , ev : MouseEvent ) => any ) => { } ,
227235 removeEventListener : ( type : string , listener : ( this : HTMLElement , ev : MouseEvent ) => any ) => { } ,
228236 getBoundingClientRect : ( ) => ( { width : 10 , height : 10 } ) ,
229- insertBefore : ( newChild : HTMLDivElement , refChild : Node ) => { } ,
230- contains : ( ) => { }
237+ insertBefore ( newChild : HTMLDivElement , refChild : Node ) {
238+ let refIndex = this . children . indexOf ( refChild ) ;
239+ if ( refIndex === - 1 ) {
240+ refIndex = 0 ;
241+ }
242+ this . children . splice ( refIndex , 0 , newChild ) ;
243+ } ,
244+ contains ( element : any ) {
245+ return this . children . indexOf ( element ) !== - 1 ;
246+ }
231247 } ;
232248 mockElement . parent = mockElement ;
233249 mockElement . parentElement = mockElement ;
250+ mockElement . parentNode = mockElement ;
234251 mockElementRef = { nativeElement : mockElement } ;
235252 mockFactoryResolver = {
236253 resolveComponentFactory : ( c : any ) => ( {
@@ -247,11 +264,34 @@ describe('igxOverlay', () => {
247264 mockAnimationBuilder = { } ;
248265 mockDocument = {
249266 body : mockElement ,
267+ listeners : { } ,
250268 defaultView : mockElement ,
269+ // this is used be able to properly invoke rxjs `fromEvent` operator, which, turns out
270+ // just adds an event listener to the element and emits accordingly
271+ dispatchEvent ( event : KeyboardEvent ) {
272+ const type = event . type ;
273+ if ( this . listeners [ type ] ) {
274+ this . listeners [ type ] . forEach ( listener => {
275+ listener ( event ) ;
276+ } ) ;
277+ }
278+ } ,
251279 createElement : ( ) => mockElement ,
252280 appendChild : ( ) => { } ,
253- addEventListener : ( type : string , listener : ( this : HTMLElement , ev : MouseEvent ) => any ) => { } ,
254- removeEventListener : ( type : string , listener : ( this : HTMLElement , ev : MouseEvent ) => any ) => { }
281+ addEventListener ( type : string , listener : ( this : HTMLElement , ev : MouseEvent ) => any ) {
282+ if ( ! this . listeners [ type ] ) {
283+ this . listeners [ type ] = [ ] ;
284+ }
285+ this . listeners [ type ] . push ( listener ) ;
286+ } ,
287+ removeEventListener ( type : string , listener : ( this : HTMLElement , ev : MouseEvent ) => any ) {
288+ if ( this . listeners [ type ] ) {
289+ const index = this . listeners [ type ] . indexOf ( listener ) ;
290+ if ( index !== - 1 ) {
291+ this . listeners [ type ] . splice ( index , 1 ) ;
292+ }
293+ }
294+ }
255295 } ;
256296 mockNgZone = { } ;
257297 mockPlatformUtil = { isIOS : false } ;
@@ -285,6 +325,40 @@ describe('igxOverlay', () => {
285325 overlay . hide ( id ) ;
286326 expect ( mockDocument . body . style . cursor ) . toEqual ( 'initialCursorValue' ) ;
287327 } ) ;
328+
329+ it ( 'Should clear listener for escape key when overlay settings have outlet specified' , ( ) => {
330+ const mockOverlaySettings : OverlaySettings = {
331+ modal : false ,
332+ closeOnEscape : true ,
333+ outlet : mockElement ,
334+ positionStrategy : new GlobalPositionStrategy ( { openAnimation : null , closeAnimation : null } )
335+ } ;
336+ const id = overlay . attach ( mockElementRef , mockOverlaySettings ) ;
337+
338+ // show the overlay
339+ overlay . show ( id ) ;
340+
341+ // expect escape listener to be added to document
342+ expect ( mockDocument . listeners [ 'keydown' ] . length > 0 ) . toBeTruthy ( ) ;
343+ const keydownListener = mockDocument . listeners [ 'keydown' ] [ 0 ] ;
344+
345+ spyOn ( overlay , 'hide' ) . and . callThrough ( ) ;
346+ spyOn ( mockDocument , 'removeEventListener' ) . and . callThrough ( ) ;
347+
348+ mockDocument . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Escape' } ) ) ;
349+
350+ // expect hide to have been called
351+ expect ( overlay . hide ) . toHaveBeenCalledTimes ( 1 ) ;
352+ expect ( mockDocument . removeEventListener ) . toHaveBeenCalled ( ) ;
353+
354+ // the keydown listener is now removed
355+ expect ( mockDocument . removeEventListener ) . toHaveBeenCalledWith ( 'keydown' , keydownListener , undefined ) ;
356+
357+ // fire event again, expecting hide NOT to be fired again
358+ mockDocument . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Escape' } ) ) ;
359+ expect ( overlay . hide ) . toHaveBeenCalledTimes ( 1 ) ;
360+ expect ( mockDocument . listeners [ 'keydown' ] . length ) . toBe ( 0 ) ;
361+ } ) ;
288362 } ) ;
289363
290364 describe ( 'Unit Tests: ' , ( ) => {
0 commit comments