Skip to content

Commit 3b27fe8

Browse files
microbit-markmicrobit-carlos
authored andcommitted
A11y: Focus err and msg modals and close with esc (#310)
1 parent 4fda800 commit 3b27fe8

File tree

2 files changed

+50
-20
lines changed

2 files changed

+50
-20
lines changed

editor.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
UPY_VERSION = "1.0.1";
8383
</script>
8484
<script id="files-template" type="x-tmpl-mustache">
85-
<div tabindex="-1" role="dialog" aria-label="load/save modal" aria-modal="true" class="modal-div">
85+
<div id="files-modal" tabindex="-1" role="dialog" aria-label="load/save modal" aria-modal="true" class="modal-div">
8686
<h2 class="modal-title"><i class="fa fa-upload"></i> <strong>{{ load-title }}</strong></h2>
8787
<div class="load-drag-target" id="load-drag-target">
8888
<input type="file" style="display: none" name="load-form-file-upload" id="file-upload-input">
@@ -140,7 +140,7 @@ <h2 class="modal-title"><i class="fa fa-download"></i> <strong>{{ files-title }}
140140
</div>
141141
</script>
142142
<script id="snippet-template" type="x-tmpl-mustache">
143-
<div tabindex="-1" role="dialog" aria-label="snippets modal" aria-modal="true" class="modal-div">
143+
<div id="snippet-modal" tabindex="-1" role="dialog" aria-label="snippets modal" aria-modal="true" class="modal-div">
144144
<h2><i class="fa fa-cogs"></i> <strong>{{ title }}</strong></h2>
145145
<p>{{ description }}</p>
146146
<p>{{ instructions }}</p>
@@ -395,7 +395,7 @@ <h2><i class="fa fa-unlock-alt"></i> <strong>{{ title }}</strong></h2>
395395
<div id="editor" class="flex3" tabindex="-1"></div>
396396
</div>
397397
<div id="flashing-overlay-container" class="modal-overlay-container">
398-
<div id="flashing-overlay" class="modal-overlay" tabindex="-1" role="dialog" aria-labelledby="flashing-overlay" aria-modal="true">
398+
<div id="flashing-overlay" class="modal-overlay modal-div" tabindex="-1" role="dialog" aria-labelledby="flashing-overlay" aria-modal="true">
399399
<div id="flashing-info">
400400
<h2 id="flashing-text">Flashing micro:bit</h2>
401401
<div id="webusb-flashing-loader"></div>
@@ -407,8 +407,8 @@ <h2 id="flashing-text">Flashing micro:bit</h2>
407407
</div>
408408
</div>
409409
<div id="modal-msg-overlay-container" class="modal-overlay-container modal-msg-overlay-container">
410-
<div id="modal-msg-overlay" class="modal-overlay" tabindex="-1" role="dialog" aria-labelledby="flashing-overlay" aria-modal="true">
411-
<a class="vex-close modal-close" onclick="$('#modal-msg-overlay-container').hide()"></a>
410+
<div id="modal-msg-overlay" class="modal-overlay modal-div" tabindex="-1" role="dialog" aria-labelledby="modal-msg-overlay" aria-modal="true">
411+
<a id="modal-msg-close-cross" class="vex-close modal-close"></a>
412412
<div id="modal-msg-text">
413413
<h2 id="modal-msg-title"></h2>
414414
<div id="modal-msg-content"></div>

python-main.js

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -919,9 +919,9 @@ function web_editor(config) {
919919
}
920920

921921
// Trap focus in modal and pass focus to first actionable element
922-
function focusModal() {
922+
function focusModal(modalId) {
923923
document.querySelector('body > :not(.vex)').setAttribute('aria-hidden', true);
924-
var dialog = document.querySelector('.modal-div');
924+
var dialog = document.querySelector(modalId);
925925
var focusableEls = dialog.querySelectorAll('a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])');
926926
$(focusableEls).each(function() {
927927
$(this).attr('tabindex', '0');
@@ -930,10 +930,15 @@ function web_editor(config) {
930930
dialog.onkeydown = function(event) {
931931
if (event.which == 9) {
932932
// if tab key is pressed
933-
var focusedEl = document.activeElement;
934933
var numberOfFocusableEls = focusableEls.length;
935-
var focusedElIndex = Array.prototype.indexOf.call(focusableEls, focusedEl);
934+
if (!numberOfFocusableEls) {
935+
dialog.focus();
936+
event.preventDefault();
937+
return;
938+
}
936939

940+
var focusedEl = document.activeElement;
941+
var focusedElIndex = Array.prototype.indexOf.call(focusableEls, focusedEl);
937942
if (event.which == 16) {
938943
// if focused on first item and user shift-tabs back, go to the last focusable item
939944
if (focusedElIndex == 0) {
@@ -987,7 +992,7 @@ function web_editor(config) {
987992
vex.open({
988993
content: Mustache.render(template, loadStrings),
989994
afterOpen: function(vexContent) {
990-
focusModal();
995+
focusModal("#files-modal");
991996
$("#show-files").attr("title", loadStrings["show-files"] +" (" + micropythonFs.ls().length + ")");
992997
document.getElementById("show-files").innerHTML = loadStrings["show-files"] + " (" + micropythonFs.ls().length + ") <i class='fa fa-caret-down'>";
993998
$('#save-hex').click(function() {
@@ -1173,7 +1178,7 @@ function web_editor(config) {
11731178
vex.open({
11741179
content: Mustache.render(template, context),
11751180
afterOpen: function(vexContent) {
1176-
focusModal();
1181+
focusModal("#snippet-modal");
11771182
$(vexContent).find('.snippet-selection').click(function(e){
11781183
var snippet_name = $(this).find('.snippet-name').text();
11791184
EDITOR.triggerSnippet(snippet_name);
@@ -1236,15 +1241,16 @@ function web_editor(config) {
12361241
loadPy(file.name, e.target.result);
12371242
};
12381243
reader.readAsText(file);
1244+
$('#editor').focus();
12391245
} else if (ext == 'hex') {
12401246
reader.onload = function(e) {
12411247
loadHex(file.name, e.target.result);
12421248
};
12431249
reader.readAsText(file);
1244-
}else{
1250+
$('#editor').focus();
1251+
} else {
12451252
invalidFileWarning(ext);
12461253
}
1247-
$('#editor').focus();
12481254
}
12491255

12501256
function showDisconnectError(event) {
@@ -1329,7 +1335,7 @@ function web_editor(config) {
13291335
$("#flashing-info").addClass('hidden');
13301336

13311337
// Log error to console for feedback
1332-
console.log("An error occured whilst attempting to use WebUSB.");
1338+
console.log("An error occurred whilst attempting to use WebUSB.");
13331339
console.log("Details of the error can be found below, and may be useful when trying to replicate and debug the error.");
13341340
console.log(err);
13351341
console.trace();
@@ -1347,7 +1353,7 @@ function web_editor(config) {
13471353

13481354
// Disconnect from the microbit
13491355
doDisconnect();
1350-
1356+
13511357
var errorType;
13521358
var errorTitle;
13531359
var errorDescription;
@@ -1450,6 +1456,15 @@ function web_editor(config) {
14501456
// Attach download handler
14511457
$("#flashing-overlay-download").click(doDownload);
14521458

1459+
// Make the modal accessible now that all the content is present
1460+
focusModal("#flashing-overlay");
1461+
// If escape key is pressed close modal
1462+
$('#flashing-overlay').keydown(function(e) {
1463+
if (e.which == 27) {
1464+
flashErrorClose();
1465+
}
1466+
});
1467+
14531468
// Send event
14541469
var errorMessage = (err.message ? (err.message.replace(/\W+/g, '-').replace(/\W$/, '').toLowerCase()) : "");
14551470
// Append error message, replace all special chars with '-', if last char is '-' remove it
@@ -1614,11 +1629,9 @@ function web_editor(config) {
16141629
document.dispatchEvent(new CustomEvent('webusb', { detail: details }));
16151630

16161631
console.log("Flash complete");
1617-
1632+
16181633
// Close overview
1619-
setTimeout(function(){
1620-
$("#flashing-overlay-container").hide();
1621-
}, 500);
1634+
setTimeout(flashErrorClose, 500);
16221635
})
16231636
.catch(webusbErrorHandler)
16241637
.finally(function() {
@@ -1720,16 +1733,32 @@ function web_editor(config) {
17201733
$("#modal-msg-title").text(title);
17211734
$("#modal-msg-content").html(content);
17221735
var modalLinks = [];
1736+
var addCloseClickListener = false;
17231737
if (links) {
17241738
Object.keys(links).forEach(function(key) {
17251739
if (links[key] === "close") {
1726-
modalLinks.push('<a href="#" onclick = "$(\'' + overlayContainer + '\').hide()">Close</a>');
1740+
modalLinks.push('<a href="#" id="modal-msg-close-link">' + key + '</a>');
1741+
addCloseClickListener = true;
17271742
} else {
17281743
modalLinks.push('<a href="' + links[key] + '" target="_blank">' + key + '</a>');
17291744
}
17301745
});
17311746
}
17321747
$("#modal-msg-links").html((modalLinks).join(' | '));
1748+
focusModal("#modal-msg-overlay");
1749+
var modalMsgClose = function() {
1750+
$(overlayContainer).hide()
1751+
$(overlayContainer).off("keydown");
1752+
};
1753+
$("#modal-msg-close-cross").click(modalMsgClose);
1754+
if (addCloseClickListener) {
1755+
$("#modal-msg-close-link").click(modalMsgClose);
1756+
}
1757+
$(overlayContainer).keydown(function(e) {
1758+
if (e.which == 27) {
1759+
modalMsgClose();
1760+
}
1761+
});
17331762
}
17341763

17351764
function formatMenuContainer(parentButtonId, containerId) {
@@ -1941,4 +1970,5 @@ function web_editor(config) {
19411970
function flashErrorClose() {
19421971
$('#flashing-overlay-error').html("");
19431972
$('#flashing-overlay-container').hide();
1973+
$('#flashing-overlay').off("keydown");
19441974
}

0 commit comments

Comments
 (0)