Skip to content

Commit 4322c88

Browse files
LausselloicXhmikosR
authored andcommitted
implement data-dismiss="toast" to allow user to interact itself with the component (#27155)
1 parent ce80dd0 commit 4322c88

File tree

4 files changed

+117
-32
lines changed

4 files changed

+117
-32
lines changed

js/src/toast.js

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ const Toast = (($) => {
2222
const JQUERY_NO_CONFLICT = $.fn[NAME]
2323

2424
const Event = {
25-
HIDE : `hide${EVENT_KEY}`,
26-
HIDDEN : `hidden${EVENT_KEY}`,
27-
SHOW : `show${EVENT_KEY}`,
28-
SHOWN : `shown${EVENT_KEY}`
25+
CLICK_DISMISS : `click.dismiss${EVENT_KEY}`,
26+
HIDE : `hide${EVENT_KEY}`,
27+
HIDDEN : `hidden${EVENT_KEY}`,
28+
SHOW : `show${EVENT_KEY}`,
29+
SHOWN : `shown${EVENT_KEY}`
2930
}
3031

3132
const ClassName = {
@@ -49,6 +50,10 @@ const Toast = (($) => {
4950
}
5051
}
5152

53+
const Selector = {
54+
DATA_DISMISS : '[data-dismiss="toast"]'
55+
}
56+
5257
/**
5358
* ------------------------------------------------------------------------
5459
* Class Definition
@@ -60,6 +65,7 @@ const Toast = (($) => {
6065
this._element = element
6166
this._config = this._getConfig(config)
6267
this._timeout = null
68+
this._setListeners()
6369
}
6470

6571
// Getters
@@ -104,30 +110,20 @@ const Toast = (($) => {
104110
}, this._config.delay.show)
105111
}
106112

107-
hide() {
113+
hide(withoutTimeout) {
108114
if (!this._element.classList.contains(ClassName.SHOW)) {
109115
return
110116
}
111117

112118
$(this._element).trigger(Event.HIDE)
113119

114-
const complete = () => {
115-
$(this._element).trigger(Event.HIDDEN)
120+
if (withoutTimeout) {
121+
this._close()
122+
} else {
123+
this._timeout = setTimeout(() => {
124+
this._close()
125+
}, this._config.delay.hide)
116126
}
117-
118-
this._timeout = setTimeout(() => {
119-
this._element.classList.remove(ClassName.SHOW)
120-
121-
if (this._config.animation) {
122-
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
123-
124-
$(this._element)
125-
.one(Util.TRANSITION_END, complete)
126-
.emulateTransitionEnd(transitionDuration)
127-
} else {
128-
complete()
129-
}
130-
}, this._config.delay.hide)
131127
}
132128

133129
dispose() {
@@ -138,6 +134,8 @@ const Toast = (($) => {
138134
this._element.classList.remove(ClassName.SHOW)
139135
}
140136

137+
$(this._element).off(Event.CLICK_DISMISS)
138+
141139
$.removeData(this._element, DATA_KEY)
142140
this._element = null
143141
this._config = null
@@ -168,6 +166,32 @@ const Toast = (($) => {
168166
return config
169167
}
170168

169+
_setListeners() {
170+
$(this._element).on(
171+
Event.CLICK_DISMISS,
172+
Selector.DATA_DISMISS,
173+
() => this.hide(true)
174+
)
175+
}
176+
177+
_close() {
178+
const complete = () => {
179+
$(this._element).trigger(Event.HIDDEN)
180+
}
181+
182+
this._element.classList.remove(ClassName.SHOW)
183+
184+
if (this._config.animation) {
185+
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
186+
187+
$(this._element)
188+
.one(Util.TRANSITION_END, complete)
189+
.emulateTransitionEnd(transitionDuration)
190+
} else {
191+
complete()
192+
}
193+
}
194+
171195
// Static
172196

173197
static _jQueryInterface(config) {

js/tests/unit/toast.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,33 @@ $(function () {
232232
})
233233
.bootstrapToast('show')
234234
})
235+
236+
237+
QUnit.test('should close toast when close element with data-dismiss attribute is set', function (assert) {
238+
assert.expect(2)
239+
var done = assert.async()
240+
241+
var toastHtml =
242+
'<div class="toast" data-delay="1" data-autohide="false" data-animation="false">' +
243+
'<button type="button" class="ml-2 mb-1 close" data-dismiss="toast">' +
244+
'close' +
245+
'</button>' +
246+
'</div>'
247+
248+
var $toast = $(toastHtml)
249+
.bootstrapToast()
250+
.appendTo($('#qunit-fixture'))
251+
252+
$toast
253+
.on('shown.bs.toast', function () {
254+
assert.strictEqual($toast.hasClass('show'), true)
255+
var button = $toast.find('.close')
256+
button.trigger('click')
257+
})
258+
.on('hidden.bs.toast', function () {
259+
assert.strictEqual($toast.hasClass('show'), false)
260+
done()
261+
})
262+
.bootstrapToast('show')
263+
})
235264
})

js/tests/visual/toast.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,28 @@ <h1>Toast <small>Bootstrap Visual Test</small></h1>
2626
</div>
2727

2828
<div class="notifications">
29-
<div id="toastAutoHide" class="toast">
29+
<div id="toastAutoHide" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
3030
<div class="toast-header">
3131
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
3232
<strong class="mr-auto">Bootstrap</strong>
3333
<small>11 mins ago</small>
34+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
35+
<span aria-hidden="true">&times;</span>
36+
</button>
3437
</div>
3538
<div class="toast-body">
3639
Hello, world! This is a toast message with <strong>autohide</strong> in 2 seconds
3740
</div>
3841
</div>
3942

40-
<div class="toast" data-autohide="false">
43+
<div class="toast" data-autohide="false" role="alert" aria-live="assertive" aria-atomic="true">
4144
<div class="toast-header">
4245
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
4346
<strong class="mr-auto">Bootstrap</strong>
4447
<small class="text-muted">2 seconds ago</small>
48+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
49+
<span aria-hidden="true">&times;</span>
50+
</button>
4551
</div>
4652
<div class="toast-body">
4753
Heads up, toasts will stack automatically

site/docs/4.1/components/toasts.md

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ A basic toast can include a header (though it doesn't strictly need one) with wh
2424

2525
<div class="bg-light">
2626
{% capture example %}
27-
<div class="toast">
27+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
2828
<div class="toast-header">
2929
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
3030
<strong class="mr-auto">Bootstrap</strong>
3131
<small>11 mins ago</small>
32+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
33+
<span aria-hidden="true">&times;</span>
34+
</button>
3235
</div>
3336
<div class="toast-body">
3437
Hello, world! This is a toast message.
@@ -42,11 +45,14 @@ They're slightly translucent, too, so they blend over whatever they might appear
4245

4346
<div class="bg-dark">
4447
{% capture example %}
45-
<div class="toast">
48+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
4649
<div class="toast-header">
4750
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
4851
<strong class="mr-auto">Bootstrap</strong>
4952
<small class="text-muted">11 mins ago</small>
53+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
54+
<span aria-hidden="true">&times;</span>
55+
</button>
5056
</div>
5157
<div class="toast-body">
5258
Hello, world! This is a toast message.
@@ -60,22 +66,28 @@ Plus, they'll easily stack.
6066

6167
<div class="bg-light">
6268
{% capture example %}
63-
<div class="toast">
69+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
6470
<div class="toast-header">
6571
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
6672
<strong class="mr-auto">Bootstrap</strong>
6773
<small class="text-muted">just now</small>
74+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
75+
<span aria-hidden="true">&times;</span>
76+
</button>
6877
</div>
6978
<div class="toast-body">
7079
See? Just like this.
7180
</div>
7281
</div>
7382

74-
<div class="toast">
83+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
7584
<div class="toast-header">
7685
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
7786
<strong class="mr-auto">Bootstrap</strong>
7887
<small class="text-muted">2 seconds ago</small>
88+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
89+
<span aria-hidden="true">&times;</span>
90+
</button>
7991
</div>
8092
<div class="toast-body">
8193
Heads up, toasts will stack automatically
@@ -88,10 +100,12 @@ Plus, they'll easily stack.
88100
## Accessibility
89101

90102
Toasts are intended to be small interruptions to your visitors or users, so to help those on screen readers, you should wrap your toasts in an [`aria-live` region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions). This allows screen readers the ability to see suggested interruptions without any visual cues.
103+
To improve accessibility level, we strongly recomend to use `autohide: false` and add a `close` button into the header to let user dismiss that element.
104+
You also need to adapt the `role` and `aria-live` level depending on the content. If it's an important message like error, use an `alert` role `assertive` otherwise use a role `status` with a `polite` level.
91105

92106
{% highlight html %}
93-
<div role="region" aria-live="polite">
94-
<div class="toast">...</div>
107+
<div role="alert" aria-live="assertive" aria-atomic="true">
108+
<div role="alert" aria-live="assertive" aria-atomic="true">...</div>
95109
</div>
96110
{% endhighlight %}
97111

@@ -107,6 +121,9 @@ Place toasts with custom CSS as you need them. The top right is often used for n
107121
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
108122
<strong class="mr-auto">Bootstrap</strong>
109123
<small>11 mins ago</small>
124+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
125+
<span aria-hidden="true">&times;</span>
126+
</button>
110127
</div>
111128
<div class="toast-body">
112129
Hello, world! This is a toast message.
@@ -126,22 +143,28 @@ For systems that generate more notifications, consider using a wrapping element
126143
<div style="position: absolute; top: 0; right: 0;">
127144

128145
<!-- Then put toasts within -->
129-
<div class="toast">
146+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
130147
<div class="toast-header">
131148
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
132149
<strong class="mr-auto">Bootstrap</strong>
133150
<small class="text-muted">just now</small>
151+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
152+
<span aria-hidden="true">&times;</span>
153+
</button>
134154
</div>
135155
<div class="toast-body">
136156
See? Just like this.
137157
</div>
138158
</div>
139159

140-
<div class="toast">
160+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
141161
<div class="toast-header">
142162
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
143163
<strong class="mr-auto">Bootstrap</strong>
144164
<small class="text-muted">2 seconds ago</small>
165+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
166+
<span aria-hidden="true">&times;</span>
167+
</button>
145168
</div>
146169
<div class="toast-body">
147170
Heads up, toasts will stack automatically
@@ -162,11 +185,14 @@ You can also get fancy with flexbox utilities.
162185
<div class="d-flex justify-content-center" style="position: absolute; top: 0; right: 0; left: 0;">
163186

164187
<!-- Then put toasts within -->
165-
<div class="toast">
188+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
166189
<div class="toast-header">
167190
<img class="rounded mr-2" data-src="holder.js/20x20?size=1&text=.&bg=#007aff" alt="">
168191
<strong class="mr-auto">Bootstrap</strong>
169192
<small>11 mins ago</small>
193+
<button type="button" class="close" data-dismiss="toast" aria-label="Close" style="">
194+
<span aria-hidden="true">&times;</span>
195+
</button>
170196
</div>
171197
<div class="toast-body">
172198
Hello, world! This is a toast message.

0 commit comments

Comments
 (0)