Added basic notifications on copy

This commit is contained in:
Andrew Henry
2015-10-28 17:16:53 -07:00
25 changed files with 503 additions and 423 deletions

View File

@@ -1,9 +1,5 @@
<div ng-controller="MessageController"
class="l-message"
ng-class="{
'message-severity-info': ngModel.severity===MessageSeverity.INFO,
'message-severity-error': ngModel.severity===MessageSeverity.ERROR,
'message-severity-alert': ngModel.severity===MessageSeverity.ALERT}">
<div class="l-message"
ng-class="'message-severity-' + ngModel.severity">
<div class="ui-symbol type-icon message-type"></div>
<div class="message-contents">
<div class="top-bar">
@@ -19,15 +15,15 @@
ng-show="ngModel.progress !== undefined || ngModel.unknownProgress"></mct-include>
</div>
<div class="bottom-bar">
<a ng-repeat="dialogAction in ngModel.actions"
<a ng-repeat="dialogOption in ngModel.options"
class="s-btn major"
ng-click="dialogAction.action()">
{{dialogAction.label}}
ng-click="dialogOption.callback()">
{{dialogOption.label}}
</a>
<a class="s-btn major"
ng-if="ngModel.primaryAction"
ng-click="ngModel.primaryAction.action()">
{{ngModel.primaryAction.label}}
ng-if="ngModel.primaryOption"
ng-click="ngModel.primaryOption.callback()">
{{ngModel.primaryOption.label}}
</a>
</div>

View File

@@ -178,7 +178,7 @@ define(
* A user action that can be performed from a blocking dialog. These
* actions will be rendered as buttons within a blocking dialog.
*
* @typedef DialogAction
* @typedef DialogOption
* @property {string} label a label to be displayed as the button
* text for this action
* @property {function} action a function to be called when the
@@ -207,12 +207,12 @@ define(
* impossible to provide an estimate for. Providing a true value for
* this attribute will indicate to the user that the progress and
* duration cannot be estimated.
* @property {DialogAction} primaryAction an action that will
* @property {DialogOption} primaryOption an action that will
* be added to the dialog as a button. The primary action can be
* used as the suggested course of action for the user. Making it
* distinct from other actions allows it to be styled differently,
* and treated preferentially in banner mode.
* @property {DialogAction[]} actions a list of actions that will
* @property {DialogOption[]} options a list of actions that will
* be added to the dialog as buttons.
*/

View File

@@ -121,6 +121,17 @@ define(
);
});
it("invokes the overlay service with the correct parameters when" +
" a blocking dialog is requested", function() {
var dialogModel = {};
expect(dialogService.showBlockingMessage(dialogModel)).toBe(true);
expect(mockOverlayService.createOverlay).toHaveBeenCalledWith(
"overlay-blocking-message",
dialogModel,
"t-dialog-sm"
);
});
});
}
);
);

View File

@@ -1,23 +1,20 @@
<div ng-controller="BannerController" ng-show="active.notification"
class="l-message-banner s-message-banner" ng-class="{
'info': active.notification.severity===MessageSeverity.INFO,
'alert': active.notification.severity===MessageSeverity.ALERT,
'error': active.notification.severity===MessageSeverity.ERROR,
'minimized': active.notification.minimized,
'new': !active.notification.minimized}"
class="l-message-banner s-message-banner {{active.notification.model.severity}}" ng-class="{
'minimized': active.notification.model.minimized,
'new': !active.notification.model.minimized}"
ng-click="maximize(active.notification)">
<span class="banner-elem label">
{{active.notification.title}}
{{active.notification.model.title}}
</span>
<span ng-show="active.notification.progress !== undefined || active.notification.unknownProgress">
<span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress">
<mct-include key="'progress-bar'" class="banner-elem"
ng-model="active.notification">
ng-model="active.notification.model">
</mct-include>
</span>
<a ng-hide="active.notification.primaryAction === undefined"
<a ng-hide="active.notification.model.primaryOption === undefined"
class="banner-elem l-action s-action"
ng-click="action(active.notification.primaryAction.action, $event)">
{{active.notification.primaryAction.label}}
ng-click="action(active.notification.model.primaryOption.callback, $event)">
{{active.notification.model.primaryOption.label}}
</a>
<a class="banner-elem ui-symbol close" ng-click="dismiss(active.notification, $event)">
&#x78;</a>

View File

@@ -22,26 +22,45 @@
/*global define*/
define(
['../../../notification/src/MessageSeverity'],
function (MessageSeverity) {
[],
function () {
"use strict";
/**
* A controller for banner notifications. Banner notifications are a
* non-blocking way of drawing the user's attention to an event such
* as system errors, or the progress or successful completion of an
* ongoing task. This controller provides scoped functions for
* dismissing and 'maximizing' notifications. See {@link NotificationService}
* for more details on Notifications.
*
* @param $scope
* @param notificationService
* @param dialogService
* @constructor
*/
function BannerController($scope, notificationService, dialogService) {
$scope.active = notificationService.active;
$scope.MessageSeverity = MessageSeverity;
$scope.action = function (action, $event){
/*
Prevents default 'maximize' behaviour when clicking on
notification button
*/
$event.stopPropagation();
return action();
};
$scope.dismiss = function(notification, $event) {
$event.stopPropagation();
notificationService.dismissOrMinimize(notification);
notification.dismissOrMinimize();
};
$scope.maximize = function(notification) {
if (notification.severity > MessageSeverity.INFO){
notification.cancel = function(){
if (notification.model.severity !== "info"){
notification.model.cancel = function(){
dialogService.dismiss();
};
dialogService.showBlockingMessage(notification);
dialogService.showBlockingMessage(notification.model);
}
};
}

View File

@@ -21,11 +21,6 @@
}
],
"controllers": [
{
"key": "MessageController",
"implementation": "MessageController.js",
"depends": ["$scope"]
},
{
"key": "NotificationIndicatorController",
"implementation": "NotificationIndicatorController.js",
@@ -47,4 +42,4 @@
}
]
}
}
}

View File

@@ -1,8 +1,5 @@
<span ng-show="notifications.length > 0" class="status block"
ng-class="{
'info': highest.severity===MessageSeverity.INFO,
'error': highest.severity===MessageSeverity.ERROR,
'alert': highest.severity===MessageSeverity.ALERT }"
ng-class="highest.severity"
ng-controller="NotificationIndicatorController">
<span class="ui-symbol status-indicator">&#xe610;</span>
<span class="label">

View File

@@ -1,34 +0,0 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define(
['./MessageSeverity'],
function (MessageSeverity) {
"use strict";
function MessageController($scope) {
$scope.MessageSeverity = MessageSeverity;
}
return MessageController;
}
);

View File

@@ -1,11 +0,0 @@
/**
* Created by akhenry on 10/7/15.
*/
/*global define*/
define(function(){
return {
INFO: 0,
ALERT: 1,
ERROR: 2
};
});

View File

@@ -26,25 +26,10 @@ define(
function () {
"use strict";
function NotificationIndicator() {
}
function NotificationIndicator() {}
NotificationIndicator.template = 'notificationIndicatorTemplate';
NotificationIndicator.prototype.getGlyph = function () {
return "A";
};
NotificationIndicator.prototype.getGlyphClass = function () {
return 'caution';
};
NotificationIndicator.prototype.getText = function () {
return "Notifications";
};
NotificationIndicator.prototype.getDescription = function () {
return "Notifications";
};
return NotificationIndicator;
}
);

View File

@@ -22,34 +22,38 @@
/*global define*/
define(
['./MessageSeverity'],
function (MessageSeverity) {
[],
function () {
"use strict";
/**
* Provides an indicator that is visible when there are
* banner notifications that have been minimized. Will also indicate
* the number of notifications. Notifications can be viewed by
* clicking on the indicator to launch a dialog showing a list of
* notifications.
* @param $scope
* @param notificationService
* @param dialogService
* @constructor
*/
function NotificationIndicatorController($scope, notificationService, dialogService) {
$scope.notifications = notificationService.notifications;
$scope.highest = notificationService.highest;
$scope.MessageSeverity = MessageSeverity;
/**
* Launch a dialog showing a list of current notifications.
*/
$scope.showNotificationsList = function(){
var model = {
title: "Messages",
severity: MessageSeverity.INFO,
actions: [
{
label: "Done",
action: function () {
dialogService.dismiss();
}
}
],
messages: []
};
model.messages = notificationService.notifications;
dialogService.getDialogResponse('overlay-message-list', {
dialog: model,
dialog: {
title: "Messages",
//Launch the message list dialog with the models
// from the notifications
messages: notificationService.notifications && notificationService.notifications.map(function(notification){
return notification.model;
})
},
cancel: function(){
dialogService.dismiss();
}
@@ -60,3 +64,4 @@ define(
return NotificationIndicatorController;
}
);

View File

@@ -29,21 +29,21 @@
* dialogs so that the same information can be provided in a dialog
* and then minimized to a banner notification if needed.
*
* @namespace platform/commonUI/dialog
* @namespace platform/commonUI/notification
*/
define(
["./MessageSeverity"],
function (MessageSeverity) {
[],
function () {
"use strict";
/**
* A representation of a user action. Actions are provided to
* A representation of a user action. Options are provided to
* dialogs and notifications and are shown as buttons.
*
* @typedef {object} NotificationAction
* @typedef {object} NotificationOption
* @property {string} label the label to appear on the button for
* this action
* @property {function} action a callback function to be invoked
* @property {function} callback a callback function to be invoked
* when the button is clicked
*/
@@ -54,10 +54,10 @@ define(
* dialogs so that the same information can be provided in a dialog
* and then minimized to a banner notification if needed.
*
* @typedef {object} Notification
* @typedef {object} NotificationModel
* @property {string} title The title of the message
* @property {MessageSeverity} severity The importance of the
* message (eg. error, info)
* @property {string} severity The importance of the message (one of
* 'info', 'alert', or 'error' where info < alert <error)
* @property {number} progress The completion status of a task
* represented numerically
* @property {boolean} unknownProgress a boolean indicating that the
@@ -67,31 +67,55 @@ define(
* be automatically minimized or dismissed (depending on severity).
* Additionally, if the provided value is a number, it will be used
* as the delay period before being dismissed.
* @property {NotificationAction} primaryAction the default user
* @property {NotificationOption} primaryOption the default user
* response to
* this message. Will be represented as a button with the provided
* label and action. May be used by banner notifications to display
* only the most important option to users.
* @property {NotificationAction[]} actions any additional
* @property {NotificationOption[]} options any additional
* actions the user can take. Will be represented as additional buttons
* that may or may not be available from a banner.
*/
/**
* A wrapper object that is returned as a handle to a newly created
* notification. Wraps the notifications model and decorates with
* functions to dismiss or minimize the notification.
*
* @typedef {object} Notification
* @property {function} dismiss This method is added to the object
* returned by {@link NotificationService#notify} and can be used to
* dismiss this notification. Dismissing a notification will remove
* it completely and it will not appear in the notification indicator
* @property {function} minimize This method is added to the object
* returned by {@link NotificationService#notify} and can be used to
* minimize this notification. Minimizing a notification will send
* it to the notification indicator
* @property {function} dismissOrMinimize This method is added to the
* object returned by {@link NotificationService#notify}. It will
* hide the notification by either dismissing or minimizing it,
* depending on severity. Typically this is the method that should
* be used for dismissing a notification. If more control is
* required, then the minimize or dismiss functions can be called
* individually.
*/
/**
* The notification service is responsible for informing the user of
* events via the use of banner notifications.
* @memberof platform/commonUI/notification
* @constructor
* @param $timeout the Angular $timeout service
* @param DEFAULT_AUTO_DISMISS The period of time that an
* auto-dismissed message will be displayed for.
* @param MINIMIZE_TIMEOUT When notifications are minimized, a brief
* animation is shown. This animation requires some time to execute,
* so a timeout is required before the notification is hidden
* @constructor
*/
function NotificationService($timeout, DEFAULT_AUTO_DISMISS, MINIMIZE_TIMEOUT) {
this.notifications = [];
this.$timeout = $timeout;
this.highest ={ severity: MessageSeverity.INFO };
this.highest ={ severity: "info" };
this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS;
this.MINIMIZE_TIMEOUT = MINIMIZE_TIMEOUT;
@@ -102,6 +126,83 @@ define(
this.active = {};
}
/*
* Minimize a notification. The notification will still be available
* from the notification list. Typically notifications with a
* severity of 'info' should not be minimized, but rather
* dismissed. If you're not sure which is appropriate,
* use {@link Notification#dismissOrMinimize}
*/
function minimize (service, notification) {
//Check this is a known notification
var index = service.notifications.indexOf(notification);
if (service.active.timeout){
/*
Method can be called manually (clicking dismiss) or
automatically from an auto-timeout. this.active.timeout
acts as a semaphore to prevent race conditions. Cancel any
timeout in progress (for the case where a manual dismiss
has shortcut an active auto-dismiss), and clear the
semaphore.
*/
service.$timeout.cancel(service.active.timeout);
delete service.active.timeout;
}
if (index >= 0) {
notification.model.minimized=true;
//Add a brief timeout before showing the next notification
// in order to allow the minimize animation to run through.
service.$timeout(function() {
service.setActiveNotification(service.selectNextNotification());
}, service.MINIMIZE_TIMEOUT);
}
}
/*
* Completely removes a notification. This will dismiss it from the
* message banner and remove it from the list of notifications.
* Typically only notifications with a severity of info should be
* dismissed. If you're not sure whether to dismiss or minimize a
* notification, use {@link Notification#dismissOrMinimize}.
* dismiss
*/
function dismiss (service, notification) {
//Check this is a known notification
var index = service.notifications.indexOf(notification);
if (service.active.timeout){
/* Method can be called manually (clicking dismiss) or
* automatically from an auto-timeout. this.active.timeout
* acts as a semaphore to prevent race conditions. Cancel any
* timeout in progress (for the case where a manual dismiss
* has shortcut an active auto-dismiss), and clear the
* semaphore.
*/
service.$timeout.cancel(service.active.timeout);
delete service.active.timeout;
}
if (index >= 0) {
service.notifications.splice(index, 1);
}
service.setActiveNotification(service.selectNextNotification());
}
/*
* Depending on the severity of the notification will selectively
* dismiss or minimize where appropriate.
*/
function dismissOrMinimize (notification){
//For now minimize everything, and have discussion around which
//kind of messages should or should not be in the minimized
//notifications list
notification.minimize();
}
/**
* Returns the notification that is currently visible in the banner area
* @returns {Notification}
@@ -114,12 +215,15 @@ define(
* A convenience method for info notifications. Notifications
* created via this method will be auto-dismissed after a default
* wait period
* @param {Notification} notification The notification to display
* @param {NotificationModel} notificationModel Options describing the
* notification to display
* @returns {Notification} the provided notification decorated with
* functions to dismiss or minimize
*/
NotificationService.prototype.info = function (notification) {
notification.autoDismiss = notification.autoDismiss || true;
notification.severity = MessageSeverity.INFO;
this.notify(notification);
NotificationService.prototype.info = function (notificationModel) {
notificationModel.autoDismiss = notificationModel.autoDismiss || true;
notificationModel.severity = "info";
return this.notify(notificationModel);
};
/**
@@ -127,20 +231,45 @@ define(
* already active, then it will be dismissed or minimized automatically,
* and the provided notification displayed in its place.
*
* @param {Notification} notification The notification to display
* @param {NotificationModel} notificationModel The notification to
* display
* @returns {Notification} the provided notification decorated with
* functions to {@link Notification#dismiss} or {@link Notification#minimize}
*/
NotificationService.prototype.notify = function (notification) {
var self = this;
NotificationService.prototype.notify = function (notificationModel) {
var self = this,
notification,
ordinality = {
"info": 1,
"alert": 2,
"error": 3
},
activeNotification = self.active.notification;
if (notification.autoDismiss === true){
notification.autoDismiss = this.DEFAULT_AUTO_DISMISS;
notification = {
model: notificationModel,
minimize: function() {
minimize(self, notification);
},
dismiss: function(){
dismiss(self, notification);
},
dismissOrMinimize: function(){
dismissOrMinimize(notification);
}
};
notificationModel.severity = notificationModel.severity || "info";
if (notificationModel.autoDismiss === true){
notificationModel.autoDismiss = this.DEFAULT_AUTO_DISMISS;
}
if (notification.severity > this.highest.severity){
this.highest.severity = notification.severity;
if (ordinality[notificationModel.severity.toLowerCase()] > ordinality[this.highest.severity.toLowerCase()]){
this.highest.severity = notificationModel.severity;
}
this.notifications.push(notification);
/*
Check if there is already an active (ie. visible) notification
*/
@@ -159,10 +288,12 @@ define(
serviced as soon as possible.
*/
this.active.timeout = this.$timeout(function () {
self.dismissOrMinimize(self.active.notification);
activeNotification.dismissOrMinimize();
}, this.DEFAULT_AUTO_DISMISS);
}
return notification;
};
/**
@@ -180,12 +311,12 @@ define(
notifications queued for display, setup a timeout to
dismiss the dialog.
*/
if (notification && (notification.autoDismiss
if (notification && (notification.model.autoDismiss
|| this.selectNextNotification())) {
timeout = notification.autoDismiss || this.DEFAULT_AUTO_DISMISS;
timeout = notification.model.autoDismiss || this.DEFAULT_AUTO_DISMISS;
this.active.timeout = this.$timeout(function () {
self.dismissOrMinimize(notification);
notification.dismissOrMinimize();
}, timeout);
} else {
delete this.active.timeout;
@@ -208,7 +339,7 @@ define(
for (; i< this.notifications.length; i++) {
notification = this.notifications[i];
if (!notification.minimized
if (!notification.model.minimized
&& notification!== this.active.notification) {
return notification;
@@ -216,69 +347,6 @@ define(
}
};
/**
* Minimize a notification. The notification will still be available
* from the notification list. Typically notifications with a
* severity of 'info' should not be minimized, but rather
* dismissed. If you're not sure which is appropriate,
* use {@link NotificationService#dismissOrMinimize}
* @see dismiss
* @see dismissOrMinimize
* @param notification
*/
NotificationService.prototype.minimize = function (notification) {
//Check this is a known notification
var index = this.notifications.indexOf(notification),
self = this;
if (index >= 0) {
notification.minimized=true;
//Add a brief timeout before showing the next notification
// in order to allow the minimize animation to run through.
this.$timeout(function() {
self.setActiveNotification(self.selectNextNotification());
}, this.MINIMIZE_TIMEOUT);
}
};
/**
* Completely removes a notification. This will dismiss it from the
* message banner and remove it from the list of notifications.
* Typically only notifications with a severity of info should be
* dismissed. If you're not sure whether to dismiss or minimize a
* notification, use {@link NotificationService#dismissOrMinimize}.
* dismiss
* @see dismissOrMinimize
* @param notification The notification to dismiss
*/
NotificationService.prototype.dismiss = function (notification) {
//Check this is a known notification
var index = this.notifications.indexOf(notification);
if (index >= 0) {
this.notifications.splice(index, 1);
}
this.setActiveNotification(this.selectNextNotification());
};
/**
* Depending on the severity of the notification will selectively
* dismiss or minimize where appropriate.
* @see dismiss
* @see minimize
* @param notification
*/
NotificationService.prototype.dismissOrMinimize = function (notification){
//For now minimize everything, and have discussion around which
//kind of messages should or should not be in the minimized
//notifications list
/*if (notification.severity > MessageSeverity.INFO){
this.minimize(notification);
} else {
this.dismiss(notification);
}*/
this.minimize(notification);
};
return NotificationService;
}
);

View File

@@ -0,0 +1,78 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
define(
['../src/NotificationIndicatorController'],
function (NotificationIndicatorController) {
"use strict";
describe("The notification indicator controller ", function () {
var mockNotificationService,
mockScope,
mockDialogService;
beforeEach(function(){
mockNotificationService = jasmine.createSpy("notificationService");
mockScope = jasmine.createSpy("$scope");
mockDialogService = jasmine.createSpyObj(
"dialogService",
["getDialogResponse","dismiss"]
);
});
it("exposes the highest notification severity to the template", function() {
mockNotificationService.highest = {
severity: "error"
};
var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
expect(mockScope.highest).toBeTruthy();
expect(mockScope.highest.severity).toBe("error");
});
it("invokes the dialog service to show list of messages", function() {
var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
expect(mockScope.showNotificationsList).toBeDefined();
mockScope.showNotificationsList();
expect(mockDialogService.getDialogResponse).toHaveBeenCalled();
expect(mockDialogService.getDialogResponse.mostRecentCall.args[0]).toBe('overlay-message-list');
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() {
var controller = new NotificationIndicatorController(mockScope, mockNotificationService, mockDialogService);
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

@@ -22,8 +22,8 @@
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine */
define(
['../src/NotificationService','../src/MessageSeverity'],
function (NotificationService, MessageSeverity) {
['../src/NotificationService'],
function (NotificationService) {
"use strict";
describe("The notification service ", function () {
@@ -34,46 +34,6 @@ define(
successModel,
errorModel;
/**
* 1) Calling .notify results in a new notification being created
* with the provided model and set to the active notification. DONE
*
* 2) Calling .notify with autoDismiss results in a SUCCESS notification
* becoming dismissed after timeout has elapsed DONE
*
* 3) Calling .notify with autoDismiss results in an ERROR notification
* being MINIMIZED after a timeout has elapsed DONE
*
* 4) Calling .notify with an active info notification results in that
* notification being auto-dismissed, and the new notification becoming
* active. DONE
*
* 5) Calling .notify with an active error notification results in that
* notification being auto-minimized and the new notification becoming
* active. DONE
*
* 6) Calling .notify with an active error notification, AND a
* queued error notification results in the active notification
* being auto-dismissed, the next message in the queue becoming
* active, then auto-dismissed, and then the provided notification
* becoming active.
*/
/**
var model = {
title: string,
progress: number,
severity: MessageSeverity,
unknownProgress: boolean,
minimized: boolean,
autoDismiss: boolean | number,
actions: {
label: string,
action: function
}
}
*/
beforeEach(function(){
mockTimeout = jasmine.createSpy("$timeout");
mockAutoDismiss = mockMinimizeTimeout = 1000;
@@ -81,11 +41,11 @@ define(
mockTimeout, mockAutoDismiss, mockMinimizeTimeout);
successModel = {
title: "Mock Success Notification",
severity: MessageSeverity.INFO
severity: "info"
};
errorModel = {
title: "Mock Error Notification",
severity: MessageSeverity.ERROR
severity: "error"
};
});
@@ -94,7 +54,7 @@ define(
var activeNotification;
notificationService.notify(successModel);
activeNotification = notificationService.getActiveNotification();
expect(activeNotification).toBe(successModel);
expect(activeNotification.model).toBe(successModel);
});
it("gets a new success notification with" +
@@ -103,7 +63,7 @@ define(
successModel.autoDismiss = 1000;
notificationService.notify(successModel);
activeNotification = notificationService.getActiveNotification();
expect(activeNotification).toBe(successModel);
expect(activeNotification.model).toBe(successModel);
mockTimeout.mostRecentCall.args[0]();
expect(mockTimeout.calls.length).toBe(2);
mockTimeout.mostRecentCall.args[0]();
@@ -117,7 +77,7 @@ define(
successModel.autoDismiss = true;
notificationService.notify(successModel);
activeNotification = notificationService.getActiveNotification();
expect(activeNotification).toBe(successModel);
expect(activeNotification.model).toBe(successModel);
mockTimeout.mostRecentCall.args[0]();
expect(mockTimeout.calls.length).toBe(2);
mockTimeout.mostRecentCall.args[0]();
@@ -125,6 +85,37 @@ define(
expect(activeNotification).toBeUndefined();
});
it("allows minimization of notifications", function() {
var notification,
activeNotification;
successModel.autoDismiss = false;
notification = notificationService.notify(successModel);
activeNotification = notificationService.getActiveNotification();
expect(activeNotification.model).toBe(successModel);
notification.minimize();
mockTimeout.mostRecentCall.args[0]();
activeNotification = notificationService.getActiveNotification();
expect(activeNotification).toBeUndefined();
expect(notificationService.notifications.length).toBe(1);
});
it("allows dismissal of notifications", function() {
var notification,
activeNotification;
successModel.autoDismiss = false;
notification = notificationService.notify(successModel);
activeNotification = notificationService.getActiveNotification();
expect(activeNotification.model).toBe(successModel);
notification.dismiss();
activeNotification = notificationService.getActiveNotification();
expect(activeNotification).toBeUndefined();
expect(notificationService.notifications.length).toBe(0);
});
describe(" gets called with multiple notifications", function(){
it("auto-dismisses the previously active notification, making" +
" the new notification active", function() {
@@ -134,7 +125,7 @@ define(
activeNotification =
notificationService.getActiveNotification();
//Initially expect the active notification to be info
expect(activeNotification).toBe(successModel);
expect(activeNotification.model).toBe(successModel);
//Then notify of an error
notificationService.notify(errorModel);
//But it should be auto-dismissed and replaced with the
@@ -145,25 +136,8 @@ define(
// second is to allow minimization animation to take place.
mockTimeout.mostRecentCall.args[0]();
activeNotification = notificationService.getActiveNotification();
expect(activeNotification).toBe(errorModel);
expect(activeNotification.model).toBe(errorModel);
});
/* Test is temporarily invalid as info messages are being
minimized
it("auto-dismisses an active success notification, removing" +
" it completely", function() {
//First pre-load with a info message
notificationService.notify(successModel);
//Then notify of an error
notificationService.notify(errorModel);
expect(notificationService.notifications.length).toEqual(2);
mockTimeout.mostRecentCall.args[0]();
//Two timeouts, one is to force minimization after
// displaying the message for a minimum period, the
// second is to allow minimization animation to take place.
mockTimeout.mostRecentCall.args[0]();
//Previous info message should be completely dismissed
expect(notificationService.notifications.length).toEqual(1);
});*/
it("auto-minimizes an active error notification", function() {
var activeNotification;
//First pre-load with an error message
@@ -182,7 +156,7 @@ define(
expect(notificationService.notifications.length).toEqual(2);
activeNotification =
notificationService.getActiveNotification();
expect(activeNotification).toBe(successModel);
expect(activeNotification.model).toBe(successModel);
expect(errorModel.minimized).toEqual(true);
});
it("auto-minimizes errors when a number of them arrive in" +
@@ -190,11 +164,11 @@ define(
var activeNotification,
error2 = {
title: "Second Mock Error Notification",
severity: MessageSeverity.ERROR
severity: "error"
},
error3 = {
title: "Third Mock Error Notification",
severity: MessageSeverity.ERROR
severity: "error"
};
//First pre-load with a info message
@@ -214,7 +188,7 @@ define(
expect(notificationService.notifications.length).toEqual(3);
activeNotification =
notificationService.getActiveNotification();
expect(activeNotification).toBe(error2);
expect(activeNotification.model).toBe(error2);
expect(errorModel.minimized).toEqual(true);
//Mock the second auto-minimize
@@ -225,10 +199,11 @@ define(
mockTimeout.mostRecentCall.args[0]();
activeNotification =
notificationService.getActiveNotification();
expect(activeNotification).toBe(error3);
expect(activeNotification.model).toBe(error3);
expect(error2.minimized).toEqual(true);
});
});
});
});
}
);

View File

@@ -1,3 +1,4 @@
[
"NotificationService"
]
"NotificationService",
"NotificationIndicatorController"
]

View File

@@ -44,14 +44,12 @@ define(
function progress(phase, totalObjects, processed){
if (phase.toLowerCase() === 'preparing'){
console.log('preparing');
dialogService.showBlockingMessage({
title: "Preparing to copy objects",
unknownProgress: true,
severity: "info",
});
} else if (phase.toLowerCase() === "copying") {
console.log('copying');
dialogService.dismiss();
if (!notification) {
notification = notificationService.notify(notificationModel);

View File

@@ -113,14 +113,14 @@ define(
function newPerform (domainObject, parent, progress) {
var $q = this.$q,
processed = 0,
self = this;
if (this.validate(domainObject, parent)) {
progress("preparing");
return this.buildCopyGraph(domainObject, parent)
.then(function(clones){
return $q.all(clones.map(function(clone, index){
progress("copying", clones.length, index);
return self.persistenceService.createObject(clone.persistence.getSpace(), clone.model.id, clone.model);
return self.persistenceService.createObject(clone.persistence.getSpace(), clone.model.id, clone.model).then(function(){progress("copying", clones.length, processed++);});
})).then(function(){ return clones});
})
.then(function(clones) {