Accessibility: Feedback & focus on deleting terms via AJAX.

When deleting a term using AJAX, notify screen reader users of the deletion using `wp.a11y.speak()`, set the active row to be unfocusable, then explicitly set new focus after the deletion is completed.

Props jeremyfelt, afercia, wido, nikunj8866, SirLouen, pmbaldha, joedolson.
Fixes #47101.
Built from https://develop.svn.wordpress.org/trunk@60700


git-svn-id: http://core.svn.wordpress.org/trunk@60036 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
joedolson
2025-09-01 21:22:31 +00:00
parent b96550533f
commit cc73b8b920
3 changed files with 42 additions and 9 deletions

View File

@@ -31,6 +31,12 @@ jQuery( function($) {
if ( r ) {
data = t.attr('href').replace(/[^?]*\?/, '').replace(/action=delete/, 'action=delete-tag');
tr.children().css('backgroundColor', '#faafaa');
// Disable pointer events and all form controls/links in the row
tr.css('pointer-events', 'none');
tr.find(':input, a').prop('disabled', true).attr('tabindex', -1);
/**
* Makes a request to the server to delete the term that corresponds to the
* delete term button.
@@ -40,8 +46,20 @@ jQuery( function($) {
* @return {void}
*/
$.post(ajaxurl, data, function(r){
var message;
if ( '1' == r ) {
$('#ajax-response').empty();
let nextFocus = tr.next( 'tr' ).find( 'a.row-title' );
let prevFocus = tr.prev( 'tr' ).find( 'a.row-title' );
// If there is neither a next row or a previous row, focus the tag input field.
if ( nextFocus.length < 1 && prevFocus.length < 1 ) {
nextFocus = $( '#tag-name' ).trigger( 'focus' );
} else {
if ( nextFocus.length < 1 ) {
nextFocus = prevFocus;
}
}
tr.fadeOut('normal', function(){ tr.remove(); });
/**
@@ -53,23 +71,38 @@ jQuery( function($) {
*/
$('select#parent option[value="' + data.match(/tag_ID=(\d+)/)[1] + '"]').remove();
$('a.tag-link-' + data.match(/tag_ID=(\d+)/)[1]).remove();
nextFocus.trigger( 'focus' );
message = wp.i18n.__( 'The selected tag has been deleted.' );
} else if ( '-1' == r ) {
$('#ajax-response').empty().append('<div class="notice notice-error"><p>' + wp.i18n.__( 'Sorry, you are not allowed to do that.' ) + '</p></div>');
tr.children().css('backgroundColor', '');
message = wp.i18n.__( 'Sorry, you are not allowed to do that.' );
$('#ajax-response').empty().append('<div class="notice notice-error"><p>' + message + '</p></div>');
resetRowAfterFailure( tr );
} else {
$('#ajax-response').empty().append('<div class="notice notice-error"><p>' + wp.i18n.__( 'An error occurred while processing your request. Please try again later.' ) + '</p></div>');
tr.children().css('backgroundColor', '');
message = wp.i18n.__( 'An error occurred while processing your request. Please try again later.' );
$('#ajax-response').empty().append('<div class="notice notice-error"><p>' + message + '</p></div>');
resetRowAfterFailure( tr );
}
wp.a11y.speak( message, 'assertive' );
});
tr.children().css('backgroundColor', '#f33');
}
return false;
});
/**
* Restores the original UI state of a table row after an AJAX failure.
*
* @param {jQuery} tr The table row to reset.
* @return {void}
*/
function resetRowAfterFailure( tr ) {
tr.children().css( 'backgroundColor', '' );
tr.css( 'pointer-events', '' );
tr.find( ':input, a' ).prop( 'disabled', false ).removeAttr( 'tabindex' );
}
/**
* Adds a deletion confirmation when removing a tag.
*

View File

@@ -1,2 +1,2 @@
/*! This file is auto-generated */
jQuery(function(o){var s=!1;o("#the-list").on("click",".delete-tag",function(){var t,e=o(this),n=e.parents("tr"),r=!0;return(r="undefined"!=showNotice?showNotice.warn():r)&&(t=e.attr("href").replace(/[^?]*\?/,"").replace(/action=delete/,"action=delete-tag"),o.post(ajaxurl,t,function(e){"1"==e?(o("#ajax-response").empty(),n.fadeOut("normal",function(){n.remove()}),o('select#parent option[value="'+t.match(/tag_ID=(\d+)/)[1]+'"]').remove(),o("a.tag-link-"+t.match(/tag_ID=(\d+)/)[1]).remove()):("-1"==e?o("#ajax-response").empty().append('<div class="notice notice-error"><p>'+wp.i18n.__("Sorry, you are not allowed to do that.")+"</p></div>"):o("#ajax-response").empty().append('<div class="notice notice-error"><p>'+wp.i18n.__("An error occurred while processing your request. Please try again later.")+"</p></div>"),n.children().css("backgroundColor",""))}),n.children().css("backgroundColor","#f33")),!1}),o("#edittag").on("click",".delete",function(e){if("undefined"==typeof showNotice)return!0;showNotice.warn()||e.preventDefault()}),o("#submit").on("click",function(){var a=o(this).parents("form");return s||(s=!0,a.find(".submit .spinner").addClass("is-active"),o.post(ajaxurl,o("#addtag").serialize(),function(e){var t,n,r;if(s=!1,a.find(".submit .spinner").removeClass("is-active"),o("#ajax-response").empty(),(t=wpAjax.parseAjaxResponse(e,"ajax-response")).errors&&"empty_term_name"===t.responses[0].errors[0].code&&validateForm(a),t&&!t.errors){if(0<(e=a.find("select#parent").val())&&0<o("#tag-"+e).length?o(".tags #tag-"+e).after(t.responses[0].supplemental.noparents):o(".tags").prepend(t.responses[0].supplemental.parents),o(".tags .no-items").remove(),a.find("select#parent")){for(e=t.responses[1].supplemental,n="",r=0;r<t.responses[1].position;r++)n+="&nbsp;&nbsp;&nbsp;";a.find("select#parent option:selected").after('<option value="'+e.term_id+'">'+n+e.name+"</option>")}o('input:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]):not([type="reset"]):visible, textarea:visible',a).val("")}})),!1})});
jQuery(function(s){var o=!1;function a(e){e.children().css("backgroundColor",""),e.css("pointer-events",""),e.find(":input, a").prop("disabled",!1).removeAttr("tabindex")}s("#the-list").on("click",".delete-tag",function(){var n,e=s(this),r=e.parents("tr"),t=!0;return(t="undefined"!=showNotice?showNotice.warn():t)&&(n=e.attr("href").replace(/[^?]*\?/,"").replace(/action=delete/,"action=delete-tag"),r.children().css("backgroundColor","#faafaa"),r.css("pointer-events","none"),r.find(":input, a").prop("disabled",!0).attr("tabindex",-1),s.post(ajaxurl,n,function(e){if("1"==e){s("#ajax-response").empty();let e=r.next("tr").find("a.row-title");var t=r.prev("tr").find("a.row-title");e.length<1&&t.length<1?e=s("#tag-name").trigger("focus"):e.length<1&&(e=t),r.fadeOut("normal",function(){r.remove()}),s('select#parent option[value="'+n.match(/tag_ID=(\d+)/)[1]+'"]').remove(),s("a.tag-link-"+n.match(/tag_ID=(\d+)/)[1]).remove(),e.trigger("focus"),t=wp.i18n.__("The selected tag has been deleted.")}else t="-1"==e?wp.i18n.__("Sorry, you are not allowed to do that."):wp.i18n.__("An error occurred while processing your request. Please try again later."),s("#ajax-response").empty().append('<div class="notice notice-error"><p>'+t+"</p></div>"),a(r);wp.a11y.speak(t,"assertive")})),!1}),s("#edittag").on("click",".delete",function(e){if("undefined"==typeof showNotice)return!0;showNotice.warn()||e.preventDefault()}),s("#submit").on("click",function(){var a=s(this).parents("form");return o||(o=!0,a.find(".submit .spinner").addClass("is-active"),s.post(ajaxurl,s("#addtag").serialize(),function(e){var t,n,r;if(o=!1,a.find(".submit .spinner").removeClass("is-active"),s("#ajax-response").empty(),(t=wpAjax.parseAjaxResponse(e,"ajax-response")).errors&&"empty_term_name"===t.responses[0].errors[0].code&&validateForm(a),t&&!t.errors){if(0<(e=a.find("select#parent").val())&&0<s("#tag-"+e).length?s(".tags #tag-"+e).after(t.responses[0].supplemental.noparents):s(".tags").prepend(t.responses[0].supplemental.parents),s(".tags .no-items").remove(),a.find("select#parent")){for(e=t.responses[1].supplemental,n="",r=0;r<t.responses[1].position;r++)n+="&nbsp;&nbsp;&nbsp;";a.find("select#parent option:selected").after('<option value="'+e.term_id+'">'+n+e.name+"</option>")}s('input:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]):not([type="reset"]):visible, textarea:visible',a).val("")}})),!1})});

View File

@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '6.9-alpha-60699';
$wp_version = '6.9-alpha-60700';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.