Merge lp:~allenap/maas/notifications-style-mk2 into lp:~maas-committers/maas/trunk

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 5751
Proposed branch: lp:~allenap/maas/notifications-style-mk2
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 216 lines (+138/-20)
2 files modified
src/maasserver/static/js/angular/directives/notifications.js (+75/-16)
src/maasserver/static/js/angular/directives/tests/test_notifications.js (+63/-4)
To merge this branch: bzr merge lp:~allenap/maas/notifications-style-mk2
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+317588@code.launchpad.net

Commit message

Use the new styling for notifications in the UI and group by category.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

See "Notifications" in http://ubuntudesign.github.io/maas-gui-vanilla-theme/ for more info on how notifications should now be styled.

Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/static/js/angular/directives/notifications.js'
2--- src/maasserver/static/js/angular/directives/notifications.js 2017-02-08 15:30:56 +0000
3+++ src/maasserver/static/js/angular/directives/notifications.js 2017-02-17 08:03:16 +0000
4@@ -7,16 +7,40 @@
5 angular.module('MAAS').run(['$templateCache', function ($templateCache) {
6 // Inject notifications.html into the template cache.
7 $templateCache.put('directive/templates/notifications.html', [
8- '<div ng-repeat="n in notifications" ng-class="classes[n.category]">',
9- '<p class="p-notification__response">',
10- '<span class="p-notification__status"></span>',
11- '<span ng-bind-html="n.message"></span> — ',
12- '<a ng-click="dismiss(n)">Dismiss</a>',
13- '<br><small>(id: {$ n.id $}, ',
14- 'ident: {$ n.ident || "-" $}, user: {$ n.user || "-" $}, ',
15- 'users: {$ n.users $}, admins: {$ n.admins $}, ',
16- 'created: {$ n.created $}, updated: {$ n.updated $})</small>',
17- '</p>',
18+ '<div ng-repeat="category in categories"',
19+ ' ng-init="notifications = categoryNotifications[category]">',
20+ // 1 notification.
21+ '<div ng-if="notifications.length == 1"',
22+ ' ng-class="categoryClasses[category]">',
23+ '<p ng-repeat="notification in notifications"',
24+ ' class="p-notification__response">',
25+ '<span ng-bind-html="notification.message"></span> ',
26+ '<a ng-click="dismiss(notification)">Dismiss</a>',
27+ '</p>',
28+ '</div>',
29+ // 2 or more notifications.
30+ '<div ng-if="notifications.length >= 2"',
31+ ' ng-class="categoryClasses[category]"',
32+ ' ng-init="shown = false"',
33+ ' class="p-notification--group">',
34+ '<p class="p-notification__response" ng-click="shown = !shown">',
35+ '<span class="p-notification__status"',
36+ ' data-count="{$ notifications.length $}"',
37+ ' ng-bind="categoryTitles[category]"></span>',
38+ '<i class="icon icon--open"></i>',
39+ '</p>',
40+ '<ul class="p-notification__list" ng-class="{\'is-open\': shown}">',
41+ '<li ng-repeat="notification in notifications"',
42+ ' class="p-notification__item">',
43+ '<p class="p-notification__msg">',
44+ '<span ng-bind-html="notification.message"></span> ',
45+ '<a ng-click="dismiss(notification)">Dismiss</a>',
46+ '</p>',
47+ '<time class="p-notification__date" ',
48+ ' ng-bind="notification.updated"></time>',
49+ '</li>',
50+ '</ul>',
51+ '</div>',
52 '</div>'
53 ].join(''));
54 }]);
55@@ -32,12 +56,47 @@
56 $scope.notifications = NotificationsManager.getItems();
57 $scope.dismiss = angular.bind(
58 NotificationsManager, NotificationsManager.dismiss);
59- $scope.classes = {
60- "error": "p-notification--error",
61- "warning": "p-notification--warning",
62- "success": "p-notification--success",
63- "info": "p-notification" // No suffix.
64- };
65+
66+ $scope.categories = [
67+ "error",
68+ "warning",
69+ "success",
70+ "info"
71+ ];
72+ $scope.categoryTitles = {
73+ error: "Errors",
74+ warning: "Warnings",
75+ success: "Successes",
76+ info: "Other messages"
77+ };
78+ $scope.categoryClasses = {
79+ error: "p-notification--error",
80+ warning: "p-notification--warning",
81+ success: "p-notification--success",
82+ info: "p-notification" // No suffix.
83+ };
84+ $scope.categoryNotifications = {
85+ error: [],
86+ warning: [],
87+ success: [],
88+ info: []
89+ };
90+
91+ $scope.$watchCollection(
92+ "notifications", function() {
93+ var cns = $scope.categoryNotifications;
94+ angular.forEach(
95+ $scope.categories, function(category) {
96+ cns[category].length = 0;
97+ }
98+ );
99+ angular.forEach(
100+ $scope.notifications, function(notification) {
101+ cns[notification.category].push(notification);
102+ }
103+ );
104+ }
105+ );
106 }
107 };
108 }]);
109
110=== modified file 'src/maasserver/static/js/angular/directives/tests/test_notifications.js'
111--- src/maasserver/static/js/angular/directives/tests/test_notifications.js 2017-02-08 15:30:56 +0000
112+++ src/maasserver/static/js/angular/directives/tests/test_notifications.js 2017-02-17 08:03:16 +0000
113@@ -45,6 +45,17 @@
114 "updated": "Fri, 27 Jan. 2017 12:19:52"
115 }
116 ];
117+ var exampleAdditionalNotification = {
118+ "id": 4,
119+ "ident": null,
120+ "message": "I'm only sleeping",
121+ "category": "info",
122+ "user": null,
123+ "users": true,
124+ "admins": true,
125+ "created": "Thu, 16 Feb. 2017 16:39:36",
126+ "updated": "Thu, 16 Feb. 2017 16:39:36"
127+ };
128
129 // Load the NotificationsManager and
130 // create a new scope before each test.
131@@ -77,11 +88,11 @@
132 theNotificationsManager._items = exampleNotifications;
133 var directive = compileDirective();
134 // The directive renders an outer div for each notification.
135- expect(directive.find("div").length).toBe(
136+ expect(directive.find("div > div").length).toBe(
137 exampleNotifications.length, directive.html());
138 // Messages are rendered in the nested tree.
139 var messages = directive.find(
140- "div > p > span:nth-child(2)").map(
141+ "div > div > p > span:nth-child(1)").map(
142 function() { return $(this).text(); }).get();
143 expect(messages).toEqual(exampleNotifications.map(
144 function(notification) { return notification.message; }));
145@@ -92,14 +103,14 @@
146 theNotificationsManager._items = [notification];
147 var dismiss = spyOn(theNotificationsManager, "dismiss");
148 var directive = compileDirective();
149- directive.find("div > p > a").click();
150+ directive.find("div > div > p > a").click();
151 expect(dismiss).toHaveBeenCalledWith(notification);
152 });
153
154 it("adjusts class according to category", function() {
155 theNotificationsManager._items = exampleNotifications;
156 var directive = compileDirective();
157- var classes = directive.find("div").map(
158+ var classes = directive.find("div > div").map(
159 function() { return $(this).attr("class"); }).get();
160 expect(classes.length).toBe(3);
161 var p_classes = [];
162@@ -115,6 +126,54 @@
163 ]);
164 });
165
166+ it("adjusts class according to number in category", function() {
167+ // Get the message from a notification object.
168+ var getMessage = function(ntfn) { return ntfn.message; };
169+ // Is the notification object an "info" notification.
170+ var isInfo = function(ntfn) { return ntfn.category === "info"; };
171+ // Find message texts rendered into the DOM.
172+ var findRenderedMessages = function() {
173+ return directive.find(
174+ "div > div > p > span:nth-child(1)").map(
175+ function() { return $(this).text(); }).get();
176+ };
177+ // Find grouped message texts rendered into the DOM.
178+ var findRenderedGroupedMessages = function() {
179+ return directive.find(
180+ "div > div > ul > li > p > span:nth-child(1)").map(
181+ function() { return $(this).text(); }).get();
182+ };
183+
184+ theNotificationsManager._items =
185+ angular.copy(exampleNotifications);
186+ var directive = compileDirective();
187+
188+ // At first there is only one message per category.
189+ var messagesExpected1 = exampleNotifications.map(getMessage);
190+ expect(findRenderedMessages()).toEqual(messagesExpected1);
191+
192+ // Now we add an additional "info" message.
193+ theNotificationsManager._items.push(exampleAdditionalNotification);
194+ $scope.$digest();
195+
196+ // A category title can now be found at the point where we
197+ // previously found the "info" message.
198+ var messagesExpected2 = angular.copy(messagesExpected1);
199+ messagesExpected2.splice(
200+ messagesExpected2.length - 1, 1, $scope.categoryTitles[
201+ exampleAdditionalNotification.category]);
202+ expect(findRenderedMessages())
203+ .toEqual(messagesExpected2);
204+
205+ // The "info" messages are now grouped.
206+ var groupedMessagesExpected = (
207+ exampleNotifications.filter(isInfo).map(getMessage)
208+ .concat([exampleAdditionalNotification.message])
209+ );
210+ expect(findRenderedGroupedMessages())
211+ .toEqual(groupedMessagesExpected);
212+ });
213+
214 it("sanitizes messages", function() {
215 var harmfulNotification = angular.copy(exampleNotifications[0]);
216 harmfulNotification.message =