Skip to content

Commit 9b4969a

Browse files
authored
Added closeOnEsc, hideHeaderClose & enforceFocus
Added option for disabling close on ESC (prop: closeOnEsc, defaults to true) and switched to using @keyup.esc handler. Added option to hide header close button (prop: hideHeaderClose, defaults to false) Added in the enforceFocus handler to make sure focus never leaves dialog while it is open. This provides better ARIA compliance as some screen readers (and browsers) will let you tab through document even though a dialog is open. Also added aria-labeledby and aria-describedby attributes, as well as switched the modal header and footer to semantic elements for better ARIA accessibility.
1 parent 1e357ab commit 9b4969a

File tree

1 file changed

+47
-20
lines changed

1 file changed

+47
-20
lines changed

lib/components/modal.vue

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,33 +11,47 @@
1111
<div key="modal" :id="id"
1212
v-show="visible"
1313
:class="['modal',{fade :fade}]"
14+
role="dialog"
1415
@click="onClickOut($event)"
16+
@keyup.esc="onEsc($event)"
1517
>
1618

1719
<div :class="['modal-dialog','modal-'+size]">
18-
<div class="modal-content" @click.stop>
19-
20-
<div class="modal-header" v-if="!hideHeader">
20+
<div class="modal-content"
21+
tabindex="-1"
22+
role="document"
23+
ref="content"
24+
:aria-labeledby="hideHeader ? '' : (id + '_modal_title')"
25+
:aria-describedby="id + '_modal_body'"
26+
@click.stop
27+
>
28+
29+
<header class="modal-header" v-if="!hideHeader">
2130
<slot name="modal-header">
22-
<h5 class="modal-title">
31+
<h5 class="modal-title" :id="id + '_modal_title'">
2332
<slot name="modal-title">{{title}}</slot>
2433
</h5>
25-
<button type="button" class="close" aria-label="Close" @click="hide">
34+
<button type="button"
35+
v-if="!hideHeaderClose"
36+
class="close"
37+
:aria-label="closeTitle"
38+
@click="hide"
39+
>
2640
<span aria-hidden="true">&times;</span>
2741
</button>
2842
</slot>
29-
</div>
43+
</header>
3044

31-
<div class="modal-body">
45+
<div class="modal-body" :id="id + '_modal_body'">
3246
<slot></slot>
3347
</div>
3448

35-
<div class="modal-footer" v-if="!hideFooter">
49+
<footer class="modal-footer" v-if="!hideFooter">
3650
<slot name="modal-footer">
3751
<b-btn variant="secondary" @click="hide(false)">{{closeTitle}}</b-btn>
3852
<b-btn variant="primary" @click="hide(true)">{{okTitle}}</b-btn>
3953
</slot>
40-
</div>
54+
</footer>
4155

4256
</div>
4357
</div>
@@ -108,13 +122,21 @@
108122
type: Boolean,
109123
default: true
110124
},
125+
closeOnEsc: {
126+
type: Boolean,
127+
default: true
128+
},
111129
hideHeader: {
112130
type: Boolean,
113131
default: false
114132
},
115133
hideFooter: {
116134
type: Boolean,
117135
default: false
136+
},
137+
hideHeaderClose: {
138+
type: Boolean,
139+
default: false
118140
}
119141
},
120142
methods: {
@@ -163,22 +185,27 @@
163185
this.hide();
164186
}
165187
},
166-
pressedButton(e) {
167-
// If not visible don't do anything
168-
if (!this.visible) {
169-
return;
170-
}
171-
172-
// Support for esc key press
173-
const key = e.which || e.keyCode;
174-
if (key === 27) { // 27 is esc
188+
onEsc() {
189+
// If ESC presses, hide modal
190+
if (this.visible && this.closeOnEsc) {
175191
this.hide();
176192
}
177193
},
178194
afterEnter(el) {
179195
// Add show class to keep el showed just after transition is ended,
180196
// Because transition removes all used classes
181197
el.classList.add('show');
198+
},
199+
enforceFocus(e) {
200+
// If focus leaves modal, bring it back
201+
// eventListener bound on document
202+
if (this.visible &&
203+
document !== e.target &&
204+
this.$refs.content &&
205+
this.$refs.content !== e.target &&
206+
!this.$refs.content.contains(e.target) {
207+
this.$refs.content.focus();
208+
}
182209
}
183210
},
184211
created() {
@@ -196,12 +223,12 @@
196223
},
197224
mounted() {
198225
if (typeof document !== 'undefined') {
199-
document.addEventListener('keydown', this.pressedButton);
226+
document.addEventListener('focus', this.enforceFocus);
200227
}
201228
},
202229
destroyed() {
203230
if (typeof document !== 'undefined') {
204-
document.removeEventListener('keydown', this.pressedButton);
231+
document.removeEventListener('focus', this.enforceFocus);
205232
}
206233
}
207234
};

0 commit comments

Comments
 (0)