Editor: A11y: Use a tablist pattern for taxonomy terms.

Switch the classic editor's taxonomy tabs to use the APG tablist/tabpanel structure. Add appropriate ARIA roles to tabs and panels and update JS to handle required keyboard events and selected states.

Props alh0319, mukesh27, joedolson.
Fixes #63981.
Built from https://develop.svn.wordpress.org/trunk@61764


git-svn-id: http://core.svn.wordpress.org/trunk@61070 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
joedolson
2026-02-27 22:49:42 +00:00
parent aaa00322d7
commit 56325ab718
6 changed files with 71 additions and 31 deletions
+10 -10
View File
@@ -644,18 +644,18 @@ function post_categories_meta_box( $post, $box ) {
$taxonomy = get_taxonomy( $parsed_args['taxonomy'] );
?>
<div id="taxonomy-<?php echo $tax_name; ?>" class="categorydiv">
<ul id="<?php echo $tax_name; ?>-tabs" class="category-tabs">
<li class="tabs"><a href="#<?php echo $tax_name; ?>-all"><?php echo $taxonomy->labels->all_items; ?></a></li>
<li class="hide-if-no-js"><a href="#<?php echo $tax_name; ?>-pop"><?php echo esc_html( $taxonomy->labels->most_used ); ?></a></li>
<ul id="<?php echo $tax_name; ?>-tabs" class="category-tabs" role="tablist">
<li class="tabs"><a href="#<?php echo $tax_name; ?>-all" role="tab" aria-selected="true" aria-controls="<?php echo $tax_name; ?>-all"><?php echo $taxonomy->labels->all_items; ?></a></li>
<li class="hide-if-no-js"><a href="#<?php echo $tax_name; ?>-pop" role="tab" aria-controls="<?php echo $tax_name; ?>-pop"><?php echo esc_html( $taxonomy->labels->most_used ); ?></a></li>
</ul>
<div id="<?php echo $tax_name; ?>-pop" class="tabs-panel" style="display: none;">
<div id="<?php echo $tax_name; ?>-pop" class="tabs-panel" style="display: none;" role="tabpanel">
<ul id="<?php echo $tax_name; ?>checklist-pop" class="categorychecklist form-no-clear" >
<?php $popular_ids = wp_popular_terms_checklist( $tax_name ); ?>
</ul>
</div>
<div id="<?php echo $tax_name; ?>-all" class="tabs-panel">
<div id="<?php echo $tax_name; ?>-all" class="tabs-panel" role="tabpanel">
<?php
$name = ( 'category' === $tax_name ) ? 'post_category' : 'tax_input[' . $tax_name . ']';
// Allows for an empty term set to be sent. 0 is an invalid term ID and will be ignored by empty() checks.
@@ -1176,12 +1176,12 @@ function link_submit_meta_box( $link ) {
function link_categories_meta_box( $link ) {
?>
<div id="taxonomy-linkcategory" class="categorydiv">
<ul id="category-tabs" class="category-tabs">
<li class="tabs"><a href="#categories-all"><?php _e( 'All categories' ); ?></a></li>
<li class="hide-if-no-js"><a href="#categories-pop"><?php _ex( 'Most Used', 'categories' ); ?></a></li>
<ul id="category-tabs" class="category-tabs" role="tablist">
<li class="tabs"><a href="#categories-all" role="tab" aria-controls="categories-all" aria-selected="true"><?php _e( 'All categories' ); ?></a></li>
<li class="hide-if-no-js"><a href="#categories-pop" role="tab" aria-controls="categories-pop"><?php _ex( 'Most Used', 'categories' ); ?></a></li>
</ul>
<div id="categories-all" class="tabs-panel">
<div id="categories-all" class="tabs-panel" role="tabpanel">
<ul id="categorychecklist" data-wp-lists="list:category" class="categorychecklist form-no-clear">
<?php
if ( isset( $link->link_id ) ) {
@@ -1193,7 +1193,7 @@ function link_categories_meta_box( $link ) {
</ul>
</div>
<div id="categories-pop" class="tabs-panel" style="display: none;">
<div id="categories-pop" class="tabs-panel" style="display: none;" role="tabpanel">
<ul id="categorychecklist-pop" class="categorychecklist form-no-clear">
<?php wp_popular_terms_checklist( 'link_category' ); ?>
</ul>
+30 -9
View File
@@ -19,16 +19,37 @@ jQuery( function($) {
*
* @return {boolean} Always returns false to prevent the default behavior.
*/
$('#category-tabs a').on( 'click', function(){
$('#category-tabs a').on( 'click keyup keydown', function( event ){
var t = $(this).attr('href');
$(this).parent().addClass('tabs').siblings('li').removeClass('tabs');
$('.tabs-panel').hide();
$(t).show();
if ( '#categories-all' == t )
deleteUserSetting('cats');
else
setUserSetting('cats','pop');
return false;
if ( event.type === 'keydown' && event.key === ' ' ) {
event.preventDefault();
}
if ( ( event.type === 'keyup' && event.key === ' ' ) || ( event.type === 'keydown' && event.key === 'Enter' ) || event.type === 'click' ) {
event.preventDefault();
$('#category-tabs a').removeAttr( 'aria-selected' ).attr( 'tabindex', '-1' );
$(this).attr( 'aria-selected', 'true' ).removeAttr( 'tabindex' );
$(this).parent().addClass('tabs').siblings('li').removeClass('tabs');
$('.tabs-panel').hide();
$(t).show();
if ( '#categories-all' == t ) {
deleteUserSetting('cats');
} else {
setUserSetting('cats','pop');
}
return false;
}
if ( event.type === 'keyup' && ( event.key === 'ArrowRight' || event.key === 'ArrowLeft' ) ) {
$(this).attr( 'tabindex', '-1' );
let next = $(this).parent('li').next();
let prev = $(this).parent('li').prev();
if ( next.length > 0 ) {
next.find('a').removeAttr( 'tabindex');
next.find('a').trigger( 'focus' );
} else {
prev.find('a').removeAttr( 'tabindex');
prev.find('a').trigger( 'focus' );
}
}
});
if ( getUserSetting('cats') )
$('#category-tabs a[href="#categories-pop"]').trigger( 'click' );
+1 -1
View File
@@ -1,2 +1,2 @@
/*! This file is auto-generated */
jQuery(function(a){var t,c,e,i=!1;a("#link_name").trigger("focus"),postboxes.add_postbox_toggles("link"),a("#category-tabs a").on("click",function(){var t=a(this).attr("href");return a(this).parent().addClass("tabs").siblings("li").removeClass("tabs"),a(".tabs-panel").hide(),a(t).show(),"#categories-all"==t?deleteUserSetting("cats"):setUserSetting("cats","pop"),!1}),getUserSetting("cats")&&a('#category-tabs a[href="#categories-pop"]').trigger("click"),t=a("#newcat").one("focus",function(){a(this).val("").removeClass("form-input-tip")}),a("#link-category-add-submit").on("click",function(){t.focus()}),c=function(){var t,e;i||(i=!0,t=(e=a(this)).is(":checked"),e=e.val().toString(),a("#in-link-category-"+e+", #in-popular-link_category-"+e).prop("checked",t),i=!1)},e=function(t,e){a(e.what+" response_data",t).each(function(){a(a(this).text()).find("label").each(function(){var t=a(this),e=t.find("input").val(),i=t.find("input")[0].id,t=t.text().trim();a("#"+i).on("change",c),a('<option value="'+parseInt(e,10)+'"></option>').text(t)})})},a("#categorychecklist").wpList({alt:"",what:"link-category",response:"category-ajax-response",addAfter:e}),a('a[href="#categories-all"]').on("click",function(){deleteUserSetting("cats")}),a('a[href="#categories-pop"]').on("click",function(){setUserSetting("cats","pop")}),"pop"==getUserSetting("cats")&&a('a[href="#categories-pop"]').trigger("click"),a("#category-add-toggle").on("click",function(){return a(this).parents("div:first").toggleClass("wp-hidden-children"),a('#category-tabs a[href="#categories-all"]').trigger("click"),a("#newcategory").trigger("focus"),!1}),a(".categorychecklist :checkbox").on("change",c).filter(":checked").trigger("change")});
jQuery(function(i){var e,r,t,a=!1;i("#link_name").trigger("focus"),postboxes.add_postbox_toggles("link"),i("#category-tabs a").on("click keyup keydown",function(e){var t=i(this).attr("href");if("keydown"===e.type&&" "===e.key&&e.preventDefault(),"keyup"===e.type&&" "===e.key||"keydown"===e.type&&"Enter"===e.key||"click"===e.type)return e.preventDefault(),i("#category-tabs a").removeAttr("aria-selected").attr("tabindex","-1"),i(this).attr("aria-selected","true").removeAttr("tabindex"),i(this).parent().addClass("tabs").siblings("li").removeClass("tabs"),i(".tabs-panel").hide(),i(t).show(),"#categories-all"==t?deleteUserSetting("cats"):setUserSetting("cats","pop"),!1;"keyup"!==e.type||"ArrowRight"!==e.key&&"ArrowLeft"!==e.key||(i(this).attr("tabindex","-1"),t=i(this).parent("li").next(),e=i(this).parent("li").prev(),(0<t.length?(t.find("a").removeAttr("tabindex"),t):(e.find("a").removeAttr("tabindex"),e)).find("a").trigger("focus"))}),getUserSetting("cats")&&i('#category-tabs a[href="#categories-pop"]').trigger("click"),e=i("#newcat").one("focus",function(){i(this).val("").removeClass("form-input-tip")}),i("#link-category-add-submit").on("click",function(){e.focus()}),r=function(){var e,t;a||(a=!0,e=(t=i(this)).is(":checked"),t=t.val().toString(),i("#in-link-category-"+t+", #in-popular-link_category-"+t).prop("checked",e),a=!1)},t=function(e,t){i(t.what+" response_data",e).each(function(){i(i(this).text()).find("label").each(function(){var e=i(this),t=e.find("input").val(),a=e.find("input")[0].id,e=e.text().trim();i("#"+a).on("change",r),i('<option value="'+parseInt(t,10)+'"></option>').text(e)})})},i("#categorychecklist").wpList({alt:"",what:"link-category",response:"category-ajax-response",addAfter:t}),i('a[href="#categories-all"]').on("click",function(){deleteUserSetting("cats")}),i('a[href="#categories-pop"]').on("click",function(){setUserSetting("cats","pop")}),"pop"==getUserSetting("cats")&&i('a[href="#categories-pop"]').trigger("click"),i("#category-add-toggle").on("click",function(){return i(this).parents("div:first").toggleClass("wp-hidden-children"),i('#category-tabs a[href="#categories-all"]').trigger("click"),i("#newcategory").trigger("focus"),!1}),i(".categorychecklist :checkbox").on("change",r).filter(":checked").trigger("change")});
+28 -9
View File
@@ -566,16 +566,35 @@ jQuery( function($) {
}
// @todo Move to jQuery 1.3+, support for multiple hierarchical taxonomies, see wp-lists.js.
$('a', '#' + taxonomy + '-tabs').on( 'click', function( e ) {
e.preventDefault();
$('a', '#' + taxonomy + '-tabs').on( 'click keyup keydown', function( event ) {
var t = $(this).attr('href');
$(this).parent().addClass('tabs').siblings('li').removeClass('tabs');
$('#' + taxonomy + '-tabs').siblings('.tabs-panel').hide();
$(t).show();
if ( '#' + taxonomy + '-all' == t ) {
deleteUserSetting( settingName );
} else {
setUserSetting( settingName, 'pop' );
if ( event.type === 'keydown' && event.key === ' ' ) {
event.preventDefault();
}
if ( ( event.type === 'keyup' && event.key === ' ' ) || ( event.type === 'keydown' && event.key === 'Enter' ) || event.type === 'click' ) {
event.preventDefault();
$('#' + taxonomy + '-tabs a').removeAttr( 'aria-selected' ).attr( 'tabindex', '-1' );
$(this).attr( 'aria-selected', 'true' ).removeAttr( 'tabindex' );
$(this).parent().addClass('tabs').siblings('li').removeClass('tabs');
$('#' + taxonomy + '-tabs').siblings('.tabs-panel').hide();
$(t).show();
if ( '#' + taxonomy + '-all' == t ) {
deleteUserSetting( settingName );
} else {
setUserSetting( settingName, 'pop' );
}
}
if ( event.type === 'keyup' && ( event.key === 'ArrowRight' || event.key === 'ArrowLeft' ) ) {
$(this).attr( 'tabindex', '-1' );
let next = $(this).parent('li').next();
let prev = $(this).parent('li').prev();
if ( next.length > 0 ) {
next.find('a').removeAttr( 'tabindex');
next.find('a').trigger( 'focus' );
} else {
prev.find('a').removeAttr( 'tabindex');
prev.find('a').trigger( 'focus' );
}
}
});
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -16,7 +16,7 @@
*
* @global string $wp_version
*/
$wp_version = '7.0-beta2-61763';
$wp_version = '7.0-beta2-61764';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.