Skip to content

Commit 35e63f8

Browse files
committed
Administration: Make subpage hierarchy in post list tables accessible.
In post list tables, subpages within hierarchical post types were visibly marked using an em dash to represent their nesting position. This information was not communicated in any way to screen reader users. Add `aria-describedby` with associated descriptive text to linked post titles, and screen reader text for non-editable post title contexts. Developed in WordPress#12394 Props juliemoynat, audrasjb, ekaterina92, ishikaatxecurify, aion11, sabernhardt, maxerns, joedolson. Fixes #64932. git-svn-id: https://develop.svn.wordpress.org/trunk@62814 602fd350-edb4-49c9-b593-d223f7449a82
1 parent f8d8fce commit 35e63f8

2 files changed

Lines changed: 116 additions & 6 deletions

File tree

src/wp-admin/includes/class-wp-posts-list-table.php

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,7 +1174,38 @@ public function column_title( $post ) {
11741174
echo '<div class="locked-info"><span class="locked-avatar">' . $locked_avatar . '</span> <span class="locked-text">' . $locked_text . "</span></div>\n";
11751175
}
11761176

1177-
$pad = str_repeat( '&#8212; ', $this->current_level );
1177+
$pad = str_repeat(
1178+
'<span aria-hidden="true">&#8212;</span> ',
1179+
$this->current_level
1180+
);
1181+
$described_by_attr = '';
1182+
$hierarchy_linked = '';
1183+
$hierarchy_nolink = '';
1184+
1185+
if ( $post->post_parent ) {
1186+
$parent = get_post( $post->post_parent );
1187+
1188+
if ( $parent ) {
1189+
/** This filter is documented in wp-includes/post-template.php */
1190+
$parent_title = apply_filters( 'the_title', $parent->post_title, $parent->ID );
1191+
1192+
$hierarchy_id = 'post-hierarchy-' . $post->ID;
1193+
$described_by_attr = sprintf( ' aria-describedby="%s"', esc_attr( $hierarchy_id ) );
1194+
$hierarchy_linked = sprintf(
1195+
'<span id="%1$s" class="hidden">%2$s</span>',
1196+
esc_attr( $hierarchy_id ),
1197+
/* translators: %s: Parent post title. */
1198+
esc_html( sprintf( __( 'Child of %s' ), wp_strip_all_tags( $parent_title ) ) )
1199+
);
1200+
$hierarchy_nolink = sprintf(
1201+
'<span id="%1$s" class="screen-reader-text"> (%2$s)</span>',
1202+
esc_attr( $hierarchy_id ),
1203+
/* translators: %s: Parent post title. */
1204+
esc_html( sprintf( __( 'Child of %s' ), wp_strip_all_tags( $parent_title ) ) )
1205+
);
1206+
}
1207+
}
1208+
11781209
echo '<strong>';
11791210

11801211
$title = _draft_or_post_title();
@@ -1187,16 +1218,19 @@ public function column_title( $post ) {
11871218

11881219
if ( $can_edit_post && 'trash' !== $post->post_status ) {
11891220
printf(
1190-
'<a class="row-title" href="%s">%s%s</a>',
1191-
get_edit_post_link( $post->ID ),
1221+
'%1$s<a class="row-title" href="%2$s"%3$s>%4$s</a>%5$s',
11921222
$pad,
1193-
$title
1223+
get_edit_post_link( $post->ID ),
1224+
$described_by_attr,
1225+
$title,
1226+
$hierarchy_linked
11941227
);
11951228
} else {
11961229
printf(
1197-
'<span>%s%s</span>',
1230+
'%1$s<span>%2$s%3$s</span>',
11981231
$pad,
1199-
$title
1232+
$title,
1233+
$hierarchy_nolink
12001234
);
12011235
}
12021236
_post_states( $post );

tests/phpunit/tests/admin/wpPostsListTable.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,61 @@ public function test_grandchildren_hierarchical_pages_second_page() {
196196
);
197197
}
198198

199+
/**
200+
* @ticket 64932
201+
*
202+
* @covers WP_Posts_List_Table::display_rows
203+
* @covers WP_Posts_List_Table::set_hierarchical_display
204+
* @covers WP_Posts_List_Table::column_title
205+
*/
206+
public function test_child_page_row_title_has_hierarchy_description() {
207+
wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
208+
209+
$output = $this->get_hierarchical_page_list_output(
210+
array(
211+
self::$top[1],
212+
self::$children[1][1],
213+
)
214+
);
215+
216+
$expected = sprintf(
217+
'<span aria-hidden="true">&#8212;</span> ' .
218+
'<a class="row-title" href="%1$s" aria-describedby="post-hierarchy-%2$d">Child 1</a>' .
219+
'<span id="post-hierarchy-%2$d" class="hidden">Child of Top Level Page 1</span>',
220+
get_edit_post_link( self::$children[1][1]->ID ),
221+
self::$children[1][1]->ID
222+
);
223+
224+
$this->assertStringContainsString( $expected, $output );
225+
}
226+
227+
/**
228+
* @ticket 64932
229+
*
230+
* @covers WP_Posts_List_Table::display_rows
231+
* @covers WP_Posts_List_Table::set_hierarchical_display
232+
* @covers WP_Posts_List_Table::column_title
233+
*/
234+
public function test_top_level_page_row_title_does_not_have_hierarchy_description() {
235+
wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
236+
237+
$output = $this->get_hierarchical_page_list_output(
238+
array(
239+
self::$top[1],
240+
)
241+
);
242+
243+
$this->assertStringContainsString(
244+
sprintf(
245+
'<a class="row-title" href="%s">Top Level Page 1</a>',
246+
get_edit_post_link( self::$top[1]->ID )
247+
),
248+
$output
249+
);
250+
$this->assertStringNotContainsString( 'aria-describedby="post-hierarchy-', $output );
251+
$this->assertStringNotContainsString( 'class="hidden">Child of ', $output );
252+
}
253+
199254
/**
200255
* Helper function to test the output of a page which uses `WP_Posts_List_Table`.
201256
*
@@ -245,6 +300,27 @@ protected function _test_list_hierarchical_page( array $args, array $expected_id
245300
}
246301
}
247302

303+
/**
304+
* Gets the output for a hierarchical page list table.
305+
*
306+
* @param WP_Post[] $posts Posts to display.
307+
* @return string List table rows output.
308+
*/
309+
protected function get_hierarchical_page_list_output( array $posts ) {
310+
$_REQUEST['paged'] = 1;
311+
$GLOBALS['per_page'] = 20;
312+
313+
ob_start();
314+
$this->table->set_hierarchical_display( true );
315+
$this->table->display_rows( $posts );
316+
$output = ob_get_clean();
317+
318+
unset( $_REQUEST['paged'] );
319+
unset( $GLOBALS['per_page'] );
320+
321+
return $output;
322+
}
323+
248324
/**
249325
* @ticket 37407
250326
*

0 commit comments

Comments
 (0)