Skip to content

Commit 502759d

Browse files
committed
Improve accessibility feedback for quick actions
1 parent f7e3a87 commit 502759d

File tree

3 files changed

+275
-9
lines changed

3 files changed

+275
-9
lines changed

liens-morts-detector-jlg/assets/js/blc-admin-scripts.js

Lines changed: 146 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
jQuery(document).ready(function($) {
2+
var ACTION_FOCUS_SELECTOR = '.blc-edit-link, .blc-unlink';
3+
24
var defaultMessages = {
35
editPromptMessage: "Entrez la nouvelle URL pour :\n%s",
46
editPromptDefault: 'https://',
@@ -14,11 +16,58 @@ jQuery(document).ready(function($) {
1416
emptyUrlMessage: 'Veuillez saisir une URL.',
1517
invalidUrlMessage: 'Veuillez saisir une URL valide.',
1618
sameUrlMessage: "La nouvelle URL doit être différente de l'URL actuelle.",
17-
genericError: 'Une erreur est survenue. Veuillez réessayer.'
19+
genericError: 'Une erreur est survenue. Veuillez réessayer.',
20+
successAnnouncement: 'La ligne a été mise à jour avec succès.'
1821
};
1922

2023
var messages = $.extend({}, defaultMessages, window.blcAdminMessages || {});
2124

25+
var accessibility = (function() {
26+
var $liveRegion = null;
27+
28+
function ensureLiveRegion() {
29+
if ($liveRegion && $liveRegion.length && document.body.contains($liveRegion[0])) {
30+
return $liveRegion;
31+
}
32+
33+
$liveRegion = $('<div>', {
34+
class: 'blc-aria-live screen-reader-text',
35+
'aria-live': 'polite',
36+
'aria-atomic': 'true'
37+
});
38+
39+
$('body').append($liveRegion);
40+
41+
return $liveRegion;
42+
}
43+
44+
function speak(message, politeness) {
45+
if (!message) {
46+
return;
47+
}
48+
49+
if (window.wp && wp.a11y && typeof wp.a11y.speak === 'function') {
50+
wp.a11y.speak(message, politeness || 'polite');
51+
return;
52+
}
53+
54+
var $region = ensureLiveRegion();
55+
$region.text('');
56+
57+
window.setTimeout(function() {
58+
$region.text(message);
59+
}, 50);
60+
}
61+
62+
return {
63+
speak: speak,
64+
ensureLiveRegion: ensureLiveRegion
65+
};
66+
})();
67+
68+
window.blcAdmin = window.blcAdmin || {};
69+
window.blcAdmin.accessibility = accessibility;
70+
2271
var modal = (function() {
2372
var $modal = $('#blc-modal');
2473

@@ -103,7 +152,23 @@ jQuery(document).ready(function($) {
103152
$modal.toggleClass('is-submitting', isSubmitting);
104153
}
105154

106-
function close() {
155+
function normalizeFocusTarget(focusTarget) {
156+
if (!focusTarget) {
157+
return null;
158+
}
159+
160+
if (focusTarget.jquery) {
161+
focusTarget = focusTarget.get(0);
162+
}
163+
164+
if (focusTarget && typeof focusTarget.focus === 'function') {
165+
return focusTarget;
166+
}
167+
168+
return null;
169+
}
170+
171+
function close(focusTarget) {
107172
if (!state.isOpen) {
108173
return;
109174
}
@@ -124,8 +189,18 @@ jQuery(document).ready(function($) {
124189
$input.val('').attr('type', 'url');
125190
$field.removeClass('is-hidden');
126191

127-
if (lastFocusedElement && typeof lastFocusedElement.focus === 'function') {
128-
lastFocusedElement.focus();
192+
var finalFocusTarget = normalizeFocusTarget(focusTarget);
193+
194+
var body = document.body;
195+
196+
if (!finalFocusTarget && lastFocusedElement && body && typeof body.contains === 'function' && body.contains(lastFocusedElement) && typeof lastFocusedElement.focus === 'function') {
197+
finalFocusTarget = lastFocusedElement;
198+
}
199+
200+
if (finalFocusTarget) {
201+
window.setTimeout(function() {
202+
finalFocusTarget.focus();
203+
}, 0);
129204
}
130205

131206
lastFocusedElement = null;
@@ -279,6 +354,71 @@ jQuery(document).ready(function($) {
279354
};
280355
})();
281356

357+
function getAnnouncementMessage(response) {
358+
if (!response || !response.data) {
359+
return messages.successAnnouncement || '';
360+
}
361+
362+
var data = response.data;
363+
364+
if (typeof data.announcement === 'string' && data.announcement.trim()) {
365+
return data.announcement.trim();
366+
}
367+
368+
if (typeof data.message === 'string' && data.message.trim()) {
369+
return data.message.trim();
370+
}
371+
372+
return messages.successAnnouncement || '';
373+
}
374+
375+
function findNextFocusTarget(row) {
376+
var $row = row && row.jquery ? row : $(row);
377+
378+
if (!$row || !$row.length) {
379+
return null;
380+
}
381+
382+
var $candidate = $row.nextAll('tr').filter(':visible').find(ACTION_FOCUS_SELECTOR).filter(':visible').first();
383+
384+
if (!$candidate.length) {
385+
$candidate = $row.prevAll('tr').filter(':visible').find(ACTION_FOCUS_SELECTOR).filter(':visible').first();
386+
}
387+
388+
if (!$candidate.length) {
389+
$candidate = $('#post-query-submit').filter(':visible').first();
390+
}
391+
392+
if (!$candidate.length) {
393+
$candidate = $('.tablenav .button, .tablenav input[type="submit"]').filter(':visible').first();
394+
}
395+
396+
return $candidate.length ? $candidate[0] : null;
397+
}
398+
399+
function handleSuccessfulResponse(response, row, helpers) {
400+
var $row = row && row.jquery ? row : $(row);
401+
402+
accessibility.speak(getAnnouncementMessage(response), 'polite');
403+
404+
var nextFocusTarget = findNextFocusTarget($row);
405+
406+
if (helpers && typeof helpers.close === 'function') {
407+
helpers.close(nextFocusTarget);
408+
}
409+
410+
if ($row && $row.length) {
411+
$row.fadeOut(300, function() {
412+
$(this).remove();
413+
});
414+
}
415+
}
416+
417+
window.blcAdmin.listActions = $.extend({}, window.blcAdmin.listActions, {
418+
handleSuccessfulResponse: handleSuccessfulResponse,
419+
findNextFocusTarget: findNextFocusTarget
420+
});
421+
282422
function hasWhitespace(value) {
283423
return /\s/.test(value);
284424
}
@@ -347,8 +487,7 @@ jQuery(document).ready(function($) {
347487
_ajax_nonce: nonce
348488
}).done(function(response) {
349489
if (response && response.success) {
350-
helpers.close();
351-
row.fadeOut(300, function() { $(this).remove(); });
490+
handleSuccessfulResponse(response, row, helpers);
352491
} else {
353492
var errorMessage = response && response.data && response.data.message
354493
? response.data.message
@@ -412,8 +551,7 @@ jQuery(document).ready(function($) {
412551
_ajax_nonce: nonce
413552
}).done(function(response) {
414553
if (response && response.success) {
415-
helpers.close();
416-
row.fadeOut(300, function() { $(this).remove(); });
554+
handleSuccessfulResponse(response, row, helpers);
417555
} else {
418556
var errorMessage = response && response.data && response.data.message
419557
? response.data.message

liens-morts-detector-jlg/liens-morts-detector-jlg.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ function blc_enqueue_admin_assets($hook) {
141141
'emptyUrlMessage' => __('Veuillez saisir une URL.', 'liens-morts-detector-jlg'),
142142
'invalidUrlMessage' => __('Veuillez saisir une URL valide.', 'liens-morts-detector-jlg'),
143143
'sameUrlMessage' => __('La nouvelle URL doit être différente de l\'URL actuelle.', 'liens-morts-detector-jlg'),
144-
'genericError' => __('Une erreur est survenue. Veuillez réessayer.', 'liens-morts-detector-jlg'),
144+
'genericError' => __('Une erreur est survenue. Veuillez réessayer.', 'liens-morts-detector-jlg'),
145+
'successAnnouncement' => __('Action effectuée avec succès. La ligne a été retirée de la liste.', 'liens-morts-detector-jlg'),
145146
)
146147
);
147148
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
const path = require('path');
2+
const $ = require('jquery');
3+
4+
describe('blc-admin-scripts accessibility helper', () => {
5+
let originalReady;
6+
let originalFadeOut;
7+
let originalVisible;
8+
let originalHidden;
9+
10+
beforeEach(() => {
11+
jest.resetModules();
12+
document.body.innerHTML = `
13+
<div>
14+
<table>
15+
<tbody id="the-list">
16+
<tr id="row-1">
17+
<td><button type="button" class="blc-edit-link">Modifier</button></td>
18+
</tr>
19+
<tr id="row-2">
20+
<td><button type="button" class="blc-edit-link">Modifier</button></td>
21+
</tr>
22+
</tbody>
23+
</table>
24+
<div class="tablenav">
25+
<button type="button" id="post-query-submit">Filtrer</button>
26+
</div>
27+
</div>
28+
`;
29+
30+
originalReady = $.fn.ready;
31+
$.fn.ready = function(fn) {
32+
fn.call(document, $);
33+
return this;
34+
};
35+
36+
originalVisible = $.expr.pseudos.visible;
37+
originalHidden = $.expr.pseudos.hidden;
38+
$.expr.pseudos.visible = () => true;
39+
$.expr.pseudos.hidden = () => false;
40+
41+
originalFadeOut = $.fn.fadeOut;
42+
$.fn.fadeOut = function(_duration, callback) {
43+
if (typeof callback === 'function') {
44+
callback.call(this);
45+
}
46+
return this;
47+
};
48+
49+
window.blcAdminMessages = {
50+
successAnnouncement: 'Action effectuée avec succès.'
51+
};
52+
53+
window.wp = {
54+
a11y: {
55+
speak: jest.fn()
56+
}
57+
};
58+
59+
global.jQuery = $;
60+
global.$ = $;
61+
window.jQuery = $;
62+
window.$ = $;
63+
64+
require(path.resolve(__dirname, '../../..', 'liens-morts-detector-jlg/assets/js/blc-admin-scripts.js'));
65+
});
66+
67+
afterEach(() => {
68+
delete window.blcAdminMessages;
69+
delete window.blcAdmin;
70+
delete window.wp;
71+
delete global.jQuery;
72+
delete global.$;
73+
delete window.jQuery;
74+
delete window.$;
75+
76+
if (originalReady) {
77+
$.fn.ready = originalReady;
78+
} else {
79+
delete $.fn.ready;
80+
}
81+
82+
if (originalFadeOut) {
83+
$.fn.fadeOut = originalFadeOut;
84+
} else {
85+
delete $.fn.fadeOut;
86+
}
87+
88+
if (originalVisible) {
89+
$.expr.pseudos.visible = originalVisible;
90+
} else {
91+
delete $.expr.pseudos.visible;
92+
}
93+
94+
if (originalHidden) {
95+
$.expr.pseudos.hidden = originalHidden;
96+
} else {
97+
delete $.expr.pseudos.hidden;
98+
}
99+
100+
jest.resetModules();
101+
102+
originalReady = undefined;
103+
originalFadeOut = undefined;
104+
originalVisible = undefined;
105+
originalHidden = undefined;
106+
});
107+
108+
it('announces success through wp.a11y.speak for successful responses', () => {
109+
const row = $('#row-1');
110+
const helpers = {
111+
close: jest.fn()
112+
};
113+
114+
window.blcAdmin.listActions.handleSuccessfulResponse(
115+
{
116+
success: true,
117+
data: {
118+
announcement: 'La ligne a été mise à jour.'
119+
}
120+
},
121+
row,
122+
helpers
123+
);
124+
125+
expect(window.wp.a11y.speak).toHaveBeenCalledWith('La ligne a été mise à jour.', 'polite');
126+
});
127+
});

0 commit comments

Comments
 (0)