32892 Add more aria labels to list tables th row headers#12683
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
There was a problem hiding this comment.
Pull request overview
This PR extends WP_List_Table accessibility support by adding per-row aria-label values for primary-column row headers across several admin list tables, and updates @since annotations to match current core versioning.
Changes:
- Adds
get_primary_column_aria_label()overrides to multiple list tables so row headers expose a cleaner accessible name (e.g., term name, email, username, attachment title). - Updates
@sincetags for theget_primary_column_aria_label()API to7.1.0in core list table code and in the Posts list table implementation. - Includes a small whitespace alignment fix in
class-wp-posts-list-table.php.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/wp-admin/includes/class-wp-terms-list-table.php | Adds a primary-column aria-label provider using the term name. |
| src/wp-admin/includes/class-wp-privacy-data-removal-requests-list-table.php | Adds a primary-column aria-label provider using requester email. |
| src/wp-admin/includes/class-wp-privacy-data-export-requests-list-table.php | Adds a primary-column aria-label provider using requester email. |
| src/wp-admin/includes/class-wp-posts-list-table.php | Updates @since for the posts implementation and fixes minor whitespace. |
| src/wp-admin/includes/class-wp-ms-users-list-table.php | Adds a primary-column aria-label provider using user_login. |
| src/wp-admin/includes/class-wp-ms-sites-list-table.php | Adds a primary-column aria-label provider using the site title via blog switching. |
| src/wp-admin/includes/class-wp-media-list-table.php | Adds a primary-column aria-label provider using the attachment title. |
| src/wp-admin/includes/class-wp-list-table.php | Updates @since for the base get_primary_column_aria_label() API. |
| src/wp-admin/includes/class-wp-links-list-table.php | Adds a primary-column aria-label provider using the link name. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| protected function get_primary_column_aria_label( $blog ) { | ||
| switch_to_blog( $blog['blog_id'] ); | ||
| $site_title = get_option( 'blogname' ); | ||
| restore_current_blog(); | ||
|
|
||
| return $site_title; | ||
| } |
| * to omit the attribute. | ||
| * | ||
| * @since 6.9.0 | ||
| * @since 7.1.0 |
There was a problem hiding this comment.
Why this change needed?
There was a problem hiding this comment.
Because of https://core.trac.wordpress.org/changeset/62838?
There was a problem hiding this comment.
This is new code in WP 7.1. I guess the notation was copied and pasted from other methods introduced in 6.9.
There was a problem hiding this comment.
Yes, that was just overlooked.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/wp-admin/includes/class-wp-ms-sites-list-table.php:901
get_primary_column_aria_label()currently fetches the site title viaget_blog_option( ..., 'blogname' ), which (a) can add an extra per-row blog option lookup, and (b) does not match what the primaryblognamecolumn actually displays (the site URL/domain+path). For the row headeraria-label, returning the same URL string used incolumn_blogname()avoids the extra lookup and keeps the accessible name aligned with the visible primary identifier.
protected function get_primary_column_aria_label( $blog ) {
return get_blog_option( $blog['blog_id'], 'blogname' );
}
joedolson
left a comment
There was a problem hiding this comment.
A couple of these names can return HTML, and should be cleaned up before use.
| * @return string The attachment title. | ||
| */ | ||
| protected function get_primary_column_aria_label( $post ) { | ||
| return _draft_or_post_title( $post ); |
There was a problem hiding this comment.
_draft_or_post_title() can return escaped HTML, either inserted from the the_title filter or directly written into the post_title field. So this probably needs to reverse the escaping then strip HTML before use.
| * @return string The Site title. | ||
| */ | ||
| protected function get_primary_column_aria_label( $blog ) { | ||
| return get_blog_option( $blog['blog_id'], 'blogname' ); |
There was a problem hiding this comment.
The blogname can also contain HTML, but would not be expected to be escaped. Can probably just use wp_strip_all_tags() here.
There was a problem hiding this comment.
Interestingly, as far as I can tell the value get stored in the database already escaped. So that it may be something like My <div>books. We need to first decode the HTML entities and then strip the HTML. I observed there are a few cases of this pattern in the Customizer and in the REST API.
| esc_html( sprintf( __( 'Child of %s' ), wp_strip_all_tags( $parent_title ) ) ) | ||
| ); | ||
| $hierarchy_nolink = sprintf( | ||
| $hierarchy_nolink = sprintf( |
There was a problem hiding this comment.
Revert this as it need two spaces
There was a problem hiding this comment.
That only applies to consecutive lines, which is not the case here.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/wp-admin/includes/class-wp-ms-sites-list-table.php:903
get_blog_option()switches blogs internally (viaswitch_to_blog()), so calling it here introduces an extra blog context switch per row. It also makes the row header’s accessible name differ from the visible primary-column content (URL). Consider using the already-available site URL (domain + path) as the cleanaria-labelinstead, and update the docblock accordingly.
protected function get_primary_column_aria_label( $blog ) {
$blog_name = html_entity_decode( get_blog_option( $blog['blog_id'], 'blogname' ), ENT_QUOTES, get_bloginfo( 'charset' ) );
$blog_name = wp_strip_all_tags( $blog_name );
return $blog_name;
| protected function get_primary_column_aria_label( $post ) { | ||
| $attachment_title = $post->post_title ?? ''; | ||
|
|
||
| if ( ! empty( $attachment_title ) ) { | ||
| // The title may contain HTML. The printed aria-label uses esc_attr() later. | ||
| $attachment_title = wp_strip_all_tags( $attachment_title ); | ||
| } | ||
|
|
||
| return $attachment_title; | ||
| } |
| * @return string The User login. | ||
| */ | ||
| protected function get_primary_column_aria_label( $user ) { | ||
| return $user->user_login; |
There was a problem hiding this comment.
I believe usernames cannot be created with illegal characters so they can't contain HTML.
| * @return string The term name. | ||
| */ | ||
| protected function get_primary_column_aria_label( $tag ) { | ||
| return $tag->name; |
There was a problem hiding this comment.
I believe term names cannot be created with illegal characters so they can't contain HTM. It is stripped out during the term name creation.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
src/wp-admin/includes/class-wp-ms-sites-list-table.php:903
get_primary_column_aria_label()usesget_blog_option( $blog['blog_id'], 'blogname' ), which will trigger an options lookup per row in the Sites list table. On large multisite networks this can introduce an N+1 performance regression during table rendering. The primary column is labeled “URL” and the URL is already available in the$blogarray, so the aria-label can be derived without extra queries.
protected function get_primary_column_aria_label( $blog ) {
$blog_name = html_entity_decode( get_blog_option( $blog['blog_id'], 'blogname' ), ENT_QUOTES, get_bloginfo( 'charset' ) );
$blog_name = wp_strip_all_tags( $blog_name );
return $blog_name;
src/wp-admin/includes/class-wp-media-list-table.php:955
- This returns an empty string for untitled attachments, which causes the aria-label to be omitted and screen readers to fall back to the full cell content (including row actions). It also returns raw
$post->post_titlewithout the same “(no title)” fallback used elsewhere in WP admin list tables.
protected function get_primary_column_aria_label( $post ) {
$attachment_title = $post->post_title ?? '';
if ( ! empty( $attachment_title ) ) {
// The title may contain HTML. The printed aria-label uses esc_attr() later.
$attachment_title = wp_strip_all_tags( $attachment_title );
}
return $attachment_title;
e18e584 to
6048ca5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/wp-admin/includes/class-wp-ms-sites-list-table.php:903
get_blog_option( $blog['blog_id'], 'blogname' )triggersswitch_to_blog()for every rendered row, which can noticeably slow down the Sites list table (especially for large networks). Since a concise label is the goal, consider using the already-available URL (domain+path) for thearia-labelto avoid per-row blog context switches during rendering.
$blog_name = html_entity_decode( get_blog_option( $blog['blog_id'], 'blogname' ), ENT_QUOTES, get_bloginfo( 'charset' ) );
$blog_name = wp_strip_all_tags( $blog_name );
return $blog_name;
| protected function get_primary_column_aria_label( $blog ) { | ||
| $blog_name = html_entity_decode( get_blog_option( $blog['blog_id'], 'blogname' ), ENT_QUOTES, get_bloginfo( 'charset' ) ); | ||
| $blog_name = wp_strip_all_tags( $blog_name ); | ||
|
|
||
| return $blog_name; | ||
| } |
There was a problem hiding this comment.
Interesting. To my understanding, the blog name is required when creating a site (it's the blog title field) but then it can be manually emptied after the site creation, thus leaving a blog without a blogname. I think this surfaces a bug that should be addressed separately,
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/wp-admin/includes/class-wp-ms-sites-list-table.php:903
- This uses
get_blog_option( $blog['blog_id'], 'blogname' ), which callsswitch_to_blog()internally (seewp-includes/ms-blogs.php:get_blog_option()), adding a blog switch for every row. In non-list mode,column_blogname()already switches blogs, so this doubles the switching work per row. Consider using the already-available URL (domain + path) as the aria-label to avoid the extra switching overhead.
protected function get_primary_column_aria_label( $blog ) {
$blog_name = html_entity_decode( get_blog_option( $blog['blog_id'], 'blogname' ), ENT_QUOTES, get_bloginfo( 'charset' ) );
$blog_name = wp_strip_all_tags( $blog_name );
// Fall back to the blog URL and path if the blog name is empty.
| protected function get_primary_column_aria_label( $tag ) { | ||
| return $tag->name; | ||
| } |
There was a problem hiding this comment.
As far as I can tell, this is not needed because HTML is stripped out when creating a term name.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/wp-admin/includes/class-wp-posts-list-table.php:1136
- In
get_primary_column_aria_label(), the fallback string uses a new translatable__( 'no title' )that is inconsistent with core’s existing untitled-post string__( '(no title)' )(see_draft_or_post_title()inwp-admin/includes/template.php). Reusing the existing string avoids duplicate translations and keeps UI/ARIA wording consistent.
* @since 7.1.0
*
* @param WP_Post $item The current post object.
* @return string The post title, or 'no title' if no title.
*/
| * @since 7.1.0 | ||
| * | ||
| * @param WP_User $user The current WP_User object. | ||
| * @return string The User login. |
| /** | ||
| * Returns a clean label for the primary (URL) column's row header `aria-label`. | ||
| * | ||
| * Provides screen readers with just the Site Title as the row header name, | ||
| * preventing them from computing the name from the full cell content. | ||
| * | ||
| * @since 7.1.0 | ||
| * | ||
| * @param array $blog The current site properties array. | ||
| * @return string The Site title. | ||
| */ |
Trac ticket: https://core.trac.wordpress.org/ticket/32892
Adds aria-label attributes to more list table row headers, to provide cleaner names for screen reader users.
Corrects
@sincenotation after changeset 62838.Use of AI Tools
None
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.