diff --git a/platform/commonUI/dialog/bundle.js b/platform/commonUI/dialog/bundle.js index 009cafba0e..b191e1b0fe 100644 --- a/platform/commonUI/dialog/bundle.js +++ b/platform/commonUI/dialog/bundle.js @@ -53,7 +53,8 @@ define([ "depends": [ "overlayService", "$q", - "$log" + "$log", + "$document" ] }, { diff --git a/platform/commonUI/dialog/src/DialogService.js b/platform/commonUI/dialog/src/DialogService.js index 61e3673dd6..e744549ffa 100644 --- a/platform/commonUI/dialog/src/DialogService.js +++ b/platform/commonUI/dialog/src/DialogService.js @@ -35,11 +35,15 @@ define( * @memberof platform/commonUI/dialog * @constructor */ - function DialogService(overlayService, $q, $log) { + function DialogService(overlayService, $q, $log, $document) { this.overlayService = overlayService; this.$q = $q; this.$log = $log; this.activeOverlay = undefined; + + this.findBody = function () { + return $document.find('body'); + }; } /** @@ -76,13 +80,22 @@ define( // Cancel or X button click function cancel() { deferred.reject(); + self.findBody().off('keydown', handleEscKeydown); self.dismissOverlay(overlay); } + function handleEscKeydown(event){ + if (event.keyCode === 27) { + cancel(); + } + } + // Add confirm/cancel callbacks model.confirm = confirm; model.cancel = cancel; + this.findBody().on('keydown', handleEscKeydown); + if (this.canShowDialog(model)) { // Add the overlay using the OverlayService, which // will handle actual insertion into the DOM diff --git a/platform/commonUI/dialog/test/DialogServiceSpec.js b/platform/commonUI/dialog/test/DialogServiceSpec.js index e8b987b996..e7cde7b24c 100644 --- a/platform/commonUI/dialog/test/DialogServiceSpec.js +++ b/platform/commonUI/dialog/test/DialogServiceSpec.js @@ -33,6 +33,8 @@ define( mockLog, mockOverlay, mockDeferred, + mockDocument, + mockBody, dialogService; beforeEach(function () { @@ -56,6 +58,16 @@ define( "deferred", ["resolve", "reject"] ); + mockDocument = jasmine.createSpyObj( + "$document", + ["find"] + ); + mockBody = angular.element(document.createElement('body')); + spyOn(mockBody, 'on').andCallThrough(); + spyOn(mockBody, 'off').andCallThrough(); + + mockDocument.find.andReturn(mockBody); + mockDeferred.promise = "mock promise"; mockQ.defer.andReturn(mockDeferred); @@ -64,7 +76,8 @@ define( dialogService = new DialogService( mockOverlayService, mockQ, - mockLog + mockLog, + mockDocument ); }); @@ -130,6 +143,38 @@ define( ); }); + it("adds a keydown event listener to the body", function () { + dialogService.getUserInput({}, {}); + expect(mockDocument.find).toHaveBeenCalledWith("body"); + expect(mockBody.on).toHaveBeenCalledWith("keydown", jasmine.any(Function)); + }); + + it("destroys the event listener when the dialog is cancelled", function () { + dialogService.getUserInput({}, {}); + mockOverlayService.createOverlay.mostRecentCall.args[1].cancel(); + expect(mockBody.off).toHaveBeenCalledWith("keydown", jasmine.any(Function)); + }); + + it("cancels the dialog when an escape keydown event is triggered", function () { + dialogService.getUserInput({}, {}); + mockBody.triggerHandler({ + type: 'keydown', + keyCode: 27 + }); + expect(mockDeferred.reject).toHaveBeenCalled(); + expect(mockDeferred.resolve).not.toHaveBeenCalled(); + }); + + it("ignores non escape keydown events", function () { + dialogService.getUserInput({}, {}); + mockBody.triggerHandler({ + type: 'keydown', + keyCode: 13 + }); + expect(mockDeferred.reject).not.toHaveBeenCalled(); + expect(mockDeferred.resolve).not.toHaveBeenCalled(); + }); + describe("the blocking message dialog", function () { var dialogModel = {}; var dialogHandle;