Merge remote-tracking branch 'origin/open254'

This commit is contained in:
Pete Richards 2016-07-18 11:35:43 -07:00
commit 99ec188813
11 changed files with 212 additions and 148 deletions

View File

@ -44,30 +44,31 @@ define(
periodically with the progress of an ongoing process. periodically with the progress of an ongoing process.
*/ */
$scope.launchProgress = function (knownProgress) { $scope.launchProgress = function (knownProgress) {
var model = { var dialog,
title: "Progress Dialog Example", model = {
progress: 0, title: "Progress Dialog Example",
hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.", progress: 0,
actionText: "Calculating...", hint: "Do not navigate away from this page or close this browser tab while this operation is in progress.",
unknownProgress: !knownProgress, actionText: "Calculating...",
unknownDuration: false, unknownProgress: !knownProgress,
severity: "info", unknownDuration: false,
options: [ severity: "info",
{ options: [
label: "Cancel Operation", {
callback: function () { label: "Cancel Operation",
$log.debug("Operation cancelled"); callback: function () {
dialogService.dismiss(); $log.debug("Operation cancelled");
dialog.dismiss();
}
},
{
label: "Do something else...",
callback: function () {
$log.debug("Something else pressed");
}
} }
}, ]
{ };
label: "Do something else...",
callback: function () {
$log.debug("Something else pressed");
}
}
]
};
function incrementProgress() { function incrementProgress() {
model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30)); model.progress = Math.min(100, Math.floor(model.progress + Math.random() * 30));
@ -77,7 +78,9 @@ define(
} }
} }
if (dialogService.showBlockingMessage(model)) { dialog = dialogService.showBlockingMessage(model);
if (dialog) {
//Do processing here //Do processing here
model.actionText = "Processing 100 objects..."; model.actionText = "Processing 100 objects...";
if (knownProgress) { if (knownProgress) {
@ -93,29 +96,31 @@ define(
Demonstrates launching an error dialog Demonstrates launching an error dialog
*/ */
$scope.launchError = function () { $scope.launchError = function () {
var model = { var dialog,
title: "Error Dialog Example", model = {
actionText: "Something happened, and it was not good.", title: "Error Dialog Example",
severity: "error", actionText: "Something happened, and it was not good.",
options: [ severity: "error",
{ options: [
label: "Try Again", {
callback: function () { label: "Try Again",
$log.debug("Try Again Pressed"); callback: function () {
dialogService.dismiss(); $log.debug("Try Again Pressed");
dialog.dismiss();
}
},
{
label: "Cancel",
callback: function () {
$log.debug("Cancel Pressed");
dialog.dismiss();
}
} }
}, ]
{ };
label: "Cancel", dialog = dialogService.showBlockingMessage(model);
callback: function () {
$log.debug("Cancel Pressed");
dialogService.dismiss();
}
}
]
};
if (!dialogService.showBlockingMessage(model)) { if (!dialog) {
$log.error("Could not display modal dialog"); $log.error("Could not display modal dialog");
} }
}; };
@ -124,22 +129,25 @@ define(
Demonstrates launching an error dialog Demonstrates launching an error dialog
*/ */
$scope.launchInfo = function () { $scope.launchInfo = function () {
var model = { var dialog,
title: "Info Dialog Example", model = {
actionText: "This is an example of a blocking info" + title: "Info Dialog Example",
" dialog. This dialog can be used to draw the user's" + actionText: "This is an example of a blocking info" +
" attention to an event.", " dialog. This dialog can be used to draw the user's" +
severity: "info", " attention to an event.",
primaryOption: { severity: "info",
label: "OK", primaryOption: {
callback: function () { label: "OK",
$log.debug("OK Pressed"); callback: function () {
dialogService.dismiss(); $log.debug("OK Pressed");
dialog.dismiss();
}
} }
} };
};
if (!dialogService.showBlockingMessage(model)) { dialog = dialogService.showBlockingMessage(model);
if (!dialog) {
$log.error("Could not display modal dialog"); $log.error("Could not display modal dialog");
} }
}; };

View File

@ -39,25 +39,28 @@ define(
this.overlayService = overlayService; this.overlayService = overlayService;
this.$q = $q; this.$q = $q;
this.$log = $log; this.$log = $log;
this.overlay = undefined; this.activeOverlay = undefined;
this.dialogVisible = false;
} }
// Stop showing whatever overlay is currently active /**
// (e.g. because the user hit cancel) * @private
DialogService.prototype.dismiss = function () { */
var overlay = this.overlay; DialogService.prototype.dismissOverlay = function (overlay) {
if (overlay) { //Dismiss the overlay
overlay.dismiss(); overlay.dismiss();
//If dialog is the current active one, dismiss it
if (overlay === this.activeOverlay) {
this.activeOverlay = undefined;
} }
this.dialogVisible = false;
}; };
DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) { DialogService.prototype.getDialogResponse = function (key, model, resultGetter, typeClass) {
// We will return this result as a promise, because user // We will return this result as a promise, because user
// input is asynchronous. // input is asynchronous.
var deferred = this.$q.defer(), var deferred = this.$q.defer(),
self = this; self = this,
overlay;
// Confirm function; this will be passed in to the // Confirm function; this will be passed in to the
// overlay-dialog template and associated with a // overlay-dialog template and associated with a
@ -65,9 +68,7 @@ define(
function confirm(value) { function confirm(value) {
// Pass along the result // Pass along the result
deferred.resolve(resultGetter ? resultGetter() : value); deferred.resolve(resultGetter ? resultGetter() : value);
self.dismissOverlay(overlay);
// Stop showing the dialog
self.dismiss();
} }
// Cancel function; this will be passed in to the // Cancel function; this will be passed in to the
@ -75,7 +76,7 @@ define(
// Cancel or X button click // Cancel or X button click
function cancel() { function cancel() {
deferred.reject(); deferred.reject();
self.dismiss(); self.dismissOverlay(overlay);
} }
// Add confirm/cancel callbacks // Add confirm/cancel callbacks
@ -85,15 +86,11 @@ define(
if (this.canShowDialog(model)) { if (this.canShowDialog(model)) {
// Add the overlay using the OverlayService, which // Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM // will handle actual insertion into the DOM
this.overlay = this.overlayService.createOverlay( overlay = this.activeOverlay = this.overlayService.createOverlay(
key, key,
model, model,
typeClass || "t-dialog" typeClass || "t-dialog"
); );
// Track that a dialog is already visible, to
// avoid spawning multiple dialogs at once.
this.dialogVisible = true;
} else { } else {
deferred.reject(); deferred.reject();
} }
@ -156,7 +153,7 @@ define(
* otherwise * otherwise
*/ */
DialogService.prototype.canShowDialog = function (dialogModel) { DialogService.prototype.canShowDialog = function (dialogModel) {
if (this.dialogVisible) { if (this.activeOverlay) {
// Only one dialog should be shown at a time. // Only one dialog should be shown at a time.
// The application design should be such that // The application design should be such that
// we never even try to do this. // we never even try to do this.
@ -183,6 +180,11 @@ define(
* button is clicked * button is clicked
*/ */
/**
* @typedef DialogHandle
* @property {function} dismiss a function to dismiss the given dialog
*/
/** /**
* A description of the model options that may be passed to the * A description of the model options that may be passed to the
* showBlockingMessage method. Note that the DialogModel desribed * showBlockingMessage method. Note that the DialogModel desribed
@ -222,21 +224,26 @@ define(
* the user can take if necessary * the user can take if necessary
* @param {DialogModel} dialogModel defines options for the dialog * @param {DialogModel} dialogModel defines options for the dialog
* @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class * @param {typeClass} string tells overlayService that this overlay should use appropriate CSS class
* @returns {boolean} * @returns {boolean | {DialogHandle}}
*/ */
DialogService.prototype.showBlockingMessage = function (dialogModel) { DialogService.prototype.showBlockingMessage = function (dialogModel) {
if (this.canShowDialog(dialogModel)) { if (this.canShowDialog(dialogModel)) {
// Add the overlay using the OverlayService, which // Add the overlay using the OverlayService, which
// will handle actual insertion into the DOM // will handle actual insertion into the DOM
this.overlay = this.overlayService.createOverlay( var self = this,
"overlay-blocking-message", overlay = this.overlayService.createOverlay(
dialogModel, "overlay-blocking-message",
"t-dialog-sm" dialogModel,
); "t-dialog-sm"
// Track that a dialog is already visible, to );
// avoid spawning multiple dialogs at once.
this.dialogVisible = true; this.activeOverlay = overlay;
return true;
return {
dismiss: function () {
self.dismissOverlay(overlay);
}
};
} else { } else {
return false; return false;
} }

View File

@ -122,7 +122,7 @@ define(
it("invokes the overlay service with the correct parameters when" + it("invokes the overlay service with the correct parameters when" +
" a blocking dialog is requested", function () { " a blocking dialog is requested", function () {
var dialogModel = {}; var dialogModel = {};
expect(dialogService.showBlockingMessage(dialogModel)).toBe(true); expect(dialogService.showBlockingMessage(dialogModel)).not.toBe(false);
expect(mockOverlayService.createOverlay).toHaveBeenCalledWith( expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
"overlay-blocking-message", "overlay-blocking-message",
dialogModel, dialogModel,
@ -130,6 +130,45 @@ define(
); );
}); });
describe("the blocking message dialog", function () {
var dialogModel = {};
var dialogHandle;
beforeEach(function () {
dialogHandle = dialogService.showBlockingMessage(dialogModel);
});
it("returns a handle to the dialog", function () {
expect(dialogHandle).not.toBe(undefined);
});
it("dismissing the dialog dismisses the overlay", function () {
dialogHandle.dismiss();
expect(mockOverlay.dismiss).toHaveBeenCalled();
});
it("individual dialogs can be dismissed", function () {
var secondDialogHandle,
secondMockOverlay;
dialogHandle.dismiss();
secondMockOverlay = jasmine.createSpyObj(
"overlay",
["dismiss"]
);
mockOverlayService.createOverlay.andReturn(secondMockOverlay);
secondDialogHandle = dialogService.showBlockingMessage(dialogModel);
//Dismiss the first dialog. It should only dismiss if it
// is active
dialogHandle.dismiss();
expect(secondMockOverlay.dismiss).not.toHaveBeenCalled();
secondDialogHandle.dismiss();
expect(secondMockOverlay.dismiss).toHaveBeenCalled();
});
});
}); });
} }
); );

View File

@ -1,10 +1,11 @@
define([], function () { define([], function () {
function SaveInProgressDialog(dialogService) { function SaveInProgressDialog(dialogService) {
this.dialogService = dialogService; this.dialogService = dialogService;
this.dialog = undefined;
} }
SaveInProgressDialog.prototype.show = function () { SaveInProgressDialog.prototype.show = function () {
this.dialogService.showBlockingMessage({ this.dialog = this.dialogService.showBlockingMessage({
title: "Saving...", title: "Saving...",
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.", hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
unknownProgress: true, unknownProgress: true,
@ -13,7 +14,9 @@ define([], function () {
}; };
SaveInProgressDialog.prototype.hide = function () { SaveInProgressDialog.prototype.hide = function () {
this.dialogService.dismiss(); if (this.dialog) {
this.dialog.dismiss();
}
}; };
return SaveInProgressDialog; return SaveInProgressDialog;

View File

@ -70,7 +70,7 @@ define(
}; };
dialogService = jasmine.createSpyObj( dialogService = jasmine.createSpyObj(
"dialogService", "dialogService",
["showBlockingMessage", "dismiss"] ["showBlockingMessage"]
); );
mockDomainObject.hasCapability.andReturn(true); mockDomainObject.hasCapability.andReturn(true);
@ -111,17 +111,28 @@ define(
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate"); expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
}); });
it("shows a dialog while saving", function () { describe("a blocking dialog", function () {
mockEditorCapability.save.andReturn(new Promise(function () {})); var mockDialogHandle;
action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
expect(dialogService.dismiss).not.toHaveBeenCalled();
});
it("hides a dialog when saving is complete", function () { beforeEach(function () {
action.perform(); mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
expect(dialogService.showBlockingMessage).toHaveBeenCalled(); dialogService.showBlockingMessage.andReturn(mockDialogHandle);
expect(dialogService.dismiss).toHaveBeenCalled(); });
it("shows a dialog while saving", function () {
mockEditorCapability.save.andReturn(new Promise(function () {
}));
action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
});
it("hides a dialog when saving is complete", function () {
action.perform();
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
});
}); });
}); });

View File

@ -101,8 +101,7 @@ define(
"dialogService", "dialogService",
[ [
"getUserInput", "getUserInput",
"showBlockingMessage", "showBlockingMessage"
"dismiss"
] ]
); );
mockDialogService.getUserInput.andReturn(mockPromise(undefined)); mockDialogService.getUserInput.andReturn(mockPromise(undefined));
@ -171,25 +170,33 @@ define(
expect(mockDialogService.getUserInput).toHaveBeenCalled(); expect(mockDialogService.getUserInput).toHaveBeenCalled();
}); });
it("shows a blocking dialog while waiting for save", function () { describe("a blocking dialog", function () {
mockEditorCapability.save.andReturn(new Promise(function () {})); var mockDialogHandle;
action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogService.dismiss).not.toHaveBeenCalled();
});
it("hides the blocking dialog after saving", function () { beforeEach(function () {
var mockCallback = jasmine.createSpy(); mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
action.perform().then(mockCallback); mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
waitsFor(function () {
return mockCallback.calls.length > 0;
}); });
runs(function () {
expect(mockDialogService.dismiss).toHaveBeenCalled(); it("indicates that a save is taking place", function () {
mockEditorCapability.save.andReturn(new Promise(function () {}));
action.perform();
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
});
it("is hidden after saving", function () {
var mockCallback = jasmine.createSpy();
action.perform().then(mockCallback);
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
waitsFor(function () {
return mockCallback.calls.length > 0;
});
runs(function () {
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
});
}); });
}); });
}); });
} }
); );

View File

@ -54,17 +54,17 @@ define(
}; };
$scope.maximize = function (notification) { $scope.maximize = function (notification) {
if (notification.model.severity !== "info") { if (notification.model.severity !== "info") {
var dialog;
notification.model.cancel = function () { notification.model.cancel = function () {
dialogService.dismiss(); dialog.dismiss();
}; };
//If the notification is dismissed by the user, close //If the notification is dismissed by the user, close
// the dialog. // the dialog.
notification.onDismiss(function () { notification.onDismiss(function () {
dialogService.dismiss(); dialog.dismiss();
}); });
dialogService.showBlockingMessage(notification.model); dialog = dialogService.showBlockingMessage(notification.model);
} }
}; };
} }

View File

@ -49,9 +49,6 @@ define(
//Launch the message list dialog with the models //Launch the message list dialog with the models
// from the notifications // from the notifications
messages: notificationService.notifications messages: notificationService.notifications
},
cancel: function () {
dialogService.dismiss();
} }
}); });

View File

@ -54,22 +54,7 @@ define(
expect(mockDialogService.getDialogResponse).toHaveBeenCalled(); expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
expect(mockDialogService.getDialogResponse.mostRecentCall.args[0]).toBe('overlay-message-list'); expect(mockDialogService.getDialogResponse.mostRecentCall.args[0]).toBe('overlay-message-list');
expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].dialog).toBeDefined(); expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].dialog).toBeDefined();
expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
//Invoke the cancel callback
mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
expect(mockDialogService.dismiss).toHaveBeenCalled();
}); });
it("provides a means of dismissing the message list", function () {
expect(mockScope.showNotificationsList).toBeDefined();
mockScope.showNotificationsList();
expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
expect(mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel).toBeDefined();
//Invoke the cancel callback
mockDialogService.getDialogResponse.mostRecentCall.args[1].cancel();
expect(mockDialogService.dismiss).toHaveBeenCalled();
});
}); });
} }
); );

View File

@ -86,7 +86,9 @@ define(
severity: "info" severity: "info"
}); });
} else if (phase.toLowerCase() === "copying") { } else if (phase.toLowerCase() === "copying") {
this.dialogService.dismiss(); if (this.dialog) {
this.dialog.dismiss();
}
if (!this.notification) { if (!this.notification) {
this.notification = this.notificationService this.notification = this.notificationService
.notify({ .notify({
@ -115,7 +117,8 @@ define(
} }
function error(errorDetails) { function error(errorDetails) {
var errorMessage = { var errorDialog,
errorMessage = {
title: "Error copying objects.", title: "Error copying objects.",
severity: "error", severity: "error",
hint: errorDetails.message, hint: errorDetails.message,
@ -123,12 +126,12 @@ define(
options: [{ options: [{
label: "OK", label: "OK",
callback: function () { callback: function () {
self.dialogService.dismiss(); errorDialog.dismiss();
} }
}] }]
}; };
self.dialogService.dismiss(); self.dialog.dismiss();
if (self.notification) { if (self.notification) {
self.notification.dismiss(); // Clear the progress notification self.notification.dismiss(); // Clear the progress notification
} }
@ -136,7 +139,7 @@ define(
//Show a minimized notification of error for posterity //Show a minimized notification of error for posterity
self.notificationService.notify(errorMessage); self.notificationService.notify(errorMessage);
//Display a blocking message //Display a blocking message
self.dialogService.showBlockingMessage(errorMessage); errorDialog = self.dialogService.showBlockingMessage(errorMessage);
} }
function notification(details) { function notification(details) {

View File

@ -44,6 +44,7 @@ define(
notificationService, notificationService,
notification, notification,
dialogService, dialogService,
mockDialog,
mockLog, mockLog,
abstractComposePromise, abstractComposePromise,
progress = {phase: "copying", totalObjects: 10, processed: 1}; progress = {phase: "copying", totalObjects: 10, processed: 1};
@ -120,9 +121,12 @@ define(
.andReturn(locationServicePromise); .andReturn(locationServicePromise);
dialogService = jasmine.createSpyObj('dialogService', dialogService = jasmine.createSpyObj('dialogService',
['showBlockingMessage', 'dismiss'] ['showBlockingMessage']
); );
mockDialog = jasmine.createSpyObj("dialog", ["dismiss"]);
dialogService.showBlockingMessage.andReturn(mockDialog);
notification = jasmine.createSpyObj('notification', notification = jasmine.createSpyObj('notification',
['dismiss', 'model'] ['dismiss', 'model']
); );