@@ -10,7 +10,7 @@ import {
1010 ViewEncapsulation
1111} from '@angular/core' ;
1212import { TestBed , fakeAsync , tick , async , inject } 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' ;
@@ -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: ' , ( ) => {
0 commit comments