Skip to content

Commit 1b8b540

Browse files
Merge pull request #612 from cdcabrera/feat-fix-toastnotifications-html
fix(pfToastNotificationList): HTML Content for Toast Notifications
2 parents d8c6849 + 5b1b6b7 commit 1b8b540

File tree

6 files changed

+82
-42
lines changed

6 files changed

+82
-42
lines changed

src/notification/toast-notification-list.component.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
* @param {Array} notifications The list of current notifications to display. Each notification should have the following (see pfToastNotification):
88
* <ul style='list-style-type: none'>
99
* <li>.type - (String) The type of the notification message. Allowed value is one of these: 'success','info','danger', 'warning'
10-
* <li>.header - (String) The header to display for the notification (optional)
11-
* <li>.message - (String) The main text message of the notification.
10+
* <li>.header - (String) The header to display for the notification, accepts HTML content when allowed. (optional)
11+
* <li>.message - (String) The main text message of the notification. Accepts HTML content when allowed.
1212
* <li>.actionTitle Text to show for the primary action, optional.
1313
* <li>.actionCallback (function(this notification)) Function to invoke when primary action is selected, optional
1414
* <li>.menuActions Optional list of actions to place in the kebab menu:<br/>
@@ -21,6 +21,7 @@
2121
* <li>.isPersistent Flag to show close button for the notification even if showClose is false.
2222
* </ul>
2323
* @param {Boolean} showClose Flag to show the close button on all notifications (not shown if the notification has menu actions)
24+
* @param {Boolean} htmlContent Flag to allow HTML content within the header and message options.
2425
* @param {function} closeCallback (function(data)) Function to invoke when closes a toast notification
2526
* @param {function} updateViewing (function(boolean, data)) Function to invoke when user is viewing/not-viewing (hovering on) a toast notification
2627
*
@@ -32,7 +33,7 @@
3233
3334
<file name="index.html">
3435
<div ng-controller="ToastNotificationListDemoCtrl" >
35-
<pf-toast-notification-list notifications="notifications" show-close="showClose" close-callback="handleClose" update-viewing="updateViewing"></pf-toast-notification-list>
36+
<pf-toast-notification-list notifications="notifications" show-close="showClose" html-content="htmlContent" close-callback="handleClose" update-viewing="updateViewing"></pf-toast-notification-list>
3637
<div class="row example-container">
3738
<div class="col-md-12">
3839
<form class="form-horizontal">
@@ -84,9 +85,13 @@
8485
<input type="checkbox" ng-model="persistent"/>
8586
</div>
8687
<label class="col-sm-2 control-label" for="type">Show Menu:</label>
87-
<div class="col-sm-2">
88+
<div class="col-sm-1">
8889
<input type="checkbox" ng-model="showMenu"/>
8990
</div>
91+
<label class="col-sm-2 control-label" for="type">Allow HTML:</label>
92+
<div class="col-sm-1">
93+
<input type="checkbox" ng-model="htmlContent"/>
94+
</div>
9095
</div>
9196
<div class="form-group">
9297
<div class="col-sm-12">
@@ -118,8 +123,9 @@
118123
119124
$scope.type = $scope.types[0];
120125
$scope.header = 'Default header.';
121-
$scope.message = 'Default notification message.';
126+
$scope.message = 'Default <strong>notification</strong> message.';
122127
$scope.showClose = false;
128+
$scope.htmlContent = false;
123129
$scope.persistent = false;
124130
125131
$scope.primaryAction = '';
@@ -193,7 +199,7 @@
193199
$scope.handleAction,
194200
($scope.showMenu ? $scope.menuActions : undefined)
195201
);
196-
}
202+
};
197203
198204
$scope.notifications = Notifications.data;
199205
});
@@ -205,6 +211,7 @@ angular.module('patternfly.notification').component('pfToastNotificationList', {
205211
bindings: {
206212
notifications: '=',
207213
showClose: '=?',
214+
htmlContent: '<?',
208215
closeCallback: '=?',
209216
updateViewing: '=?'
210217
},
@@ -225,3 +232,4 @@ angular.module('patternfly.notification').component('pfToastNotificationList', {
225232
};
226233
}
227234
});
235+

src/notification/toast-notification-list.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
header="{{notification.header}}"
55
message="{{notification.message}}"
66
show-close="{{($ctrl.showClose || notification.isPersistent === true) && !(notification.menuActions && notification.menuActions.length > 0)}}"
7+
html-content="$ctrl.htmlContent"
78
close-callback="$ctrl.handleClose"
89
action-title="{{notification.actionTitle}}"
910
action-callback="notification.actionCallback"

src/notification/toast-notification.component.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
* @scope
66
*
77
* @param {string} notificationType The type of the notification message. Allowed value is one of these: 'success','info','danger', 'warning'
8-
* @param {string} header The header text of the notification.
9-
* @param {string} message The main text message of the notification.
8+
* @param {string} header The header text of the notification. Accepts HTML content when allowed.
9+
* @param {string} message The main text message of the notification. Accepts HTML content when allowed.
1010
* @param {boolean} showClose Flag to show the close button, default: true
11+
* @param {boolean} htmlContent Flag to allow HTML content within the header and message options.
1112
* @param {function} closeCallback (function(data)) Function to invoke when close action is selected, optional
1213
* @param {string} actionTitle Text to show for the primary action, optional.
1314
* @param {function} actionCallback (function(data)) Function to invoke when primary action is selected, optional
@@ -37,9 +38,9 @@
3738
<div ng-controller="ToastNotificationDemoCtrl" class="row example-container">
3839
<div class="col-md-12">
3940
<pf-toast-notification notification-type="{{type}}" header="{{header}}" message="{{message}}"
40-
show-close="{{showClose}}" close-callback="closeCallback"
41-
action-title="{{primaryAction}}" action-callback="handleAction"
42-
menu-actions="menuActions">
41+
show-close="{{showClose}}" html-content="htmlContent"
42+
close-callback="closeCallback" action-title="{{primaryAction}}"
43+
action-callback="handleAction" menu-actions="menuActions">
4344
</pf-toast-notification>
4445
4546
<form class="form-horizontal">
@@ -81,13 +82,17 @@
8182
</div>
8283
<div class="form-group">
8384
<label class="col-sm-2 control-label" for="type">Show Close:</label>
84-
<div class="col-sm-3">
85+
<div class="col-sm-1">
8586
<input type="checkbox" ng-model="showClose"/>
8687
</div>
8788
<label class="col-sm-2 control-label" for="type">Show Menu:</label>
88-
<div class="col-sm-3">
89+
<div class="col-sm-1">
8990
<input type="checkbox" ng-model="showMenu"/>
9091
</div>
92+
<label class="col-sm-2 control-label" for="type">Allow HTML:</label>
93+
<div class="col-sm-1">
94+
<input type="checkbox" ng-model="htmlContent"/>
95+
</div>
9196
</div>
9297
</form>
9398
</div>
@@ -105,9 +110,10 @@
105110
$scope.types = ['success','info','danger', 'warning'];
106111
$scope.type = $scope.types[0];
107112
$scope.showClose = false;
113+
$scope.htmlContent = false;
108114
109115
$scope.header = 'Default Header.';
110-
$scope.message = 'Default Message.';
116+
$scope.message = 'Default <strong>notification</strong> message.';
111117
$scope.primaryAction = '';
112118
113119
$scope.updateType = function(item) {
@@ -182,6 +188,7 @@ angular.module( 'patternfly.notification' ).component('pfToastNotification', {
182188
'message': '@',
183189
'header': '@',
184190
'showClose': '@',
191+
'htmlContent': '<?',
185192
'closeCallback': '=?',
186193
'actionTitle': '@',
187194
'actionCallback': '=?',
@@ -190,7 +197,7 @@ angular.module( 'patternfly.notification' ).component('pfToastNotification', {
190197
'data': '=?'
191198
},
192199
templateUrl: 'notification/toast-notification.html',
193-
controller: function () {
200+
controller: function ($sce) {
194201
'use strict';
195202
var ctrl = this,
196203
_showClose;
@@ -249,5 +256,10 @@ angular.module( 'patternfly.notification' ).component('pfToastNotification', {
249256
ctrl.updateShowClose();
250257
}
251258
};
259+
260+
ctrl.trustAsHtml = function (html) {
261+
return $sce.trustAsHtml(html);
262+
};
252263
}
253264
});
265+

src/notification/toast-notification.html

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@
2727
<span class="pficon pficon-info" ng-if="$ctrl.notificationType === 'info'"></span>
2828
<span class="pficon pficon-error-circle-o" ng-if="$ctrl.notificationType === 'danger'"></span>
2929
<span class="pficon pficon-warning-triangle-o" ng-if="$ctrl.notificationType === 'warning'"></span>
30-
<span ng-if="$ctrl.header">
31-
<strong>{{$ctrl.header}}</strong> {{$ctrl.message}}
30+
<span ng-if="!$ctrl.htmlContent">
31+
<strong ng-if="$ctrl.header" ng-bind="$ctrl.header"></strong>
32+
<span ng-bind="$ctrl.message"></span>
3233
</span>
33-
<span ng-if="!$ctrl.header">
34-
{{$ctrl.message}}
34+
<span ng-if="$ctrl.htmlContent">
35+
<strong ng-if="$ctrl.header" ng-bind-html="$ctrl.trustAsHtml($ctrl.header)"></strong>
36+
<span ng-bind-html="$ctrl.trustAsHtml($ctrl.message)"></span>
3537
</span>
3638
</div>
39+

test/notification/toast-notification-list.spec.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,13 @@ describe('Component: pfToastNotificationList', function () {
157157
// No Menu Actions
158158
$scope.notifications.forEach(function(nextItem) {
159159
nextItem.menuActions = undefined;
160-
})
160+
});
161+
161162
var htmlTmp = '<pf-toast-notification-list notifications="notifications" show-close="false" close-callback="handleClose"></pf-toast-notification-list>';
162163

163164
compileHTML(htmlTmp, $scope);
164165

165-
var closeButton = element.find('.toast-notifications-list-pf .toast-pf button.close');
166+
closeButton = element.find('.toast-notifications-list-pf .toast-pf button.close');
166167
expect(closeButton.length).toBe(3);
167168

168169
expect($scope.closeData).toBeUndefined();
@@ -208,3 +209,4 @@ describe('Component: pfToastNotificationList', function () {
208209
expect($scope.menuData.header).toBe("Header 1");
209210
});
210211
});
212+

test/notification/toast-notification.spec.js

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ describe('Component: pfToastNotification', function () {
2121
scope.$digest();
2222
};
2323

24-
var setupHTML = function (notificationType, header, showClose, primaryAction, showMenu, data) {
24+
var setupHTML = function (notificationType, header, message, showClose, primaryAction, showMenu, data, htmlContent) {
2525
$scope.type = notificationType;
2626
$scope.header = header;
27-
$scope.message = "Test Toast Notification Message";
27+
$scope.message = message || "Test Toast Notification Message";
2828
$scope.showClose = showClose;
2929
$scope.primaryAction = primaryAction;
30-
$scope.data = data
30+
$scope.data = data;
31+
$scope.htmlContent = htmlContent;
3132

3233
$scope.closeData = undefined;
3334
$scope.closeCallback = function (data) {
@@ -91,35 +92,47 @@ describe('Component: pfToastNotification', function () {
9192
title: "Test Notification"
9293
};
9394
var htmlTmp = '<pf-toast-notification notification-type="{{type}}" header="{{header}}" message="{{message}}"' +
94-
' show-close="{{showClose}}" close-callback="closeCallback"' +
95-
' action-title="{{primaryAction}}" action-callback="handleAction"' +
96-
' menu-actions="menuActions" data="data">' +
95+
' show-close="{{showClose}}" html-content="htmlContent" ' +
96+
' close-callback="closeCallback" action-title="{{primaryAction}}"' +
97+
' action-callback="handleAction" menu-actions="menuActions" data="data">' +
9798
' </pf-toast-notification>';
9899

99100
compileHTML(htmlTmp, $scope);
100101
};
101102

102103
it('should have the correct header and message', function () {
103-
setupHTML ("info", "Test Header", false, '', false);
104+
setupHTML ("info", "Test Header", null, false, '', false);
104105
header = element.find('.toast-pf span strong');
105106
expect(header.length).toBe(1);
106107
expect(header.text()).toBe("Test Header");
107-
message = element.find('.toast-pf span');
108+
message = element.find('.toast-pf > span');
108109
expect(message.length).toBe(2);
109110
expect(angular.element(message[1]).text()).toContain("Test Toast Notification Message");
110111
});
111112

112113
it('should have the correct message when no header is given', function () {
113-
setupHTML ("info", "", false, '', false);
114+
setupHTML ("info", "", null, false, '', false);
114115
var header = element.find('.toast-pf span strong');
115116
expect(header.length).toBe(0);
116-
var message = element.find('.toast-pf span');
117+
var message = element.find('.toast-pf > span');
117118
expect(message.length).toBe(2);
118119
expect(angular.element(message[1]).text()).toContain("Test Toast Notification Message");
119120
});
120121

122+
it('should allow HTML content within the header and message', function () {
123+
setupHTML ("info", "<em>Test Header</em>", null, false, '', false, null, true);
124+
var header = element.find('.toast-pf span strong em');
125+
expect(header.length).toBe(1);
126+
expect(header.text()).toContain("Test Header");
127+
128+
setupHTML ("info", "", "<em>Test Notification Message</em>", false, '', false, null, true);
129+
var message = element.find('.toast-pf > span em');
130+
expect(message.length).toBe(1);
131+
expect(message.text()).toContain("Test Notification Message");
132+
});
133+
121134
it('should have the correct status icon', function () {
122-
setupHTML ("success", "Test Header", false, '', false);
135+
setupHTML ("success", "Test Header", null, false, '', false);
123136
var okIcon = element.find('.pficon.pficon-ok');
124137
var infoIcon = element.find('.pficon.pficon-info');
125138
var errorIcon = element.find('.pficon.pficon-error-circle-o');
@@ -129,7 +142,7 @@ describe('Component: pfToastNotification', function () {
129142
expect(errorIcon.length).toBe(0);
130143
expect(warnIcon.length).toBe(0);
131144

132-
setupHTML ("info", "Test Header", false, '', false);
145+
setupHTML ("info", "Test Header", null, false, '', false);
133146
okIcon = element.find('.pficon.pficon-ok');
134147
infoIcon = element.find('.pficon.pficon-info');
135148
errorIcon = element.find('.pficon.pficon-error-circle-o');
@@ -139,7 +152,7 @@ describe('Component: pfToastNotification', function () {
139152
expect(errorIcon.length).toBe(0);
140153
expect(warnIcon.length).toBe(0);
141154

142-
setupHTML ("danger", "Test Header", false, '', false);
155+
setupHTML ("danger", "Test Header", null, false, '', false);
143156
okIcon = element.find('.pficon.pficon-ok');
144157
infoIcon = element.find('.pficon.pficon-info');
145158
errorIcon = element.find('.pficon.pficon-error-circle-o');
@@ -149,7 +162,7 @@ describe('Component: pfToastNotification', function () {
149162
expect(errorIcon.length).toBe(1);
150163
expect(warnIcon.length).toBe(0);
151164

152-
setupHTML ("warning", "Test Header", false, '', false);
165+
setupHTML ("warning", "Test Header", null, false, '', false);
153166
okIcon = element.find('.pficon.pficon-ok');
154167
infoIcon = element.find('.pficon.pficon-info');
155168
errorIcon = element.find('.pficon.pficon-error-circle-o');
@@ -162,11 +175,11 @@ describe('Component: pfToastNotification', function () {
162175
});
163176

164177
it('should have the close button when specified', function () {
165-
setupHTML ("success", "Test Header", false, 'Test Action', true);
178+
setupHTML ("success", "Test Header", null, false, 'Test Action', true);
166179
var closeButton = element.find('button.close');
167180
expect(closeButton.length).toBe(0);
168181

169-
setupHTML ("success", "Test Header", true, 'Test Action', false);
182+
setupHTML ("success", "Test Header", null, true, 'Test Action', false);
170183
closeButton = element.find('button.close');
171184
expect(closeButton.length).toBe(1);
172185

@@ -179,13 +192,13 @@ describe('Component: pfToastNotification', function () {
179192
expect($scope.closeData.title).toBe("Test Notification");
180193

181194
// No close button even if specified when menu actions exist
182-
setupHTML ("success", "Test Header", true, 'Test Action', true);
195+
setupHTML ("success", "Test Header", null, true, 'Test Action', true);
183196
closeButton = element.find('button.close');
184197
expect(closeButton.length).toBe(0);
185198
});
186199

187200
it('should have the correct primary action and call the correct callback when clicked', function () {
188-
setupHTML ("success", "Test Header", false, 'Test Action', false);
201+
setupHTML ("success", "Test Header", null, false, 'Test Action', false);
189202
var actionButton = element.find('.toast-pf-action > a');
190203
expect(actionButton.length).toBe(1);
191204
expect($scope.actionData).toBeUndefined();
@@ -198,7 +211,7 @@ describe('Component: pfToastNotification', function () {
198211
});
199212

200213
it('should have the correct kebab menu and call the correct callback when items are clicked', function () {
201-
setupHTML ("success", "Test Header", false, 'Test Action', true);
214+
setupHTML ("success", "Test Header", null, false, 'Test Action', true);
202215
var menuIndicator = element.find('.dropdown-kebab-pf');
203216
expect(menuIndicator.length).toBe(1);
204217
var menuItems = element.find('.dropdown-kebab-pf .dropdown-menu li');
@@ -219,13 +232,13 @@ describe('Component: pfToastNotification', function () {
219232
});
220233

221234
it('should have correct number of separators', function () {
222-
setupHTML ("success", "Test Header", false, 'Test Action', true);
235+
setupHTML ("success", "Test Header", null, false, 'Test Action', true);
223236
var fields = element.find('.dropdown-kebab-pf .dropdown-menu .divider');
224237
expect(fields.length).toBe(1);
225238
});
226239

227240
it('should correctly disable actions and not call the callback if clicked', function () {
228-
setupHTML ("success", "Test Header", false, 'Test Action', true);
241+
setupHTML ("success", "Test Header", null, false, 'Test Action', true);
229242
var fields = element.find('.dropdown-kebab-pf .dropdown-menu .disabled > a');
230243
expect(fields.length).toBe(1);
231244

@@ -239,3 +252,4 @@ describe('Component: pfToastNotification', function () {
239252
expect($scope.menuData).toBeUndefined();
240253
});
241254
});
255+

0 commit comments

Comments
 (0)