Skip to content

Commit 3426127

Browse files
authored
feat(toast): image variant, a11y support, title html, click support
This PR - adds accessibility support using aria labels to toasts just like we did for modals in #2036 - adds a ui image toast variant to show images without any padding - fixes missing html support in titles - fixes wrong placement of vertical actions when the message was too short - supports the onClick handler in any case now even if closeOnClick is set to false - onClick can now also return false to prevent closing the toast - the close icon gets a dedicated event now independent of the onClick event
1 parent 2f5aacb commit 3426127

File tree

3 files changed

+81
-23
lines changed

3 files changed

+81
-23
lines changed

src/definitions/modules/toast.js

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,14 @@ $.fn.toast = function(parameters) {
6969
element = this,
7070
instance = isToastComponent ? $module.data(moduleNamespace) : undefined,
7171

72+
id,
7273
module
7374
;
7475
module = {
7576

7677
initialize: function() {
7778
module.verbose('Initializing element');
79+
module.create.id();
7880
if (!module.has.container()) {
7981
module.create.container();
8082
}
@@ -124,17 +126,22 @@ $.fn.toast = function(parameters) {
124126
},
125127

126128
show: function(callback) {
127-
callback = callback || function(){};
128-
module.debug('Showing toast');
129129
if(settings.onShow.call($toastBox, element) === false) {
130130
module.debug('onShow callback returned false, cancelling toast animation');
131131
return;
132132
}
133+
callback = callback || function(){};
134+
module.debug('Showing toast');
133135
module.animate.show(callback);
134136
},
135137

136138
close: function(callback) {
139+
if(settings.onHide.call($toastBox, element) === false) {
140+
module.debug('onHide callback returned false, cancelling toast animation');
141+
return;
142+
}
137143
callback = callback || function(){};
144+
module.debug('Closing toast');
138145
module.remove.visible();
139146
module.unbind.events();
140147
module.animate.close(callback);
@@ -146,12 +153,16 @@ $.fn.toast = function(parameters) {
146153
module.verbose('Creating container');
147154
$context.append($('<div/>',{class: settings.position + ' ' + className.container + ' ' +(settings.horizontal ? className.horizontal : '')}));
148155
},
156+
id: function() {
157+
id = (Math.random().toString(16) + '000000000').substr(2, 8);
158+
module.verbose('Creating unique id for element', id);
159+
},
149160
toast: function() {
150161
$toastBox = $('<div/>', {class: className.box});
151162
var iconClass = module.get.iconClass();
152163
if (!isToastComponent) {
153164
module.verbose('Creating toast');
154-
$toast = $('<div/>');
165+
$toast = $('<div/>', {role: 'alert'});
155166
var $content = $('<div/>', {class: className.content});
156167
if (iconClass !== '') {
157168
$toast.append($('<i/>', {class: iconClass + ' ' + className.icon}));
@@ -164,21 +175,29 @@ $.fn.toast = function(parameters) {
164175
}));
165176
}
166177
if (settings.title !== '') {
178+
var titleId = '_' + module.get.id() + 'title';
179+
$toast.attr('aria-labelledby', titleId);
167180
$content.append($('<div/>', {
168181
class: className.title,
169-
text: settings.title
182+
id: titleId,
183+
html: module.helpers.escape(settings.title, settings.preserveHTML)
170184
}));
171185
}
172-
173-
$content.append($('<div/>', {class: className.message, html: module.helpers.escape(settings.message, settings.preserveHTML)}));
186+
var descId = '_' + module.get.id() + 'desc';
187+
$toast.attr('aria-describedby', descId);
188+
$content.append($('<div/>', {
189+
class: className.message,
190+
id: descId,
191+
html: module.helpers.escape(settings.message, settings.preserveHTML)
192+
}));
174193

175194
$toast
176195
.addClass(settings.class + ' ' + className.toast)
177196
.append($content)
178197
;
179198
$toast.css('opacity', settings.opacity);
180199
if (settings.closeIcon) {
181-
$close = $('<i/>', {class: className.close + ' ' + (typeof settings.closeIcon === 'string' ? settings.closeIcon : '')});
200+
$close = $('<i/>', {class: className.close + ' ' + (typeof settings.closeIcon === 'string' ? settings.closeIcon : ''), role: 'button', tabindex: 0, 'aria-label': settings.text.close});
182201
if($close.hasClass(className.left)) {
183202
$toast.prepend($close);
184203
} else {
@@ -221,12 +240,13 @@ $.fn.toast = function(parameters) {
221240
}
222241
}
223242
settings.actions.forEach(function (el) {
224-
var icon = el[fields.icon] ? '<i class="' + module.helpers.deQuote(el[fields.icon]) + ' icon"></i>' : '',
243+
var icon = el[fields.icon] ? '<i '+(el[fields.text] ? 'aria-hidden="true"' : '')+' class="' + module.helpers.deQuote(el[fields.icon]) + ' icon"></i>' : '',
225244
text = module.helpers.escape(el[fields.text] || '', settings.preserveHTML),
226245
cls = module.helpers.deQuote(el[fields.class] || ''),
227246
click = el[fields.click] && $.isFunction(el[fields.click]) ? el[fields.click] : function () {};
228247
$actions.append($('<button/>', {
229248
html: icon + text,
249+
'aria-label': $('<div>'+(el[fields.text] || el[fields.icon] || '')+'</div>').text(),
230250
class: className.button + ' ' + cls,
231251
click: function () {
232252
var button = $(this);
@@ -330,13 +350,12 @@ $.fn.toast = function(parameters) {
330350
bind: {
331351
events: function() {
332352
module.debug('Binding events to toast');
333-
if(settings.closeOnClick || settings.closeIcon) {
334-
(settings.closeIcon ? $close : $toast)
335-
.on('click' + eventNamespace, module.event.click)
336-
;
353+
if(settings.closeIcon) {
354+
$close.on('click' + eventNamespace, module.event.close);
337355
}
356+
$toast.on('click' + eventNamespace, module.event.click);
338357
if($animationObject) {
339-
$animationObject.on('animationend' + eventNamespace, module.close);
358+
$animationObject.on('animationend' + eventNamespace, module.event.close);
340359
}
341360
$toastBox
342361
.on('click' + eventNamespace, selector.approve, module.event.approve)
@@ -348,11 +367,10 @@ $.fn.toast = function(parameters) {
348367
unbind: {
349368
events: function() {
350369
module.debug('Unbinding events to toast');
351-
if(settings.closeOnClick || settings.closeIcon) {
352-
(settings.closeIcon ? $close : $toast)
353-
.off('click' + eventNamespace)
354-
;
370+
if(settings.closeIcon) {
371+
$close.off('click' + eventNamespace);
355372
}
373+
$toast.off('click' + eventNamespace);
356374
if($animationObject) {
357375
$animationObject.off('animationend' + eventNamespace);
358376
}
@@ -384,11 +402,6 @@ $.fn.toast = function(parameters) {
384402
},
385403
close: function(callback) {
386404
callback = $.isFunction(callback) ? callback : function(){};
387-
module.debug('Closing toast');
388-
if(settings.onHide.call($toastBox, element) === false) {
389-
module.debug('onHide callback returned false, cancelling toast animation');
390-
return;
391-
}
392405
if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) {
393406
$toastBox
394407
.transition({
@@ -458,6 +471,9 @@ $.fn.toast = function(parameters) {
458471
},
459472

460473
get: {
474+
id: function() {
475+
return id;
476+
},
461477
container: function() {
462478
return ($context.find(module.helpers.toClass(settings.position) + selector.container + (settings.horizontal ? module.helpers.toClass(className.horizontal) : ':not('+module.helpers.toClass(className.horizontal)+')'))[0]);
463479
},
@@ -491,9 +507,15 @@ $.fn.toast = function(parameters) {
491507
},
492508

493509
event: {
510+
close: function(){
511+
module.close();
512+
},
494513
click: function(event) {
495514
if($(event.target).closest('a').length === 0) {
496-
settings.onClick.call($toastBox, element);
515+
if(settings.onClick.call($toastBox, element) === false || !settings.closeOnClick) {
516+
module.verbose('Click callback returned false or close denied by setting cancelling close');
517+
return;
518+
}
497519
module.close();
498520
}
499521
},
@@ -834,6 +856,10 @@ $.fn.toast.settings = {
834856
unclickable : 'unclickable'
835857
},
836858

859+
text: {
860+
close : 'Close'
861+
},
862+
837863
icons : {
838864
info : 'info',
839865
success : 'checkmark',

src/definitions/modules/toast.less

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,12 @@
523523
}
524524
}
525525
&.vertical when (@variationToastVertical) {
526+
& > .content {
527+
flex-grow: 1;
528+
}
529+
&.attached when (@variationToastAttached){
530+
flex-grow: 1;
531+
}
526532
& > .close.icon + .content when (@variationToastClose){
527533
padding-left: @toastCloseDistanceVertical;
528534
}
@@ -554,6 +560,29 @@
554560
border-bottom-right-radius: 0;
555561
}
556562
}
563+
564+
&.ui.ui.ui.image when (@variationToastImage) {
565+
padding: 0;
566+
& > .content {
567+
padding-top: @inputVerticalPadding;
568+
padding-bottom: @inputVerticalPadding;
569+
padding-right: @inputHorizontalPadding;
570+
}
571+
& > .actions when (@variationToastActions) {
572+
margin: 0;
573+
}
574+
& > .ui.image {
575+
border-top-left-radius: @defaultBorderRadius;
576+
border-bottom-left-radius: @defaultBorderRadius;
577+
&.mini {
578+
min-width: @toastImageMiniImageAdjustment;
579+
& + .content {
580+
min-height: @toastImageMiniImageAdjustment;
581+
padding-left: @toastImageMiniImagePadding;
582+
}
583+
}
584+
}
585+
}
557586
}
558587

559588

src/themes/default/modules/toast.variables

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@
5858
@toastIconCenteredAdjustment: 1.2em;
5959
@toastImageCenteredAdjustment: 2em;
6060

61+
@toastImageMiniImageAdjustment: e(%("calc(%d + %d)", @inputVerticalPadding, @toastMiniImageHeight));
62+
@toastImageMiniImagePadding: 4.4em;
63+
6164
/* Progressbar Colors */
6265
@toastInfoProgressColor: darken(@toastInfoColor,15);
6366
@toastWarningProgressColor: darken(@toastWarningColor,15);

0 commit comments

Comments
 (0)